/[LeafOK_CVS]/pvpgn-1.7.4/src/bnetd/channel.c
ViewVC logotype

Contents of /pvpgn-1.7.4/src/bnetd/channel.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1.1.1 - (show annotations) (vendor branch)
Tue Jun 6 03:41:37 2006 UTC (19 years, 9 months ago) by sysadm
Branch: GNU, MAIN
CVS Tags: pvpgn_1-7-4-0_MIL, arelease, HEAD
Changes since 1.1: +0 -0 lines
Content type: text/x-csrc
no message

1 /*
2 * Copyright (C) 1998 Mark Baysinger (mbaysing@ucsd.edu)
3 * Copyright (C) 1998,1999,2000 Ross Combs (rocombs@cs.nmsu.edu)
4 * Copyright (C) 2000,2001 Marco Ziech (mmz@gmx.net)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20 #define CHANNEL_INTERNAL_ACCESS
21 #include "common/setup_before.h"
22 #include <stdio.h>
23 #ifdef HAVE_STDDEF_H
24 # include <stddef.h>
25 #else
26 # ifndef NULL
27 # define NULL ((void *)0)
28 # endif
29 #endif
30 #ifdef STDC_HEADERS
31 # include <stdlib.h>
32 #else
33 # ifdef HAVE_MALLOC_H
34 # include <malloc.h>
35 # endif
36 #endif
37 #ifdef HAVE_STRING_H
38 # include <string.h>
39 #else
40 # ifdef HAVE_STRINGS_H
41 # include <strings.h>
42 # endif
43 #endif
44 #include "compat/strrchr.h"
45 #include "compat/strdup.h"
46 #include "compat/strcasecmp.h"
47 #include <errno.h>
48 #include "compat/strerror.h"
49 #ifdef TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # ifdef HAVE_SYS_TIME_H
54 # include <sys/time.h>
55 # else
56 # include <time.h>
57 # endif
58 #endif
59 #ifdef HAVE_SYS_TYPES_H
60 # include <sys/types.h>
61 #endif
62 #include "connection.h"
63 #include "common/eventlog.h"
64 #include "common/list.h"
65 #include "message.h"
66 #include "account.h"
67 #include "account_wrap.h"
68 #include "common/util.h"
69 #include "prefs.h"
70 #include "common/token.h"
71 #include "channel.h"
72 #include "irc.h"
73 #include "common/tag.h"
74 #include "common/xalloc.h"
75 #include "common/setup_after.h"
76
77
78 static t_list * channellist_head=NULL;
79
80 static t_channelmember * memberlist_curr=NULL;
81 static int totalcount=0;
82
83
84 static int channellist_load_permanent(char const * filename);
85 static t_channel * channellist_find_channel_by_fullname(char const * name);
86 static char * channel_format_name(char const * sname, char const * country, char const * realmname, unsigned int id);
87
88 extern int channel_set_userflags(t_connection * c);
89
90 extern t_channel * channel_create(char const * fullname, char const * shortname, char const * clienttag, int permflag, int botflag, int operflag, int logflag, char const * country, char const * realmname, int maxmembers, int moderated, int clanflag, int autoname)
91 {
92 t_channel * channel;
93
94 if (!fullname)
95 {
96 eventlog(eventlog_level_error,__FUNCTION__,"got NULL fullname");
97 return NULL;
98 }
99 if (fullname[0]=='\0')
100 {
101 eventlog(eventlog_level_error,__FUNCTION__,"got empty fullname");
102 return NULL;
103 }
104 if (shortname && shortname[0]=='\0')
105 {
106 eventlog(eventlog_level_error,__FUNCTION__,"got empty shortname");
107 return NULL;
108 }
109 if (clienttag && strlen(clienttag)!=4)
110 {
111 eventlog(eventlog_level_error,__FUNCTION__,"client tag has bad length (%u chars)",strlen(clienttag));
112 return NULL;
113 }
114
115 /* non-permanent already checks for this in conn_set_channel */
116 if (permflag)
117 {
118 if ((channel = channellist_find_channel_by_fullname(fullname)))
119 {
120 if ((channel_get_clienttag(channel)) && (clienttag) && (strcmp(channel_get_clienttag(channel),clienttag)==0))
121 {
122 eventlog(eventlog_level_error,__FUNCTION__,"could not create duplicate permanent channel (fullname \"%s\")",fullname);
123 return NULL;
124 }
125 else if (((channel->flags & channel_flags_allowbots)!=(botflag?channel_flags_allowbots:0)) ||
126 ((channel->flags & channel_flags_allowopers)!=(operflag?channel_flags_allowopers:0)) ||
127 (channel->maxmembers!=maxmembers) ||
128 ((channel->flags & channel_flags_moderated)!=(moderated?channel_flags_moderated:0)) ||
129 (channel->logname && logflag==0) || (!(channel->logname) && logflag ==1))
130 {
131 eventlog(eventlog_level_error,__FUNCTION__,"channel parameters do not match for \"%s\" and \"%s\"",fullname,channel->name);
132 return NULL;
133 }
134 }
135 }
136
137 channel = xmalloc(sizeof(t_channel));
138
139 if (permflag)
140 {
141 channel->flags = channel_flags_public;
142 if (clienttag && maxmembers!=-1) /* approximation.. we want things like "Starcraft USA-1" */
143 channel->flags |= channel_flags_system;
144 } else
145 channel->flags = channel_flags_none;
146
147 if (moderated)
148 channel->flags |= channel_flags_moderated;
149
150 if(shortname && (!strcasecmp(shortname, CHANNEL_NAME_KICKED)
151 || !strcasecmp(shortname, CHANNEL_NAME_BANNED)))
152 channel->flags |= channel_flags_thevoid;
153
154 eventlog(eventlog_level_debug,__FUNCTION__,"creating new channel \"%s\" shortname=%s%s%s clienttag=%s%s%s country=%s%s%s realm=%s%s%s",fullname,
155 shortname?"\"":"(", /* all this is doing is printing the name in quotes else "none" in parens */
156 shortname?shortname:"none",
157 shortname?"\"":")",
158 clienttag?"\"":"(",
159 clienttag?clienttag:"none",
160 clienttag?"\"":")",
161 country?"\"":"(",
162 country?country:"none",
163 country?"\"":")",
164 realmname?"\"":"(",
165 realmname?realmname:"none",
166 realmname?"\"":")");
167
168
169 channel->name = xstrdup(fullname);
170
171 if (!shortname)
172 channel->shortname = NULL;
173 else
174 channel->shortname = xstrdup(shortname);
175
176 if (clienttag)
177 channel->clienttag = xstrdup(clienttag);
178 else
179 channel->clienttag = NULL;
180
181 if (country)
182 channel->country = xstrdup(country);
183 else
184 channel->country = NULL;
185
186 if (realmname)
187 channel->realmname = xstrdup(realmname);
188 else
189 channel->realmname=NULL;
190
191 channel->banlist = list_create();
192
193 totalcount++;
194 if (totalcount==0) /* if we wrap (yeah right), don't use id 0 */
195 totalcount = 1;
196 channel->id = totalcount;
197 channel->maxmembers = maxmembers;
198 channel->currmembers = 0;
199 channel->memberlist = NULL;
200
201 if (permflag) channel->flags |= channel_flags_permanent;
202 if (botflag) channel->flags |= channel_flags_allowbots;
203 if (operflag) channel->flags |= channel_flags_allowopers;
204 if (clanflag) channel->flags |= channel_flags_clan;
205 if (autoname) channel->flags |= channel_flags_autoname;
206
207 if (logflag)
208 {
209 time_t now;
210 struct tm * tmnow;
211 char dstr[64];
212 char timetemp[CHANLOG_TIME_MAXLEN];
213
214 now = time(NULL);
215
216 if (!(tmnow = localtime(&now)))
217 dstr[0] = '\0';
218 else
219 sprintf(dstr,"%04d%02d%02d%02d%02d%02d",
220 1900+tmnow->tm_year,
221 tmnow->tm_mon+1,
222 tmnow->tm_mday,
223 tmnow->tm_hour,
224 tmnow->tm_min,
225 tmnow->tm_sec);
226
227 channel->logname = xmalloc(strlen(prefs_get_chanlogdir())+9+strlen(dstr)+1+6+1); /* dir + "/chanlog-" + dstr + "-" + id + NUL */
228 sprintf(channel->logname,"%s/chanlog-%s-%06u",prefs_get_chanlogdir(),dstr,channel->id);
229
230 if (!(channel->log = fopen(channel->logname,"w")))
231 eventlog(eventlog_level_error,__FUNCTION__,"could not open channel log \"%s\" for writing (fopen: %s)",channel->logname,pstrerror(errno));
232 else
233 {
234 fprintf(channel->log,"name=\"%s\"\n",channel->name);
235 if (channel->shortname)
236 fprintf(channel->log,"shortname=\"%s\"\n",channel->shortname);
237 else
238 fprintf(channel->log,"shortname=none\n");
239 fprintf(channel->log,"permanent=\"%s\"\n",(channel->flags & channel_flags_permanent)?"true":"false");
240 fprintf(channel->log,"allowbotse=\"%s\"\n",(channel->flags & channel_flags_allowbots)?"true":"false");
241 fprintf(channel->log,"allowopers=\"%s\"\n",(channel->flags & channel_flags_allowopers)?"true":"false");
242 if (channel->clienttag)
243 fprintf(channel->log,"clienttag=\"%s\"\n",channel->clienttag);
244 else
245 fprintf(channel->log,"clienttag=none\n");
246
247 if (tmnow)
248 strftime(timetemp,sizeof(timetemp),CHANLOG_TIME_FORMAT,tmnow);
249 else
250 strcpy(timetemp,"?");
251 fprintf(channel->log,"created=\"%s\"\n\n",timetemp);
252 fflush(channel->log);
253 }
254 }
255 else
256 {
257 channel->logname = NULL;
258 channel->log = NULL;
259 }
260
261 list_append_data(channellist_head,channel);
262
263 eventlog(eventlog_level_debug,__FUNCTION__,"channel created successfully");
264 return channel;
265 }
266
267
268 extern int channel_destroy(t_channel * channel, t_elem ** curr)
269 {
270 t_elem * ban;
271
272 if (!channel)
273 {
274 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
275 return -1;
276 }
277
278 if (channel->memberlist)
279 {
280 eventlog(eventlog_level_debug,__FUNCTION__,"channel is not empty, deferring");
281 channel->flags &= ~channel_flags_permanent; /* make it go away when the last person leaves */
282 return -1;
283 }
284
285 if (list_remove_data(channellist_head,channel,curr)<0)
286 {
287 eventlog(eventlog_level_error,__FUNCTION__,"could not remove item from list");
288 return -1;
289 }
290
291 eventlog(eventlog_level_info,__FUNCTION__,"destroying channel \"%s\"",channel->name);
292
293 LIST_TRAVERSE(channel->banlist,ban)
294 {
295 char const * banned;
296
297 if (!(banned = elem_get_data(ban)))
298 eventlog(eventlog_level_error,__FUNCTION__,"found NULL name in banlist");
299 else
300 xfree((void *)banned); /* avoid warning */
301 if (list_remove_elem(channel->banlist,&ban)<0)
302 eventlog(eventlog_level_error,__FUNCTION__,"unable to remove item from list");
303 }
304 list_destroy(channel->banlist);
305
306 if (channel->log)
307 {
308 time_t now;
309 struct tm * tmnow;
310 char timetemp[CHANLOG_TIME_MAXLEN];
311
312 now = time(NULL);
313 if ((!(tmnow = localtime(&now))))
314 strcpy(timetemp,"?");
315 else
316 strftime(timetemp,sizeof(timetemp),CHANLOG_TIME_FORMAT,tmnow);
317 fprintf(channel->log,"\ndestroyed=\"%s\"\n",timetemp);
318
319 if (fclose(channel->log)<0)
320 eventlog(eventlog_level_error,__FUNCTION__,"could not close channel log \"%s\" after writing (fclose: %s)",channel->logname,pstrerror(errno));
321 }
322
323 if (channel->logname)
324 xfree((void *)channel->logname); /* avoid warning */
325
326 if (channel->country)
327 xfree((void *)channel->country); /* avoid warning */
328
329 if (channel->realmname)
330 xfree((void *)channel->realmname); /* avoid warning */
331
332 if (channel->clienttag)
333 xfree((void *)channel->clienttag); /* avoid warning */
334
335 if (channel->shortname)
336 xfree((void *)channel->shortname); /* avoid warning */
337
338 xfree((void *)channel->name); /* avoid warning */
339
340 xfree(channel);
341
342 return 0;
343 }
344
345
346 extern char const * channel_get_name(t_channel const * channel)
347 {
348 if (!channel)
349 {
350 eventlog(eventlog_level_warn,__FUNCTION__,"got NULL channel");
351 return "";
352 }
353
354 return channel->name;
355 }
356
357
358 extern char const * channel_get_clienttag(t_channel const * channel)
359 {
360 if (!channel)
361 {
362 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
363 return "";
364 }
365
366 return channel->clienttag;
367 }
368
369
370 extern t_channel_flags channel_get_flags(t_channel const * channel)
371 {
372 if (!channel)
373 {
374 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
375 return channel_flags_none;
376 }
377
378 return channel->flags;
379 }
380
381 extern int channel_set_flags(t_channel * channel, t_channel_flags flags)
382 {
383 if (!channel)
384 {
385 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
386 return -1;
387 }
388 channel->flags = flags;
389
390 return 0;
391 }
392
393 extern int channel_get_permanent(t_channel const * channel)
394 {
395 if (!channel)
396 {
397 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
398 return 0;
399 }
400
401 return (channel->flags & channel_flags_permanent);
402 }
403
404
405 extern unsigned int channel_get_channelid(t_channel const * channel)
406 {
407 if (!channel)
408 {
409 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
410 return 0;
411 }
412 return channel->id;
413 }
414
415
416 extern int channel_set_channelid(t_channel * channel, unsigned int channelid)
417 {
418 if (!channel)
419 {
420 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
421 return -1;
422 }
423 channel->id = channelid;
424 return 0;
425 }
426
427 extern int channel_rejoin(t_connection * conn)
428 {
429 t_channel const * channel;
430 char const * temp;
431 char const * chname;
432
433 if (!(channel = conn_get_channel(conn)))
434 return -1;
435
436 if (!(temp = channel_get_name(channel)))
437 return -1;
438
439 chname=xstrdup(temp);
440 conn_set_channel(conn, NULL);
441 if (conn_set_channel(conn,chname)<0)
442 conn_set_channel(conn,CHANNEL_NAME_BANNED);
443 xfree((void *)chname);
444 return 0;
445 }
446
447
448 extern int channel_add_connection(t_channel * channel, t_connection * connection)
449 {
450 t_channelmember * member;
451 t_connection * user;
452
453 if (!channel)
454 {
455 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
456 return -1;
457 }
458 if (!connection)
459 {
460 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
461 return -1;
462 }
463
464 if (channel_check_banning(channel,connection))
465 {
466 channel_message_log(channel,connection,0,"JOIN FAILED (banned)");
467 return -1;
468 }
469
470 member = xmalloc(sizeof(t_channelmember));
471 member->connection = connection;
472 member->next = channel->memberlist;
473 channel->memberlist = member;
474 channel->currmembers++;
475
476 channel_message_log(channel,connection,0,"JOINED");
477
478 message_send_text(connection,message_type_channel,connection,channel_get_name(channel));
479
480 if ((!(channel->flags & channel_flags_permanent))
481 && (!(channel->flags & channel_flags_thevoid))
482 && (!(channel->flags & channel_flags_clan))
483 && (channel->currmembers==1)
484 && (account_is_operator_or_admin(conn_get_account(connection),channel_get_name(channel))==0))
485 {
486 message_send_text(connection,message_type_info,connection,"you are now tempOP for this channel");
487 conn_set_tmpOP_channel(connection,(char *)channel_get_name(channel));
488 channel_update_userflags(connection);
489 }
490
491 if(!(channel_get_flags(channel) & channel_flags_thevoid))
492 for (user=channel_get_first(channel); user; user=channel_get_next())
493 {
494 message_send_text(connection,message_type_adduser,user,NULL);
495 if (user!=connection)
496 message_send_text(user,message_type_join,connection,NULL);
497 }
498
499 /* please don't remove this notice */
500 if (channel->log)
501 message_send_text(connection,message_type_info,connection,prefs_get_log_notice());
502
503 return 0;
504 }
505
506
507 extern int channel_del_connection(t_channel * channel, t_connection * connection)
508 {
509 t_channelmember * curr;
510 t_channelmember * temp;
511 t_elem * curr2;
512
513 if (!channel)
514 {
515 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
516 return -1;
517 }
518 if (!connection)
519 {
520 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
521 return -1;
522 }
523
524 channel_message_log(channel,connection,0,"PARTED");
525
526 channel_message_send(channel,message_type_part,connection,NULL);
527
528 curr = channel->memberlist;
529 if (curr->connection==connection)
530 {
531 channel->memberlist = channel->memberlist->next;
532 xfree(curr);
533 }
534 else
535 {
536 while (curr->next && curr->next->connection!=connection)
537 curr = curr->next;
538
539 if (curr->next)
540 {
541 temp = curr->next;
542 curr->next = curr->next->next;
543 xfree(temp);
544 }
545 else
546 {
547 eventlog(eventlog_level_error,__FUNCTION__,"[%d] connection not in channel member list",conn_get_socket(connection));
548 return -1;
549 }
550 }
551 channel->currmembers--;
552
553 if (conn_get_tmpOP_channel(connection) &&
554 strcmp(conn_get_tmpOP_channel(connection),channel_get_name(channel))==0)
555 {
556 conn_set_tmpOP_channel(connection,NULL);
557 }
558
559 if (!channel->memberlist && !(channel->flags & channel_flags_permanent)) /* if channel is empty, delete it unless it's a permanent channel */
560 {
561 channel_destroy(channel,&curr2);
562 }
563
564 return 0;
565 }
566
567
568 extern void channel_update_latency(t_connection * me)
569 {
570 t_channel * channel;
571 t_message * message;
572 t_connection * c;
573
574 if (!me)
575 {
576 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
577 return;
578 }
579 if (!(channel = conn_get_channel(me)))
580 {
581 eventlog(eventlog_level_error,__FUNCTION__,"connection has no channel");
582 return;
583 }
584
585 if (!(message = message_create(message_type_userflags,me,NULL,NULL))) /* handles NULL text */
586 return;
587
588 for (c=channel_get_first(channel); c; c=channel_get_next())
589 if (conn_get_class(c)==conn_class_bnet)
590 message_send(message,c);
591 message_destroy(message);
592 }
593
594
595 extern void channel_update_userflags(t_connection * me)
596 {
597 t_channel * channel;
598 t_message * message;
599 t_connection * c;
600
601 if (!me)
602 {
603 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
604 return;
605 }
606 if (!(channel = conn_get_channel(me)))
607 {
608 eventlog(eventlog_level_error,__FUNCTION__,"connection has no channel");
609 return;
610 }
611
612 if (!(message = message_create(message_type_userflags,me,NULL,NULL))) /* handles NULL text */
613 return;
614
615 for (c=channel_get_first(channel); c; c=channel_get_next())
616 message_send(message,c);
617
618 message_destroy(message);
619 }
620
621
622 extern void channel_message_log(t_channel const * channel, t_connection * me, int fromuser, char const * text)
623 {
624 if (!channel)
625 {
626 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
627 return;
628 }
629 if (!me)
630 {
631 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
632 return;
633 }
634
635 if (channel->log)
636 {
637 time_t now;
638 struct tm * tmnow;
639 char timetemp[CHANLOG_TIME_MAXLEN];
640
641 now = time(NULL);
642 if ((!(tmnow = localtime(&now))))
643 strcpy(timetemp,"?");
644 else
645 strftime(timetemp,sizeof(timetemp),CHANLOGLINE_TIME_FORMAT,tmnow);
646
647 if (fromuser)
648 fprintf(channel->log,"%s: \"%s\" \"%s\"\n",timetemp,conn_get_username(me),text);
649 else
650 fprintf(channel->log,"%s: \"%s\" %s\n",timetemp,conn_get_username(me),text);
651 fflush(channel->log);
652 }
653 }
654
655
656 extern void channel_message_send(t_channel const * channel, t_message_type type, t_connection * me, char const * text)
657 {
658 t_connection * c;
659 unsigned int heard;
660 t_message * message;
661 char const * tname;
662
663 if (!channel)
664 {
665 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
666 return;
667 }
668 if (!me)
669 {
670 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
671 return;
672 }
673
674 if(channel_get_flags(channel) & channel_flags_thevoid) // no talking in the void
675 return;
676
677 if(channel_get_flags(channel) & channel_flags_moderated) // moderated channel - only admins,OPs and voices may talk
678 {
679 if (type==message_type_talk || type==message_type_emote)
680 {
681 if (!((account_is_operator_or_admin(conn_get_account(me),channel_get_name(channel))) ||
682 (channel_conn_has_tmpVOICE(channel,me))))
683 {
684 message_send_text(me,message_type_error,me,"This channel is moderated");
685 return;
686 }
687 }
688 }
689
690 if (!(message = message_create(type,me,NULL,text)))
691 {
692 eventlog(eventlog_level_error,__FUNCTION__,"could not create message");
693 return;
694 }
695
696 heard = 0;
697 tname = conn_get_chatname(me);
698 for (c=channel_get_first(channel); c; c=channel_get_next())
699 {
700 if (c==me && (type==message_type_talk || type==message_type_join || type==message_type_part))
701 continue; /* ignore ourself */
702 if ((type==message_type_talk || type==message_type_whisper || type==message_type_emote || type==message_type_broadcast) &&
703 conn_check_ignoring(c,tname)==1)
704 continue; /* ignore squelched players */
705
706 if (message_send(message,c)==0 && c!=me)
707 heard = 1;
708
709 }
710 conn_unget_chatname(me,tname);
711
712 message_destroy(message);
713
714 if (!heard && (type==message_type_talk || type==message_type_emote))
715 message_send_text(me,message_type_info,me,"No one hears you.");
716 }
717
718 extern int channel_ban_user(t_channel * channel, char const * user)
719 {
720 t_elem const * curr;
721 char * temp;
722
723 if (!channel)
724 {
725 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
726 return -1;
727 }
728 if (!user)
729 {
730 eventlog(eventlog_level_error,__FUNCTION__,"got NULL user");
731 return -1;
732 }
733 if (!channel->name)
734 {
735 eventlog(eventlog_level_error,__FUNCTION__,"got channel with NULL name");
736 return -1;
737 }
738
739 if (strcasecmp(channel->name,CHANNEL_NAME_BANNED)==0 ||
740 strcasecmp(channel->name,CHANNEL_NAME_KICKED)==0)
741 return -1;
742
743 LIST_TRAVERSE_CONST(channel->banlist,curr)
744 if (strcasecmp(elem_get_data(curr),user)==0)
745 return 0;
746
747 temp = xstrdup(user);
748 list_append_data(channel->banlist,temp);
749
750 return 0;
751 }
752
753
754 extern int channel_unban_user(t_channel * channel, char const * user)
755 {
756 t_elem * curr;
757
758 if (!channel)
759 {
760 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
761 return -1;
762 }
763 if (!user)
764 {
765 eventlog(eventlog_level_error,__FUNCTION__,"got NULL user");
766 return -1;
767 }
768
769 LIST_TRAVERSE(channel->banlist,curr)
770 {
771 char const * banned;
772
773 if (!(banned = elem_get_data(curr)))
774 {
775 eventlog(eventlog_level_error,__FUNCTION__,"found NULL name in banlist");
776 continue;
777 }
778 if (strcasecmp(banned,user)==0)
779 {
780 if (list_remove_elem(channel->banlist,&curr)<0)
781 {
782 eventlog(eventlog_level_error,__FUNCTION__,"unable to remove item from list");
783 return -1;
784 }
785 xfree((void *)banned); /* avoid warning */
786 return 0;
787 }
788 }
789
790 return -1;
791 }
792
793
794 extern int channel_check_banning(t_channel const * channel, t_connection const * user)
795 {
796 t_elem const * curr;
797
798 if (!channel)
799 {
800 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
801 return -1;
802 }
803 if (!user)
804 {
805 eventlog(eventlog_level_error,__FUNCTION__,"got NULL user");
806 return -1;
807 }
808
809 if (!(channel->flags & channel_flags_allowbots) && conn_get_class(user)==conn_class_bot)
810 return 1;
811
812 LIST_TRAVERSE_CONST(channel->banlist,curr)
813 if (conn_match(user,elem_get_data(curr))==1)
814 return 1;
815
816 return 0;
817 }
818
819
820 extern int channel_get_length(t_channel const * channel)
821 {
822 t_channelmember const * curr;
823 int count;
824
825 for (curr=channel->memberlist,count=0; curr; curr=curr->next,count++);
826
827 return count;
828 }
829
830
831 extern t_connection * channel_get_first(t_channel const * channel)
832 {
833 if (!channel)
834 {
835 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
836 return NULL;
837 }
838
839 memberlist_curr = channel->memberlist;
840
841 return channel_get_next();
842 }
843
844 extern t_connection * channel_get_next(void)
845 {
846
847 t_channelmember * member;
848
849 if (memberlist_curr)
850 {
851 member = memberlist_curr;
852 memberlist_curr = memberlist_curr->next;
853
854 return member->connection;
855 }
856 return NULL;
857 }
858
859
860 extern t_list * channel_get_banlist(t_channel const * channel)
861 {
862 if (!channel)
863 {
864 eventlog(eventlog_level_warn,__FUNCTION__,"got NULL channel");
865 return NULL;
866 }
867
868 return channel->banlist;
869 }
870
871
872 extern char const * channel_get_shortname(t_channel const * channel)
873 {
874 if (!channel)
875 {
876 eventlog(eventlog_level_warn,__FUNCTION__,"got NULL channel");
877 return NULL;
878 }
879
880 return channel->shortname;
881 }
882
883
884 static int channellist_load_permanent(char const * filename)
885 {
886 FILE * fp;
887 unsigned int line;
888 unsigned int pos;
889 int botflag;
890 int operflag;
891 int logflag;
892 unsigned int modflag;
893 char * buff;
894 char * name;
895 char * sname;
896 char * tag;
897 char * bot;
898 char * oper;
899 char * log;
900 char * country;
901 char * max;
902 char * moderated;
903 char * newname;
904 char * realmname;
905
906 if (!filename)
907 {
908 eventlog(eventlog_level_error,__FUNCTION__,"got NULL filename");
909 return -1;
910 }
911
912 if (!(fp = fopen(filename,"r")))
913 {
914 eventlog(eventlog_level_error,__FUNCTION__,"could not open channel file \"%s\" for reading (fopen: %s)",filename,pstrerror(errno));
915 return -1;
916 }
917
918 for (line=1; (buff = file_get_line(fp)); line++)
919 {
920 if (buff[0]=='#' || buff[0]=='\0')
921 {
922 continue;
923 }
924 pos = 0;
925 if (!(name = next_token(buff,&pos)))
926 {
927 eventlog(eventlog_level_error,__FUNCTION__,"missing name in line %u in file \"%s\"",line,filename);
928 continue;
929 }
930 if (!(sname = next_token(buff,&pos)))
931 {
932 eventlog(eventlog_level_error,__FUNCTION__,"missing sname in line %u in file \"%s\"",line,filename);
933 continue;
934 }
935 if (!(tag = next_token(buff,&pos)))
936 {
937 eventlog(eventlog_level_error,__FUNCTION__,"missing tag in line %u in file \"%s\"",line,filename);
938 continue;
939 }
940 if (!(bot = next_token(buff,&pos)))
941 {
942 eventlog(eventlog_level_error,__FUNCTION__,"missing bot in line %u in file \"%s\"",line,filename);
943 continue;
944 }
945 if (!(oper = next_token(buff,&pos)))
946 {
947 eventlog(eventlog_level_error,__FUNCTION__,"missing oper in line %u in file \"%s\"",line,filename);
948 continue;
949 }
950 if (!(log = next_token(buff,&pos)))
951 {
952 eventlog(eventlog_level_error,__FUNCTION__,"missing log in line %u in file \"%s\"",line,filename);
953 continue;
954 }
955 if (!(country = next_token(buff,&pos)))
956 {
957 eventlog(eventlog_level_error,__FUNCTION__,"missing country in line %u in file \"%s\"",line,filename);
958 continue;
959 }
960 if (!(realmname = next_token(buff,&pos)))
961 {
962 eventlog(eventlog_level_error,__FUNCTION__,"missing realmname in line %u in file \"%s\"",line,filename);
963 continue;
964 }
965 if (!(max = next_token(buff,&pos)))
966 {
967 eventlog(eventlog_level_error,__FUNCTION__,"missing max in line %u in file \"%s\"",line,filename);
968 continue;
969 }
970 if (!(moderated = next_token(buff,&pos)))
971 {
972 eventlog(eventlog_level_error,__FUNCTION__,"missing mod in line %u in file \"%s\"",line,filename);
973 continue;
974 }
975
976 switch (str_get_bool(bot))
977 {
978 case 1:
979 botflag = 1;
980 break;
981 case 0:
982 botflag = 0;
983 break;
984 default:
985 eventlog(eventlog_level_error,__FUNCTION__,"invalid boolean value \"%s\" for field 4 on line %u in file \"%s\"",bot,line,filename);
986 continue;
987 }
988
989 switch (str_get_bool(oper))
990 {
991 case 1:
992 operflag = 1;
993 break;
994 case 0:
995 operflag = 0;
996 break;
997 default:
998 eventlog(eventlog_level_error,__FUNCTION__,"invalid boolean value \"%s\" for field 5 on line %u in file \"%s\"",oper,line,filename);
999 continue;
1000 }
1001
1002 switch (str_get_bool(log))
1003 {
1004 case 1:
1005 logflag = 1;
1006 break;
1007 case 0:
1008 logflag = 0;
1009 break;
1010 default:
1011 eventlog(eventlog_level_error,__FUNCTION__,"invalid boolean value \"%s\" for field 5 on line %u in file \"%s\"",log,line,filename);
1012 continue;
1013 }
1014
1015 switch (str_get_bool(moderated))
1016 {
1017 case 1:
1018 modflag = 1;
1019 break;
1020 case 0:
1021 modflag = 0;
1022 break;
1023 default:
1024 eventlog(eventlog_level_error,__FUNCTION__,"invalid boolean value \"%s\" for field 10 on line %u in file \"%s\"",moderated,line,filename);
1025 continue;
1026 }
1027
1028 if (strcmp(sname,"NULL") == 0)
1029 sname = NULL;
1030 if (strcmp(tag,"NULL") == 0)
1031 tag = NULL;
1032 if (strcmp(name,"NONE") == 0)
1033 name = NULL;
1034 if (strcmp(country, "NULL") == 0)
1035 country = NULL;
1036 if (strcmp(realmname,"NULL") == 0)
1037 realmname = NULL;
1038
1039 if (name)
1040 {
1041 channel_create(name,sname,tag,1,botflag,operflag,logflag,country,realmname,atoi(max),modflag,0,0);
1042 }
1043 else
1044 {
1045 newname = channel_format_name(sname,country,realmname,1);
1046 if (newname)
1047 {
1048 channel_create(newname,sname,tag,1,botflag,operflag,logflag,country,realmname,atoi(max),modflag,0,1);
1049 xfree(newname);
1050 }
1051 else
1052 {
1053 eventlog(eventlog_level_error,__FUNCTION__,"cannot format channel name");
1054 }
1055 }
1056
1057 /* FIXME: call channel_delete() on current perm channels and do a
1058 channellist_find_channel() and set the long name, perm flag, etc,
1059 otherwise call channel_create(). This will make HUPing the server
1060 handle re-reading this file correctly. */
1061 }
1062
1063 file_get_line(NULL); // clear file_get_line buffer
1064 if (fclose(fp)<0)
1065 eventlog(eventlog_level_error,__FUNCTION__,"could not close channel file \"%s\" after reading (fclose: %s)",filename,pstrerror(errno));
1066 return 0;
1067 }
1068
1069 static char * channel_format_name(char const * sname, char const * country, char const * realmname, unsigned int id)
1070 {
1071 char * fullname;
1072 unsigned int len;
1073
1074 if (!sname)
1075 {
1076 eventlog(eventlog_level_error,__FUNCTION__,"got NULL sname");
1077 return NULL;
1078 }
1079 len = strlen(sname)+1; /* FIXME: check lengths and format */
1080 if (country)
1081 len = len + strlen(country) + 1;
1082 if (realmname)
1083 len = len + strlen(realmname) + 1;
1084 len = len + 32 + 1;
1085
1086 fullname=xmalloc(len);
1087 sprintf(fullname,"%s%s%s%s%s-%d",
1088 realmname?realmname:"",
1089 realmname?" ":"",
1090 sname,
1091 country?" ":"",
1092 country?country:"",
1093 id);
1094 return fullname;
1095 }
1096
1097 extern int channellist_reload(void)
1098 {
1099 t_elem * curr;
1100 t_channel * channel, * old_channel;
1101 t_channelmember * memberlist, * member, * old_member;
1102 t_list * channellist_old;
1103
1104 if (channellist_head)
1105 {
1106
1107 channellist_old = list_create();
1108
1109 /* First pass - get members */
1110 LIST_TRAVERSE(channellist_head,curr)
1111 {
1112 if (!(channel = elem_get_data(curr)))
1113 {
1114 eventlog(eventlog_level_error,__FUNCTION__,"channel list contains NULL item");
1115 continue;
1116 }
1117 /* Trick to avoid automatic channel destruction */
1118 channel->flags |= channel_flags_permanent;
1119 if (channel->memberlist)
1120 {
1121 /* we need only channel name and memberlist */
1122
1123 old_channel = (t_channel *) xmalloc(sizeof(t_channel));
1124 old_channel->shortname = xstrdup(channel->shortname);
1125 old_channel->memberlist = NULL;
1126 member = channel->memberlist;
1127
1128 /* First pass */
1129 while (member)
1130 {
1131 old_member = xmalloc(sizeof(t_channelmember));
1132 old_member->connection = member->connection;
1133
1134 if (old_channel->memberlist)
1135 old_member->next = old_channel->memberlist;
1136 else
1137 old_member->next = NULL;
1138
1139 old_channel->memberlist = old_member;
1140 member = member->next;
1141 }
1142
1143 /* Second pass - remove connections from channel */
1144 member = old_channel->memberlist;
1145 while (member)
1146 {
1147 channel_del_connection(channel,member->connection);
1148 conn_set_channel_var(member->connection,NULL);
1149 member = member->next;
1150 }
1151
1152 list_prepend_data(channellist_old,old_channel);
1153 }
1154
1155 /* Channel is empty - Destroying it */
1156 channel->flags &= ~channel_flags_permanent;
1157 if (channel_destroy(channel,&curr)<0)
1158 eventlog(eventlog_level_error,__FUNCTION__,"could not destroy channel");
1159
1160 }
1161
1162 /* Cleanup and reload */
1163
1164 if (list_destroy(channellist_head)<0)
1165 return -1;
1166
1167 channellist_head = NULL;
1168 channellist_create();
1169
1170 /* Now put all users on their previous channel */
1171
1172 LIST_TRAVERSE(channellist_old,curr)
1173 {
1174 if (!(channel = elem_get_data(curr)))
1175 {
1176 eventlog(eventlog_level_error,__FUNCTION__,"old channel list contains NULL item");
1177 continue;
1178 }
1179
1180 memberlist = channel->memberlist;
1181 while (memberlist)
1182 {
1183 member = memberlist;
1184 memberlist = memberlist->next;
1185 conn_set_channel(member->connection, channel->shortname);
1186 }
1187 }
1188
1189
1190 /* Ross don't blame me for this but this way the code is cleaner */
1191
1192 LIST_TRAVERSE(channellist_old,curr)
1193 {
1194 if (!(channel = elem_get_data(curr)))
1195 {
1196 eventlog(eventlog_level_error,__FUNCTION__,"old channel list contains NULL item");
1197 continue;
1198 }
1199
1200 memberlist = channel->memberlist;
1201 while (memberlist)
1202 {
1203 member = memberlist;
1204 memberlist = memberlist->next;
1205 xfree((void*)member);
1206 }
1207
1208 if (channel->shortname)
1209 xfree((void*)channel->shortname);
1210
1211 if (list_remove_data(channellist_old,channel,&curr)<0)
1212 eventlog(eventlog_level_error,__FUNCTION__,"could not remove item from list");
1213 xfree((void*)channel);
1214
1215 }
1216
1217 if (list_destroy(channellist_old)<0)
1218 return -1;
1219 }
1220 return 0;
1221
1222 }
1223
1224 extern int channellist_create(void)
1225 {
1226 channellist_head = list_create();
1227
1228 return channellist_load_permanent(prefs_get_channelfile());
1229 }
1230
1231
1232 extern int channellist_destroy(void)
1233 {
1234 t_channel * channel;
1235 t_elem * curr;
1236
1237 if (channellist_head)
1238 {
1239 LIST_TRAVERSE(channellist_head,curr)
1240 {
1241 if (!(channel = elem_get_data(curr))) /* should not happen */
1242 {
1243 eventlog(eventlog_level_error,__FUNCTION__,"channel list contains NULL item");
1244 continue;
1245 }
1246
1247 channel_destroy(channel,&curr);
1248 }
1249
1250 if (list_destroy(channellist_head)<0)
1251 return -1;
1252 channellist_head = NULL;
1253 }
1254
1255 return 0;
1256 }
1257
1258
1259 extern t_list * channellist(void)
1260 {
1261 return channellist_head;
1262 }
1263
1264
1265 extern int channellist_get_length(void)
1266 {
1267 return list_get_length(channellist_head);
1268 }
1269
1270 extern int channel_get_max(t_channel const * channel)
1271 {
1272 if (!channel)
1273 {
1274 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
1275 return 0;
1276 }
1277
1278 return channel->maxmembers;
1279 }
1280
1281 extern int channel_get_curr(t_channel const * channel)
1282 {
1283 if (!channel)
1284 {
1285 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
1286 return 0;
1287 }
1288
1289 return channel->currmembers;
1290
1291 }
1292
1293 extern int channel_conn_is_tmpOP(t_channel const * channel, t_connection * c)
1294 {
1295 if (!channel)
1296 {
1297 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
1298 return 0;
1299 }
1300
1301 if (!c)
1302 {
1303 eventlog(eventlog_level_error,__FUNCTION__,"got NULL account");
1304 return 0;
1305 }
1306
1307 if (!conn_get_tmpOP_channel(c)) return 0;
1308
1309 if (strcmp(conn_get_tmpOP_channel(c),channel_get_name(channel))==0) return 1;
1310
1311 return 0;
1312 }
1313
1314 extern int channel_conn_has_tmpVOICE(t_channel const * channel, t_connection * c)
1315 {
1316 if (!channel)
1317 {
1318 eventlog(eventlog_level_error,__FUNCTION__,"got NULL channel");
1319 return 0;
1320 }
1321
1322 if (!c)
1323 {
1324 eventlog(eventlog_level_error,__FUNCTION__,"got NULL account");
1325 return 0;
1326 }
1327
1328 if (!conn_get_tmpVOICE_channel(c)) return 0;
1329
1330 if (strcmp(conn_get_tmpVOICE_channel(c),channel_get_name(channel))==0) return 1;
1331
1332 return 0;
1333 }
1334
1335 static t_channel * channellist_find_channel_by_fullname(char const * name)
1336 {
1337 t_channel * channel;
1338 t_elem const * curr;
1339
1340 if (channellist_head)
1341 {
1342 LIST_TRAVERSE(channellist_head,curr)
1343 {
1344 channel = elem_get_data(curr);
1345 if (!channel->name)
1346 {
1347 eventlog(eventlog_level_error,__FUNCTION__,"found channel with NULL name");
1348 continue;
1349 }
1350
1351 if (strcasecmp(channel->name,name)==0)
1352 return channel;
1353 }
1354 }
1355
1356 return NULL;
1357 }
1358
1359
1360 /* Find a channel based on the name.
1361 * Create a new channel if it is a permanent-type channel and all others
1362 * are full.
1363 */
1364 extern t_channel * channellist_find_channel_by_name(char const * name, char const * country, char const * realmname)
1365 {
1366 t_channel * channel;
1367 t_elem const * curr;
1368 int foundperm;
1369 int foundlang;
1370 int maxchannel; /* the number of "rollover" channels that exist */
1371 char const * saveshortname;
1372 char const * savespecialname;
1373 char const * savetag;
1374 int savebotflag;
1375 int saveoperflag;
1376 int savelogflag;
1377 unsigned int savemoderated;
1378 char const * savecountry;
1379 char const * saverealmname;
1380 int savemaxmembers;
1381 t_channel * special_channel;
1382
1383 // try to make gcc happy and initialize all variables
1384 saveshortname = savespecialname = savetag = savecountry = saverealmname = NULL;
1385 savebotflag = saveoperflag = savelogflag = savemaxmembers = savemoderated = 0;
1386
1387 maxchannel = 0;
1388 foundperm = 0;
1389 foundlang = 0;
1390 if (channellist_head)
1391 {
1392 LIST_TRAVERSE(channellist_head,curr)
1393 {
1394 channel = elem_get_data(curr);
1395 if (!channel->name)
1396 {
1397 eventlog(eventlog_level_error,__FUNCTION__,"found channel with NULL name");
1398 continue;
1399 }
1400
1401 if (strcasecmp(channel->name,name)==0)
1402 {
1403 // eventlog(eventlog_level_debug,__FUNCTION__,"found exact match for \"%s\"",name);
1404 return channel;
1405 }
1406
1407 if (channel->shortname && strcasecmp(channel->shortname,name)==0)
1408 {
1409 special_channel = channellist_find_channel_by_name(channel->name,country,realmname);
1410 if (special_channel) channel= special_channel;
1411
1412 /* FIXME: what should we do if the client doesn't have a country? For now, just take the first
1413 * channel that would otherwise match. */
1414 if ( ((!channel->country && !foundlang) || !country ||
1415 (channel->country && country && (strcmp(channel->country, country)==0))) &&
1416 ((!channel->realmname && !realmname) ||
1417 (channel->realmname && realmname && (strcmp(channel->realmname, realmname)==0))) )
1418
1419 {
1420 if (channel->maxmembers==-1 || channel->currmembers<channel->maxmembers)
1421 {
1422 eventlog(eventlog_level_debug,__FUNCTION__,"found permanent channel \"%s\" for \"%s\"",channel->name,name);
1423 return channel;
1424 }
1425
1426 if (!foundlang && (channel->country)) //remember we had found a language specific channel but it was full
1427 {
1428 foundlang = 1;
1429 if (!(channel->flags & channel_flags_autoname))
1430 savespecialname = channel->name;
1431 maxchannel = 0;
1432 }
1433
1434 maxchannel++;
1435 }
1436
1437 // eventlog(eventlog_level_debug,__FUNCTION__,"countries didn't match");
1438
1439 foundperm = 1;
1440
1441 /* save off some info in case we need to create a new copy */
1442 saveshortname = channel->shortname;
1443 savetag = channel->clienttag;
1444 savebotflag = channel->flags & channel_flags_allowbots;
1445 saveoperflag = channel->flags & channel_flags_allowopers;
1446 if (channel->logname)
1447 savelogflag = 1;
1448 else
1449 savelogflag = 0;
1450 if (country)
1451 savecountry = country;
1452 else
1453 savecountry = channel->country;
1454 if (realmname)
1455 saverealmname = realmname;
1456 else
1457 saverealmname = channel->realmname;
1458 savemaxmembers = channel->maxmembers;
1459 savemoderated = channel->flags & channel_flags_moderated;
1460 }
1461 }
1462 }
1463
1464 /* we've gone thru the whole list and either there was no match or the
1465 * channels are all full.
1466 */
1467
1468 if (foundperm) /* All the channels were full, create a new one */
1469 {
1470 char * channelname;
1471
1472 if (!foundlang || !savespecialname)
1473 {
1474 if (!(channelname=channel_format_name(saveshortname,savecountry,saverealmname,maxchannel+1)))
1475 return NULL;
1476 }
1477 else
1478 {
1479 if (!(channelname=channel_format_name(savespecialname,NULL,saverealmname,maxchannel+1)))
1480 return NULL;
1481 }
1482
1483 channel = channel_create(channelname,saveshortname,savetag,1,savebotflag,saveoperflag,savelogflag,savecountry,saverealmname,savemaxmembers,savemoderated,0,1);
1484 xfree(channelname);
1485
1486 eventlog(eventlog_level_debug,__FUNCTION__,"created copy \"%s\" of channel \"%s\"",(channel)?(channel->name):("<failed>"),name);
1487 return channel;
1488 }
1489
1490 /* no match */
1491 eventlog(eventlog_level_debug,__FUNCTION__,"could not find channel \"%s\"",name);
1492 return NULL;
1493 }
1494
1495
1496 extern t_channel * channellist_find_channel_bychannelid(unsigned int channelid)
1497 {
1498 t_channel * channel;
1499 t_elem const * curr;
1500
1501 if (channellist_head)
1502 {
1503 LIST_TRAVERSE(channellist_head,curr)
1504 {
1505 channel = elem_get_data(curr);
1506 if (!channel->name)
1507 {
1508 eventlog(eventlog_level_error,__FUNCTION__,"found channel with NULL name");
1509 continue;
1510 }
1511 if (channel->id==channelid)
1512 return channel;
1513 }
1514 }
1515
1516 return NULL;
1517 }
1518
1519 extern int channel_set_userflags(t_connection * c)
1520 {
1521 unsigned int newflags;
1522 char const * channel;
1523 t_account * acc;
1524
1525 if (!c) return -1; // user not connected, no need to update his flags
1526
1527 acc = conn_get_account(c);
1528
1529 /* well... unfortunatly channel_get_name never returns NULL but "" instead
1530 so we first have to check if user is in a channel at all */
1531 if ((!conn_get_channel(c)) || (!(channel = channel_get_name(conn_get_channel(c)))))
1532 return -1;
1533
1534 if (account_get_auth_admin(acc,channel) == 1 || account_get_auth_admin(acc,NULL) == 1)
1535 newflags = MF_BLIZZARD;
1536 else if (account_get_auth_operator(acc,channel) == 1 ||
1537 account_get_auth_operator(acc,NULL) == 1)
1538 newflags = MF_BNET;
1539 else if (channel_conn_is_tmpOP(conn_get_channel(c),c))
1540 newflags = MF_GAVEL;
1541 else if ((account_get_auth_voice(acc,channel) == 1) ||
1542 (channel_conn_has_tmpVOICE(conn_get_channel(c),c)))
1543 newflags = MF_VOICE;
1544 else
1545 if ((conn_get_clienttag(c) == CLIENTTAG_WARCRAFT3_UINT) ||
1546 (conn_get_clienttag(c) == CLIENTTAG_WAR3XP_UINT))
1547 newflags = W3_ICON_SET;
1548 else
1549 newflags = 0;
1550
1551 if (conn_get_flags(c) != newflags) {
1552 conn_set_flags(c, newflags);
1553 channel_update_userflags(c);
1554 }
1555
1556 return 0;
1557 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1