ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/hash.c
Revision: 1644
Committed: Tue Nov 6 22:20:16 2012 UTC (11 years, 5 months ago) by michael
Content type: text/x-csrc
File size: 21530 byte(s)
Log Message:
- More config subsystem cleanups

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * hash.c: Maintains hashtables.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "list.h"
27 #include "balloc.h"
28 #include "conf.h"
29 #include "channel.h"
30 #include "channel_mode.h"
31 #include "client.h"
32 #include "modules.h"
33 #include "hash.h"
34 #include "resv.h"
35 #include "rng_mt.h"
36 #include "userhost.h"
37 #include "irc_string.h"
38 #include "ircd.h"
39 #include "numeric.h"
40 #include "send.h"
41 #include "memory.h"
42 #include "dbuf.h"
43 #include "s_user.h"
44
45
46 static BlockHeap *userhost_heap = NULL;
47 static BlockHeap *namehost_heap = NULL;
48
49 static unsigned int hashf_xor_key = 0;
50
51 /* The actual hash tables, They MUST be of the same HASHSIZE, variable
52 * size tables could be supported but the rehash routine should also
53 * rebuild the transformation maps, I kept the tables of equal size
54 * so that I can use one hash function.
55 */
56 static struct Client *idTable[HASHSIZE];
57 static struct Client *clientTable[HASHSIZE];
58 static struct Channel *channelTable[HASHSIZE];
59 static struct UserHost *userhostTable[HASHSIZE];
60 static struct MaskItem *resvchannelTable[HASHSIZE];
61
62
63 /* init_hash()
64 *
65 * inputs - NONE
66 * output - NONE
67 * side effects - Initialize the maps used by hash
68 * functions and clear the tables
69 */
70 void
71 init_hash(void)
72 {
73 /* Default the userhost/namehost sizes to CLIENT_HEAP_SIZE for now,
74 * should be a good close approximation anyway
75 * - Dianora
76 */
77 userhost_heap = BlockHeapCreate("userhost", sizeof(struct UserHost), CLIENT_HEAP_SIZE);
78 namehost_heap = BlockHeapCreate("namehost", sizeof(struct NameHost), CLIENT_HEAP_SIZE);
79
80 hashf_xor_key = genrand_int32() % 256; /* better than nothing --adx */
81 }
82
83 /*
84 * New hash function based on the Fowler/Noll/Vo (FNV) algorithm from
85 * http://www.isthe.com/chongo/tech/comp/fnv/
86 *
87 * Here, we use the FNV-1 method, which gives slightly better results
88 * than FNV-1a. -Michael
89 */
90 unsigned int
91 strhash(const char *name)
92 {
93 const unsigned char *p = (const unsigned char *)name;
94 unsigned int hval = FNV1_32_INIT;
95
96 if (*p == '\0')
97 return 0;
98 for (; *p != '\0'; ++p)
99 {
100 hval += (hval << 1) + (hval << 4) + (hval << 7) +
101 (hval << 8) + (hval << 24);
102 hval ^= (ToLower(*p) ^ hashf_xor_key);
103 }
104
105 return (hval >> FNV1_32_BITS) ^ (hval & ((1 << FNV1_32_BITS) - 1));
106 }
107
108 /************************** Externally visible functions ********************/
109
110 /* Optimization note: in these functions I supposed that the CSE optimization
111 * (Common Subexpression Elimination) does its work decently, this means that
112 * I avoided introducing new variables to do the work myself and I did let
113 * the optimizer play with more free registers, actual tests proved this
114 * solution to be faster than doing things like tmp2=tmp->hnext... and then
115 * use tmp2 myself which would have given less freedom to the optimizer.
116 */
117
118 /* hash_add_client()
119 *
120 * inputs - pointer to client
121 * output - NONE
122 * side effects - Adds a client's name in the proper hash linked
123 * list, can't fail, client_p must have a non-null
124 * name or expect a coredump, the name is infact
125 * taken from client_p->name
126 */
127 void
128 hash_add_client(struct Client *client_p)
129 {
130 unsigned int hashv = strhash(client_p->name);
131
132 client_p->hnext = clientTable[hashv];
133 clientTable[hashv] = client_p;
134 }
135
136 /* hash_add_channel()
137 *
138 * inputs - pointer to channel
139 * output - NONE
140 * side effects - Adds a channel's name in the proper hash linked
141 * list, can't fail. chptr must have a non-null name
142 * or expect a coredump. As before the name is taken
143 * from chptr->name, we do hash its entire lenght
144 * since this proved to be statistically faster
145 */
146 void
147 hash_add_channel(struct Channel *chptr)
148 {
149 unsigned int hashv = strhash(chptr->chname);
150
151 chptr->hnextch = channelTable[hashv];
152 channelTable[hashv] = chptr;
153 }
154
155 void
156 hash_add_resv(struct MaskItem *conf)
157 {
158 unsigned int hashv = strhash(conf->name);
159
160 conf->hnext = resvchannelTable[hashv];
161 resvchannelTable[hashv] = conf;
162 }
163
164 void
165 hash_add_userhost(struct UserHost *userhost)
166 {
167 unsigned int hashv = strhash(userhost->host);
168
169 userhost->next = userhostTable[hashv];
170 userhostTable[hashv] = userhost;
171 }
172
173 void
174 hash_add_id(struct Client *client_p)
175 {
176 unsigned int hashv = strhash(client_p->id);
177
178 client_p->idhnext = idTable[hashv];
179 idTable[hashv] = client_p;
180 }
181
182 /* hash_del_id()
183 *
184 * inputs - pointer to client
185 * output - NONE
186 * side effects - Removes an ID from the hash linked list
187 */
188 void
189 hash_del_id(struct Client *client_p)
190 {
191 unsigned int hashv = strhash(client_p->id);
192 struct Client *tmp = idTable[hashv];
193
194 if (tmp != NULL)
195 {
196 if (tmp == client_p)
197 {
198 idTable[hashv] = client_p->idhnext;
199 client_p->idhnext = client_p;
200 }
201 else
202 {
203 while (tmp->idhnext != client_p)
204 if ((tmp = tmp->idhnext) == NULL)
205 return;
206
207 tmp->idhnext = tmp->idhnext->idhnext;
208 client_p->idhnext = client_p;
209 }
210 }
211 }
212
213 /* hash_del_client()
214 *
215 * inputs - pointer to client
216 * output - NONE
217 * side effects - Removes a Client's name from the hash linked list
218 */
219 void
220 hash_del_client(struct Client *client_p)
221 {
222 unsigned int hashv = strhash(client_p->name);
223 struct Client *tmp = clientTable[hashv];
224
225 if (tmp != NULL)
226 {
227 if (tmp == client_p)
228 {
229 clientTable[hashv] = client_p->hnext;
230 client_p->hnext = client_p;
231 }
232 else
233 {
234 while (tmp->hnext != client_p)
235 if ((tmp = tmp->hnext) == NULL)
236 return;
237
238 tmp->hnext = tmp->hnext->hnext;
239 client_p->hnext = client_p;
240 }
241 }
242 }
243
244 /* hash_del_userhost()
245 *
246 * inputs - pointer to userhost
247 * output - NONE
248 * side effects - Removes a userhost from the hash linked list
249 */
250 void
251 hash_del_userhost(struct UserHost *userhost)
252 {
253 unsigned int hashv = strhash(userhost->host);
254 struct UserHost *tmp = userhostTable[hashv];
255
256 if (tmp != NULL)
257 {
258 if (tmp == userhost)
259 {
260 userhostTable[hashv] = userhost->next;
261 userhost->next = userhost;
262 }
263 else
264 {
265 while (tmp->next != userhost)
266 if ((tmp = tmp->next) == NULL)
267 return;
268
269 tmp->next = tmp->next->next;
270 userhost->next = userhost;
271 }
272 }
273 }
274
275 /* hash_del_channel()
276 *
277 * inputs - pointer to client
278 * output - NONE
279 * side effects - Removes the channel's name from the corresponding
280 * hash linked list
281 */
282 void
283 hash_del_channel(struct Channel *chptr)
284 {
285 unsigned int hashv = strhash(chptr->chname);
286 struct Channel *tmp = channelTable[hashv];
287
288 if (tmp != NULL)
289 {
290 if (tmp == chptr)
291 {
292 channelTable[hashv] = chptr->hnextch;
293 chptr->hnextch = chptr;
294 }
295 else
296 {
297 while (tmp->hnextch != chptr)
298 if ((tmp = tmp->hnextch) == NULL)
299 return;
300
301 tmp->hnextch = tmp->hnextch->hnextch;
302 chptr->hnextch = chptr;
303 }
304 }
305 }
306
307 void
308 hash_del_resv(struct MaskItem *chptr)
309 {
310 unsigned int hashv = strhash(chptr->name);
311 struct MaskItem *tmp = resvchannelTable[hashv];
312
313 if (tmp != NULL)
314 {
315 if (tmp == chptr)
316 {
317 resvchannelTable[hashv] = chptr->hnext;
318 chptr->hnext = chptr;
319 }
320 else
321 {
322 while (tmp->hnext != chptr)
323 if ((tmp = tmp->hnext) == NULL)
324 return;
325
326 tmp->hnext = tmp->hnext->hnext;
327 chptr->hnext = chptr;
328 }
329 }
330 }
331
332 /* hash_find_client()
333 *
334 * inputs - pointer to name
335 * output - NONE
336 * side effects - New semantics: finds a client whose name is 'name'
337 * if can't find one returns NULL. If it finds one moves
338 * it to the top of the list and returns it.
339 */
340 struct Client *
341 hash_find_client(const char *name)
342 {
343 unsigned int hashv = strhash(name);
344 struct Client *client_p;
345
346 if ((client_p = clientTable[hashv]) != NULL)
347 {
348 if (irccmp(name, client_p->name))
349 {
350 struct Client *prev;
351
352 while (prev = client_p, (client_p = client_p->hnext) != NULL)
353 {
354 if (!irccmp(name, client_p->name))
355 {
356 prev->hnext = client_p->hnext;
357 client_p->hnext = clientTable[hashv];
358 clientTable[hashv] = client_p;
359 break;
360 }
361 }
362 }
363 }
364
365 return client_p;
366 }
367
368 struct Client *
369 hash_find_id(const char *name)
370 {
371 unsigned int hashv = strhash(name);
372 struct Client *client_p;
373
374 if ((client_p = idTable[hashv]) != NULL)
375 {
376 if (strcmp(name, client_p->id))
377 {
378 struct Client *prev;
379
380 while (prev = client_p, (client_p = client_p->idhnext) != NULL)
381 {
382 if (!strcmp(name, client_p->id))
383 {
384 prev->idhnext = client_p->idhnext;
385 client_p->idhnext = idTable[hashv];
386 idTable[hashv] = client_p;
387 break;
388 }
389 }
390 }
391 }
392
393 return client_p;
394 }
395
396 struct Client *
397 hash_find_server(const char *name)
398 {
399 unsigned int hashv = strhash(name);
400 struct Client *client_p = NULL;
401
402 if (IsDigit(*name) && strlen(name) == IRC_MAXSID)
403 return hash_find_id(name);
404
405 if ((client_p = clientTable[hashv]) != NULL)
406 {
407 if ((!IsServer(client_p) && !IsMe(client_p)) ||
408 irccmp(name, client_p->name))
409 {
410 struct Client *prev;
411
412 while (prev = client_p, (client_p = client_p->hnext) != NULL)
413 {
414 if ((IsServer(client_p) || IsMe(client_p)) &&
415 !irccmp(name, client_p->name))
416 {
417 prev->hnext = client_p->hnext;
418 client_p->hnext = clientTable[hashv];
419 clientTable[hashv] = client_p;
420 break;
421 }
422 }
423 }
424 }
425
426 return client_p;
427 }
428
429 /* hash_find_channel()
430 *
431 * inputs - pointer to name
432 * output - NONE
433 * side effects - New semantics: finds a channel whose name is 'name',
434 * if can't find one returns NULL, if can find it moves
435 * it to the top of the list and returns it.
436 */
437 struct Channel *
438 hash_find_channel(const char *name)
439 {
440 unsigned int hashv = strhash(name);
441 struct Channel *chptr = NULL;
442
443 if ((chptr = channelTable[hashv]) != NULL)
444 {
445 if (irccmp(name, chptr->chname))
446 {
447 struct Channel *prev;
448
449 while (prev = chptr, (chptr = chptr->hnextch) != NULL)
450 {
451 if (!irccmp(name, chptr->chname))
452 {
453 prev->hnextch = chptr->hnextch;
454 chptr->hnextch = channelTable[hashv];
455 channelTable[hashv] = chptr;
456 break;
457 }
458 }
459 }
460 }
461
462 return chptr;
463 }
464
465 /* hash_get_bucket(int type, unsigned int hashv)
466 *
467 * inputs - hash value (must be between 0 and HASHSIZE - 1)
468 * output - NONE
469 * returns - pointer to first channel in channelTable[hashv]
470 * if that exists;
471 * NULL if there is no channel in that place;
472 * NULL if hashv is an invalid number.
473 * side effects - NONE
474 */
475 void *
476 hash_get_bucket(int type, unsigned int hashv)
477 {
478 assert(hashv < HASHSIZE);
479 if (hashv >= HASHSIZE)
480 return NULL;
481
482 switch (type)
483 {
484 case HASH_TYPE_ID:
485 return idTable[hashv];
486 break;
487 case HASH_TYPE_CHANNEL:
488 return channelTable[hashv];
489 break;
490 case HASH_TYPE_CLIENT:
491 return clientTable[hashv];
492 break;
493 case HASH_TYPE_USERHOST:
494 return userhostTable[hashv];
495 break;
496 case HASH_TYPE_RESERVED:
497 return resvchannelTable[hashv];
498 break;
499 default:
500 assert(0);
501 }
502
503 return NULL;
504 }
505
506 /* hash_find_resv()
507 *
508 * inputs - pointer to name
509 * output - NONE
510 * side effects - New semantics: finds a reserved channel whose name is 'name',
511 * if can't find one returns NULL, if can find it moves
512 * it to the top of the list and returns it.
513 */
514 struct MaskItem *
515 hash_find_resv(const char *name)
516 {
517 unsigned int hashv = strhash(name);
518 struct MaskItem *chptr;
519
520 if ((chptr = resvchannelTable[hashv]) != NULL)
521 {
522 if (irccmp(name, chptr->name))
523 {
524 struct MaskItem *prev;
525
526 while (prev = chptr, (chptr = chptr->hnext) != NULL)
527 {
528 if (!irccmp(name, chptr->name))
529 {
530 prev->hnext = chptr->hnext;
531 chptr->hnext = resvchannelTable[hashv];
532 resvchannelTable[hashv] = chptr;
533 break;
534 }
535 }
536 }
537 }
538
539 return chptr;
540 }
541
542 struct UserHost *
543 hash_find_userhost(const char *host)
544 {
545 unsigned int hashv = strhash(host);
546 struct UserHost *userhost;
547
548 if ((userhost = userhostTable[hashv]))
549 {
550 if (irccmp(host, userhost->host))
551 {
552 struct UserHost *prev;
553
554 while (prev = userhost, (userhost = userhost->next) != NULL)
555 {
556 if (!irccmp(host, userhost->host))
557 {
558 prev->next = userhost->next;
559 userhost->next = userhostTable[hashv];
560 userhostTable[hashv] = userhost;
561 break;
562 }
563 }
564 }
565 }
566
567 return userhost;
568 }
569
570 /* count_user_host()
571 *
572 * inputs - user name
573 * - hostname
574 * - int flag 1 if global, 0 if local
575 * - pointer to where global count should go
576 * - pointer to where local count should go
577 * - pointer to where identd count should go (local clients only)
578 * output - none
579 * side effects -
580 */
581 void
582 count_user_host(const char *user, const char *host, unsigned int *global_p,
583 unsigned int *local_p, unsigned int *icount_p)
584 {
585 dlink_node *ptr;
586 struct UserHost *found_userhost;
587 struct NameHost *nameh;
588
589 if ((found_userhost = hash_find_userhost(host)) == NULL)
590 return;
591
592 DLINK_FOREACH(ptr, found_userhost->list.head)
593 {
594 nameh = ptr->data;
595
596 if (!irccmp(user, nameh->name))
597 {
598 if (global_p != NULL)
599 *global_p = nameh->gcount;
600 if (local_p != NULL)
601 *local_p = nameh->lcount;
602 if (icount_p != NULL)
603 *icount_p = nameh->icount;
604 return;
605 }
606 }
607 }
608
609 /* find_or_add_userhost()
610 *
611 * inputs - host name
612 * output - none
613 * side effects - find UserHost * for given host name
614 */
615 static struct UserHost *
616 find_or_add_userhost(const char *host)
617 {
618 struct UserHost *userhost;
619
620 if ((userhost = hash_find_userhost(host)) != NULL)
621 return userhost;
622
623 userhost = BlockHeapAlloc(userhost_heap);
624 strlcpy(userhost->host, host, sizeof(userhost->host));
625 hash_add_userhost(userhost);
626
627 return userhost;
628 }
629
630 /* add_user_host()
631 *
632 * inputs - user name
633 * - hostname
634 * - int flag 1 if global, 0 if local
635 * output - none
636 * side effects - add given user@host to hash tables
637 */
638 void
639 add_user_host(const char *user, const char *host, int global)
640 {
641 dlink_node *ptr;
642 struct UserHost *found_userhost;
643 struct NameHost *nameh;
644 int hasident = 1;
645
646 if (*user == '~')
647 {
648 hasident = 0;
649 ++user;
650 }
651
652 if ((found_userhost = find_or_add_userhost(host)) == NULL)
653 return;
654
655 DLINK_FOREACH(ptr, found_userhost->list.head)
656 {
657 nameh = ptr->data;
658
659 if (!irccmp(user, nameh->name))
660 {
661 nameh->gcount++;
662
663 if (!global)
664 {
665 if (hasident)
666 nameh->icount++;
667 nameh->lcount++;
668 }
669
670 return;
671 }
672 }
673
674 nameh = BlockHeapAlloc(namehost_heap);
675 strlcpy(nameh->name, user, sizeof(nameh->name));
676
677 nameh->gcount = 1;
678
679 if (!global)
680 {
681 if (hasident)
682 nameh->icount = 1;
683 nameh->lcount = 1;
684 }
685
686 dlinkAdd(nameh, &nameh->node, &found_userhost->list);
687 }
688
689 /* delete_user_host()
690 *
691 * inputs - user name
692 * - hostname
693 * - int flag 1 if global, 0 if local
694 * output - none
695 * side effects - delete given user@host to hash tables
696 */
697 void
698 delete_user_host(const char *user, const char *host, int global)
699 {
700 dlink_node *ptr = NULL, *next_ptr = NULL;
701 struct UserHost *found_userhost;
702 struct NameHost *nameh;
703 int hasident = 1;
704
705 if (*user == '~')
706 {
707 hasident = 0;
708 ++user;
709 }
710
711 if ((found_userhost = hash_find_userhost(host)) == NULL)
712 return;
713
714 DLINK_FOREACH_SAFE(ptr, next_ptr, found_userhost->list.head)
715 {
716 nameh = ptr->data;
717
718 if (!irccmp(user, nameh->name))
719 {
720 if (nameh->gcount > 0)
721 nameh->gcount--;
722 if (!global)
723 {
724 if (nameh->lcount > 0)
725 nameh->lcount--;
726 if (hasident && nameh->icount > 0)
727 nameh->icount--;
728 }
729
730 if (nameh->gcount == 0 && nameh->lcount == 0)
731 {
732 dlinkDelete(&nameh->node, &found_userhost->list);
733 BlockHeapFree(namehost_heap, nameh);
734 }
735
736 if (dlink_list_length(&found_userhost->list) == 0)
737 {
738 hash_del_userhost(found_userhost);
739 BlockHeapFree(userhost_heap, found_userhost);
740 }
741
742 return;
743 }
744 }
745 }
746
747 /*
748 * Safe list code.
749 *
750 * The idea is really quite simple. As the link lists pointed to in
751 * each "bucket" of the channel hash table are traversed atomically
752 * there is no locking needed. Overall, yes, inconsistent reported
753 * state can still happen, but normally this isn't a big deal.
754 * I don't like sticking the code into hash.c but oh well. Moreover,
755 * if a hash isn't used in future, oops.
756 *
757 * - Dianora
758 */
759
760 /* exceeding_sendq()
761 *
762 * inputs - pointer to client to check
763 * output - 1 if client is in danger of blowing its sendq
764 * 0 if it is not.
765 * side effects -
766 *
767 * Sendq limit is fairly conservative at 1/2 (In original anyway)
768 */
769 static int
770 exceeding_sendq(struct Client *to)
771 {
772 if (dbuf_length(&to->localClient->buf_sendq) > (get_sendq(&to->localClient->confs) / 2))
773 return 1;
774 else
775 return 0;
776 }
777
778 void
779 free_list_task(struct ListTask *lt, struct Client *source_p)
780 {
781 dlink_node *dl, *dln;
782
783 if ((dl = dlinkFindDelete(&listing_client_list, source_p)) != NULL)
784 free_dlink_node(dl);
785
786 DLINK_FOREACH_SAFE(dl, dln, lt->show_mask.head)
787 {
788 MyFree(dl->data);
789 free_dlink_node(dl);
790 }
791
792 DLINK_FOREACH_SAFE(dl, dln, lt->hide_mask.head)
793 {
794 MyFree(dl->data);
795 free_dlink_node(dl);
796 }
797
798 MyFree(lt);
799
800 if (MyConnect(source_p))
801 source_p->localClient->list_task = NULL;
802 }
803
804 /* list_allow_channel()
805 *
806 * inputs - channel name
807 * - pointer to a list task
808 * output - 1 if the channel is to be displayed
809 * 0 otherwise
810 * side effects -
811 */
812 static int
813 list_allow_channel(const char *chname, struct ListTask *lt)
814 {
815 dlink_node *dl = NULL;
816
817 DLINK_FOREACH(dl, lt->show_mask.head)
818 if (!match_chan(dl->data, chname))
819 return 0;
820
821 DLINK_FOREACH(dl, lt->hide_mask.head)
822 if (match_chan(dl->data, chname))
823 return 0;
824
825 return 1;
826 }
827
828 /* list_one_channel()
829 *
830 * inputs - client pointer to return result to
831 * - pointer to channel to list
832 * - pointer to ListTask structure
833 * output - none
834 * side effects -
835 */
836 static void
837 list_one_channel(struct Client *source_p, struct Channel *chptr,
838 struct ListTask *list_task)
839 {
840 if (SecretChannel(chptr) && !IsMember(source_p, chptr))
841 return;
842 if (dlink_list_length(&chptr->members) < list_task->users_min ||
843 dlink_list_length(&chptr->members) > list_task->users_max ||
844 (chptr->channelts != 0 &&
845 ((unsigned int)chptr->channelts < list_task->created_min ||
846 (unsigned int)chptr->channelts > list_task->created_max)) ||
847 (unsigned int)chptr->topic_time < list_task->topicts_min ||
848 (chptr->topic_time ? (unsigned int)chptr->topic_time : UINT_MAX) >
849 list_task->topicts_max)
850 return;
851
852 if (!list_allow_channel(chptr->chname, list_task))
853 return;
854 sendto_one(source_p, form_str(RPL_LIST), me.name, source_p->name,
855 chptr->chname, dlink_list_length(&chptr->members),
856 chptr->topic);
857 }
858
859 /* safe_list_channels()
860 *
861 * inputs - pointer to client requesting list
862 * output - 0/1
863 * side effects - safely list all channels to source_p
864 *
865 * Walk the channel buckets, ensure all pointers in a bucket are
866 * traversed before blocking on a sendq. This means, no locking is needed.
867 *
868 * N.B. This code is "remote" safe, but is not currently used for
869 * remote clients.
870 *
871 * - Dianora
872 */
873 void
874 safe_list_channels(struct Client *source_p, struct ListTask *list_task,
875 int only_unmasked_channels)
876 {
877 struct Channel *chptr = NULL;
878
879 if (!only_unmasked_channels)
880 {
881 unsigned int i;
882
883 for (i = list_task->hash_index; i < HASHSIZE; ++i)
884 {
885 if (exceeding_sendq(source_p->from))
886 {
887 list_task->hash_index = i;
888 return; /* still more to do */
889 }
890
891 for (chptr = channelTable[i]; chptr; chptr = chptr->hnextch)
892 list_one_channel(source_p, chptr, list_task);
893 }
894 }
895 else
896 {
897 dlink_node *dl;
898
899 DLINK_FOREACH(dl, list_task->show_mask.head)
900 if ((chptr = hash_find_channel(dl->data)) != NULL)
901 list_one_channel(source_p, chptr, list_task);
902 }
903
904 free_list_task(list_task, source_p);
905 sendto_one(source_p, form_str(RPL_LISTEND),
906 me.name, source_p->name);
907 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision