/[cvs]/eggdrop1.9/src/userrec.c
ViewVC logotype

Contents of /eggdrop1.9/src/userrec.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.49 - (show annotations) (download) (as text)
Sun May 26 08:34:14 2002 UTC (17 years, 8 months ago) by stdarg
Branch: MAIN
Changes since 1.48: +4 -3 lines
File MIME type: text/x-chdr
* Moved creation and maintenance of core binds from tclhash.c to core_binds.c
* Renamed check_tcl_* to check_bind_* in the core

1 /*
2 * userrec.c --
3 *
4 * add_q() del_q() str2flags() flags2str() str2chflags() chflags2str()
5 * a bunch of functions to find and change user records
6 * change and check user (and channel-specific) flags
7 */
8 /*
9 * Copyright (C) 1997 Robey Pointer
10 * Copyright (C) 1999, 2000, 2001, 2002 Eggheads Development Team
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #ifndef lint
28 static const char rcsid[] = "$Id: userrec.c,v 1.48 2002/05/05 16:40:39 tothwolf Exp $";
29 #endif
30
31 #include <sys/stat.h>
32 #include "main.h"
33 #include "users.h"
34 #include "chan.h"
35 #include "modules.h"
36 #include "tandem.h"
37 #include "logfile.h"
38 #include "modules.h" /* encrypt_pass */
39 #include "cmdt.h" /* cmd_t */
40 #include "chanprog.h" /* clear_chanlist, set_chanlist */
41 #include "dccutil.h" /* shareout, chanout_but */
42 #include "irccmp.h" /* irccmp */
43 #include "core_binds.h" /* check_bind_nkch */
44 #include "flags.h"
45 #include "match.h" /* wild_match */
46 #include "userrec.h" /* prototypes */
47
48 extern struct dcc_t *dcc;
49 extern struct chanset_t *chanset;
50 extern int default_flags, default_uflags, quiet_save,
51 dcc_total, share_greet;
52 extern char userfile[], ver[], botnetnick[];
53 extern time_t now;
54
55 #ifndef MAKING_MODS
56 extern struct dcc_table DCC_CHAT, DCC_BOT;
57 #endif /* MAKING_MODS */
58
59 int noshare = 1; /* don't send out to sharebots */
60 int sort_users = 0; /* sort the userlist when saving */
61 struct userrec *userlist = NULL; /* user records are stored here */
62 struct userrec *lastuser = NULL; /* last accessed user record */
63 maskrec *global_bans = NULL,
64 *global_exempts = NULL,
65 *global_invites = NULL;
66 struct igrec *global_ign = NULL;
67 int cache_hit = 0,
68 cache_miss = 0; /* temporary cache accounting */
69 int strict_host = 0;
70 int userfile_perm = 0600; /* Userfile permissions,
71 default rw------- */
72
73 int count_users(struct userrec *bu)
74 {
75 int tot = 0;
76 struct userrec *u ;
77
78 for (u = bu; u; u = u->next)
79 tot++;
80 return tot;
81 }
82
83 /* Convert "nick!~user@host", "nick!+user@host" and "nick!-user@host"
84 * to "nick!user@host" if necessary. (drummer)
85 */
86 char *fixfrom(char *s)
87 {
88 char *p;
89 static char buf[512];
90
91 if (s == NULL)
92 return NULL;
93 strlcpy(buf, s, sizeof buf);
94 if (strict_host)
95 return buf;
96 if ((p = strchr(buf, '!')))
97 p++;
98 else
99 p = s; /* Sometimes we get passed just a
100 * user@host here... */
101 /* These are ludicrous. */
102 if (strchr("~+-^=", *p) && (p[1] != '@')) /* added check for @ - drummer */
103 strcpy(p, p + 1);
104 /* Bug was: n!~@host -> n!@host now: n!~@host */
105 return buf;
106 }
107
108 struct userrec *check_dcclist_hand(char *handle)
109 {
110 int i;
111
112 for (i = 0; i < dcc_total; i++)
113 if (dcc[i].type && !strcasecmp(dcc[i].nick, handle))
114 return dcc[i].user;
115 return NULL;
116 }
117
118 struct userrec *get_user_by_handle(struct userrec *bu, char *handle)
119 {
120 struct userrec *u, *ret;
121
122 if (!handle)
123 return NULL;
124 /* FIXME: This should be done outside of this function. */
125 /* handle might and should be const */
126 rmspace(handle);
127 if (!handle[0] || (handle[0] == '*'))
128 return NULL;
129 if (bu == userlist) {
130 if (lastuser && !strcasecmp(lastuser->handle, handle)) {
131 cache_hit++;
132 return lastuser;
133 }
134 ret = check_dcclist_hand(handle);
135 if (ret) {
136 cache_hit++;
137 return ret;
138 }
139 ret = check_chanlist_hand(handle);
140 if (ret) {
141 cache_hit++;
142 return ret;
143 }
144 cache_miss++;
145 }
146 for (u = bu; u; u = u->next)
147 if (!strcasecmp(u->handle, handle)) {
148 if (bu == userlist)
149 lastuser = u;
150 return u;
151 }
152 return NULL;
153 }
154
155 /* Fix capitalization, etc
156 */
157 void correct_handle(char *handle)
158 {
159 struct userrec *u;
160
161 u = get_user_by_handle(userlist, handle);
162 if (u == NULL)
163 return;
164 strcpy(handle, u->handle);
165 }
166
167 /* This will be usefull in a lot of places, much more code re-use so we
168 * endup with a smaller executable bot. <cybah>
169 */
170 void clear_masks(maskrec *m)
171 {
172 maskrec *temp = NULL;
173
174 for (; m; m = temp) {
175 temp = m->next;
176 if (m->mask)
177 free(m->mask);
178 if (m->user)
179 free(m->user);
180 if (m->desc)
181 free(m->desc);
182 free(m);
183 }
184 }
185
186 void clear_userlist(struct userrec *bu)
187 {
188 struct userrec *u, *v;
189 int i;
190
191 for (u = bu; u; u = v) {
192 v = u->next;
193 freeuser(u);
194 }
195 if (userlist == bu) {
196 struct chanset_t *cst;
197
198 for (i = 0; i < dcc_total; i++)
199 dcc[i].user = NULL;
200 clear_chanlist();
201 lastuser = NULL;
202
203 while (global_ign)
204 delignore(global_ign->igmask);
205
206 clear_masks(global_bans);
207 clear_masks(global_exempts);
208 clear_masks(global_invites);
209 global_exempts = global_invites = global_bans = NULL;
210
211 for (cst = chanset; cst; cst = cst->next) {
212 clear_masks(cst->bans);
213 clear_masks(cst->exempts);
214 clear_masks(cst->invites);
215
216 cst->bans = cst->exempts = cst->invites = NULL;
217 }
218 }
219 /* Remember to set your userlist to NULL after calling this */
220 }
221
222 /* Find CLOSEST host match
223 * (if "*!*@*" and "*!*@*clemson.edu" both match, use the latter!)
224 *
225 * Checks the chanlist first, to possibly avoid needless search.
226 */
227 struct userrec *get_user_by_host(char *host)
228 {
229 struct userrec *u, *ret;
230 struct list_type *q;
231 int cnt, i;
232 char host2[UHOSTLEN];
233
234 if (host == NULL)
235 return NULL;
236 rmspace(host);
237 if (!host[0])
238 return NULL;
239 ret = check_chanlist(host);
240 cnt = 0;
241 if (ret != NULL) {
242 cache_hit++;
243 return ret;
244 }
245 cache_miss++;
246 strlcpy(host2, host, sizeof host2);
247 host = fixfrom(host);
248 for (u = userlist; u; u = u->next) {
249 q = get_user(&USERENTRY_HOSTS, u);
250 for (; q; q = q->next) {
251 i = wild_match(q->extra, host);
252 if (i > cnt) {
253 ret = u;
254 cnt = i;
255 }
256 }
257 }
258 if (ret != NULL) {
259 lastuser = ret;
260 set_chanlist(host2, ret);
261 }
262 return ret;
263 }
264
265 /* use fixfrom() or dont? (drummer)
266 */
267 struct userrec *get_user_by_equal_host(char *host)
268 {
269 struct userrec *u;
270 struct list_type *q;
271
272 for (u = userlist; u; u = u->next)
273 for (q = get_user(&USERENTRY_HOSTS, u); q; q = q->next)
274 if (!irccmp(q->extra, host))
275 return u;
276 return NULL;
277 }
278
279 /* Try: pass_match_by_host("-",host)
280 * will return 1 if no password is set for that host
281 */
282 int u_pass_match(struct userrec *u, char *pass)
283 {
284 char *cmp, new[32];
285
286 if (!u)
287 return 0;
288 cmp = get_user(&USERENTRY_PASS, u);
289 if (!cmp && (!pass[0] || (pass[0] == '-')))
290 return 1;
291 if (!cmp || !pass || !pass[0] || (pass[0] == '-'))
292 return 0;
293 if (u->flags & USER_BOT) {
294 if (!strcmp(cmp, pass))
295 return 1;
296 } else {
297 if (strlen(pass) > 15)
298 pass[15] = 0;
299 encrypt_pass(pass, new);
300 if (!strcmp(cmp, new))
301 return 1;
302 }
303 return 0;
304 }
305
306 int write_user(struct userrec *u, FILE * f, int idx)
307 {
308 char s[181];
309 struct chanuserrec *ch;
310 struct chanset_t *cst;
311 struct user_entry *ue;
312 struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
313
314 fr.global = u->flags;
315 fr.udef_global = u->flags_udef;
316 build_flags(s, &fr, NULL);
317 if (fprintf(f, "%-10s - %-24s\n", u->handle, s) == EOF)
318 return 0;
319 for (ch = u->chanrec; ch; ch = ch->next) {
320 cst = findchan_by_dname(ch->channel);
321 if (cst && ((idx < 0) || channel_shared(cst))) {
322 if (idx >= 0) {
323 fr.match = (FR_CHAN | FR_BOT);
324 get_user_flagrec(dcc[idx].user, &fr, ch->channel);
325 } else
326 fr.chan = BOT_SHARE;
327 if ((fr.chan & BOT_SHARE) || (fr.bot & BOT_GLOBAL)) {
328 fr.match = FR_CHAN;
329 fr.chan = ch->flags;
330 fr.udef_chan = ch->flags_udef;
331 build_flags(s, &fr, NULL);
332 if (fprintf(f, "! %-20s %lu %-10s %s\n", ch->channel, ch->laston, s,
333 (((idx < 0) || share_greet) && ch->info) ? ch->info
334 : "") == EOF)
335 return 0;
336 }
337 }
338 }
339 for (ue = u->entries; ue; ue = ue->next) {
340 if (ue->name) {
341 struct list_type *lt;
342
343 for (lt = ue->u.list; lt; lt = lt->next)
344 if (fprintf(f, "--%s %s\n", ue->name, lt->extra) == EOF)
345 return 0;
346 } else {
347 if (!ue->type->write_userfile(f, u, ue))
348 return 0;
349 }
350 }
351 return 1;
352 }
353
354 int sort_compare(struct userrec *a, struct userrec *b)
355 {
356 /* Order by flags, then alphabetically
357 * first bots: +h / +a / +l / other bots
358 * then users: +n / +m / +o / other users
359 * return true if (a > b)
360 */
361 if (a->flags & b->flags & USER_BOT) {
362 if (~bot_flags(a) & bot_flags(b) & BOT_HUB)
363 return 1;
364 if (bot_flags(a) & ~bot_flags(b) & BOT_HUB)
365 return 0;
366 if (~bot_flags(a) & bot_flags(b) & BOT_ALT)
367 return 1;
368 if (bot_flags(a) & ~bot_flags(b) & BOT_ALT)
369 return 0;
370 if (~bot_flags(a) & bot_flags(b) & BOT_LEAF)
371 return 1;
372 if (bot_flags(a) & ~bot_flags(b) & BOT_LEAF)
373 return 0;
374 } else {
375 if (~a->flags & b->flags & USER_BOT)
376 return 1;
377 if (a->flags & ~b->flags & USER_BOT)
378 return 0;
379 if (~a->flags & b->flags & USER_OWNER)
380 return 1;
381 if (a->flags & ~b->flags & USER_OWNER)
382 return 0;
383 if (~a->flags & b->flags & USER_MASTER)
384 return 1;
385 if (a->flags & ~b->flags & USER_MASTER)
386 return 0;
387 if (~a->flags & b->flags & USER_OP)
388 return 1;
389 if (a->flags & ~b->flags & USER_OP)
390 return 0;
391 }
392 return (strcasecmp(a->handle, b->handle) > 0);
393 }
394
395 void sort_userlist()
396 {
397 int again;
398 struct userrec *last, *p, *c, *n;
399
400 again = 1;
401 last = NULL;
402 while ((userlist != last) && (again)) {
403 p = NULL;
404 c = userlist;
405 n = c->next;
406 again = 0;
407 while (n != last) {
408 if (sort_compare(c, n)) {
409 again = 1;
410 c->next = n->next;
411 n->next = c;
412 if (p == NULL)
413 userlist = n;
414 else
415 p->next = n;
416 }
417 p = c;
418 c = n;
419 n = n->next;
420 }
421 last = c;
422 }
423 }
424
425 /* Rewrite the entire user file. Call USERFILE hook as well, probably
426 * causing the channel file to be rewritten as well.
427 */
428 void write_userfile(int idx)
429 {
430 FILE *f;
431 char *new_userfile;
432 char s1[81];
433 time_t tt;
434 struct userrec *u;
435 int ok;
436
437 if (userlist == NULL)
438 return; /* No point in saving userfile */
439
440 new_userfile = malloc(strlen(userfile) + 5);
441 sprintf(new_userfile, "%s~new", userfile);
442
443 f = fopen(new_userfile, "w");
444 chmod(new_userfile, userfile_perm);
445 if (f == NULL) {
446 putlog(LOG_MISC, "*", _("ERROR writing user file."));
447 free(new_userfile);
448 return;
449 }
450 if (!quiet_save)
451 putlog(LOG_MISC, "*", _("Writing user file..."));
452 if (sort_users)
453 sort_userlist();
454 tt = now;
455 strcpy(s1, ctime(&tt));
456 fprintf(f, "#4v: %s -- %s -- written %s", ver, botnetnick, s1);
457 ok = 1;
458 for (u = userlist; u && ok; u = u->next)
459 ok = write_user(u, f, idx);
460 if (!ok || fflush(f)) {
461 putlog(LOG_MISC, "*", "%s (%s)", _("ERROR writing user file."), strerror(ferror(f)));
462 fclose(f);
463 free(new_userfile);
464 return;
465 }
466 fclose(f);
467 call_hook(HOOK_USERFILE);
468 movefile(new_userfile, userfile);
469 free(new_userfile);
470 }
471
472 int change_handle(struct userrec *u, char *newh)
473 {
474 int i;
475 char s[HANDLEN + 1];
476
477 if (!u)
478 return 0;
479 /* Nothing that will confuse the userfile */
480 if (!newh[1] && strchr(BADHANDCHARS, newh[0]))
481 return 0;
482 check_bind_nkch(u->handle, newh);
483 /* Yes, even send bot nick changes now: */
484 if (!noshare && !(u->flags & USER_UNSHARED))
485 shareout(NULL, "h %s %s\n", u->handle, newh);
486 strlcpy(s, u->handle, sizeof s);
487 strlcpy(u->handle, newh, sizeof u->handle);
488 for (i = 0; i < dcc_total; i++)
489 if (dcc[i].type && dcc[i].type != &DCC_BOT && !strcasecmp(dcc[i].nick, s)) {
490 strlcpy(dcc[i].nick, newh, sizeof dcc[i].nick);
491 if (dcc[i].type == &DCC_CHAT && dcc[i].u.chat->channel >= 0) {
492 chanout_but(-1, dcc[i].u.chat->channel,
493 "*** Handle change: %s -> %s\n", s, newh);
494 if (dcc[i].u.chat->channel < 100000)
495 botnet_send_nkch(i, s);
496 }
497 }
498 return 1;
499 }
500
501 extern int noxtra;
502
503 struct userrec *adduser(struct userrec *bu, char *handle, char *host,
504 char *pass, int flags)
505 {
506 struct userrec *u, *x;
507 struct xtra_key *xk;
508 int oldshare = noshare;
509
510 noshare = 1;
511 u = (struct userrec *) malloc(sizeof(struct userrec));
512
513 /* u->next=bu; bu=u; */
514 strlcpy(u->handle, handle, sizeof u->handle);
515 u->next = NULL;
516 u->chanrec = NULL;
517 u->entries = NULL;
518 if (flags != USER_DEFAULT) { /* drummer */
519 u->flags = flags;
520 u->flags_udef = 0;
521 } else {
522 u->flags = default_flags;
523 u->flags_udef = default_uflags;
524 }
525 set_user(&USERENTRY_PASS, u, pass);
526 if (!noxtra) {
527 char *now2;
528 xk = malloc(sizeof(struct xtra_key));
529 xk->key = strdup("created");
530 now2 = malloc(15);
531 sprintf(now2, "%lu", now);
532 xk->data = malloc(strlen(now2) +1);
533 sprintf(xk->data, "%lu", now);
534 set_user(&USERENTRY_XTRA, u, xk);
535 free(now2);
536 }
537 /* Strip out commas -- they're illegal */
538 if (host && host[0]) {
539 char *p;
540
541 /* About this fixfrom():
542 * We should use this fixfrom before every call of adduser()
543 * but its much easier to use here... (drummer)
544 * Only use it if we have a host :) (dw)
545 */
546 host = fixfrom(host);
547 p = strchr(host, ',');
548
549 while (p != NULL) {
550 *p = '?';
551 p = strchr(host, ',');
552 }
553 set_user(&USERENTRY_HOSTS, u, host);
554 } else
555 set_user(&USERENTRY_HOSTS, u, "none");
556 if (bu == userlist)
557 clear_chanlist();
558 noshare = oldshare;
559 if ((!noshare) && (handle[0] != '*') && (!(flags & USER_UNSHARED)) &&
560 (bu == userlist)) {
561 struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0};
562 char x[100];
563
564 fr.global = u->flags;
565 fr.udef_global = u->flags_udef;
566 build_flags(x, &fr, 0);
567 shareout(NULL, "n %s %s %s %s\n", handle, host && host[0] ? host : "none",
568 pass, x);
569 }
570 if (bu == NULL)
571 bu = u;
572 else {
573 if ((bu == userlist) && (lastuser != NULL))
574 x = lastuser;
575 else
576 x = bu;
577 while (x->next != NULL)
578 x = x->next;
579 x->next = u;
580 if (bu == userlist)
581 lastuser = u;
582 }
583 return bu;
584 }
585
586 void freeuser(struct userrec *u)
587 {
588 struct user_entry *ue, *ut;
589 struct chanuserrec *ch, *z;
590
591 if (u == NULL)
592 return;
593 ch = u->chanrec;
594 while (ch) {
595 z = ch;
596 ch = ch->next;
597 if (z->info != NULL)
598 free(z->info);
599 free(z);
600 }
601 u->chanrec = NULL;
602 for (ue = u->entries; ue; ue = ut) {
603 ut = ue->next;
604 if (ue->name) {
605 struct list_type *lt, *ltt;
606
607 for (lt = ue->u.list; lt; lt = ltt) {
608 ltt = lt->next;
609 free(lt->extra);
610 free(lt);
611 }
612 free(ue->name);
613 free(ue);
614 } else {
615 ue->type->kill(ue);
616 }
617 }
618 free(u);
619 }
620
621 int deluser(char *handle)
622 {
623 struct userrec *u = userlist, *prev = NULL;
624 int fnd = 0;
625
626 while ((u != NULL) && (!fnd)) {
627 if (!strcasecmp(u->handle, handle))
628 fnd = 1;
629 else {
630 prev = u;
631 u = u->next;
632 }
633 }
634 if (!fnd)
635 return 0;
636 if (prev == NULL)
637 userlist = u->next;
638 else
639 prev->next = u->next;
640 if (!noshare && (handle[0] != '*') && !(u->flags & USER_UNSHARED))
641 shareout(NULL, "k %s\n", handle);
642 for (fnd = 0; fnd < dcc_total; fnd++)
643 if (dcc[fnd].type && dcc[fnd].user == u)
644 dcc[fnd].user = 0; /* Clear any dcc users for this entry,
645 * null is safe-ish */
646 clear_chanlist();
647 freeuser(u);
648 lastuser = NULL;
649 return 1;
650 }
651
652 int delhost_by_handle(char *handle, char *host)
653 {
654 struct userrec *u;
655 struct list_type *q, *qnext, *qprev;
656 struct user_entry *e = NULL;
657 int i = 0;
658
659 u = get_user_by_handle(userlist, handle);
660 if (!u)
661 return 0;
662 q = get_user(&USERENTRY_HOSTS, u);
663 qprev = q;
664 if (q) {
665 if (!irccmp(q->extra, host)) {
666 e = find_user_entry(&USERENTRY_HOSTS, u);
667 e->u.extra = q->next;
668 free(q->extra);
669 free(q);
670 i++;
671 qprev = NULL;
672 q = e->u.extra;
673 } else
674 q = q->next;
675 while (q) {
676 qnext = q->next;
677 if (!irccmp(q->extra, host)) {
678 if (qprev)
679 qprev->next = q->next;
680 else if (e) {
681 e->u.extra = q->next;
682 qprev = NULL;
683 }
684 free(q->extra);
685 free(q);
686 i++;
687 } else
688 qprev = q;
689 q = qnext;
690 }
691 }
692 if (!qprev)
693 set_user(&USERENTRY_HOSTS, u, "none");
694 if (!noshare && i && !(u->flags & USER_UNSHARED))
695 shareout(NULL, "-h %s %s\n", handle, host);
696 clear_chanlist();
697 return i;
698 }
699
700 void addhost_by_handle(char *handle, char *host)
701 {
702 struct userrec *u = get_user_by_handle(userlist, handle);
703
704 set_user(&USERENTRY_HOSTS, u, host);
705 /* u will be cached, so really no overhead, even tho this looks dumb: */
706 if ((!noshare) && !(u->flags & USER_UNSHARED)) {
707 if (u->flags & USER_BOT)
708 shareout(NULL, "+bh %s %s\n", handle, host);
709 else
710 shareout(NULL, "+h %s %s\n", handle, host);
711 }
712 clear_chanlist();
713 }
714
715 void touch_laston(struct userrec *u, char *where, time_t timeval)
716 {
717 if (!u)
718 return;
719 if (timeval > 1) {
720 struct laston_info *li =
721 (struct laston_info *) get_user(&USERENTRY_LASTON, u);
722
723 if (!li)
724 li = malloc(sizeof(struct laston_info));
725
726 else if (li->lastonplace)
727 free(li->lastonplace);
728 li->laston = timeval;
729 if (where)
730 li->lastonplace = strdup(where);
731 else
732 li->lastonplace = NULL;
733 set_user(&USERENTRY_LASTON, u, li);
734 } else if (timeval == 1) {
735 set_user(&USERENTRY_LASTON, u, 0);
736 }
737 }
738
739 /* Go through all channel records and try to find a matching
740 * nick. Will return the user's user record if that is known
741 * to the bot. (Fabian)
742 *
743 * Warning: This is unreliable by concept!
744 */
745 struct userrec *get_user_by_nick(char *nick)
746 {
747 struct chanset_t *chan;
748 memberlist *m;
749
750 for (chan = chanset; chan; chan = chan->next) {
751 for (m = chan->channel.member; m && m->nick[0] ;m = m->next) {
752 if (!irccmp(nick, m->nick)) {
753 char word[512];
754
755 snprintf(word, sizeof word, "%s!%s", m->nick, m->userhost);
756 /* No need to check the return value ourself */
757 return get_user_by_host(word);;
758 }
759 }
760 }
761 /* Sorry, no matches */
762 return NULL;
763 }
764
765 void user_del_chan(char *dname)
766 {
767 struct chanuserrec *ch, *och;
768 struct userrec *u;
769
770 for (u = userlist; u; u = u->next) {
771 ch = u->chanrec;
772 och = NULL;
773 while (ch) {
774 if (!irccmp(dname, ch->channel)) {
775 if (och)
776 och->next = ch->next;
777 else
778 u->chanrec = ch->next;
779
780 if (ch->info)
781 free(ch->info);
782 free(ch);
783 break;
784 }
785 och = ch;
786 ch = ch->next;
787 }
788 }
789 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23