1 /*
2 * channels.c -- part of channels.mod
3 * support for channels within the bot
4 */
5 /*
6 * Copyright (C) 1997 Robey Pointer
7 * Copyright (C) 1999 - 2021 Eggheads Development Team
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24 #define MODULE_NAME "channels"
25 #define MAKING_CHANNELS
26
27 #include <sys/stat.h>
28 #include "src/mod/module.h"
29
30 static Function *global = NULL;
31
32 static char chanfile[121], glob_chanmode[65];
33 static char *lastdeletedmask;
34
35 static struct udef_struct *udef;
36
37 static int use_info, chan_hack, quiet_save, global_revenge_mode,
38 global_stopnethack_mode, global_idle_kick, global_aop_min,
39 global_aop_max, global_ban_time, global_exempt_time,
40 global_invite_time, global_ban_type, allow_ps;
41
42 /* Global channel settings (drummer/dw) */
43 static char glob_chanset[512];
44
45 /* Global flood settings */
46 static int gfld_chan_thr, gfld_chan_time, gfld_deop_thr, gfld_deop_time,
47 gfld_kick_thr, gfld_kick_time, gfld_join_thr, gfld_join_time,
48 gfld_ctcp_thr, gfld_ctcp_time, gfld_nick_thr, gfld_nick_time;
49
50 #include "channels.h"
51 #include "cmdschan.c"
52 #include "tclchan.c"
53 #include "userchan.c"
54 #include "udefchan.c"
55
56
channel_malloc(int size,char * file,int line)57 static void *channel_malloc(int size, char *file, int line)
58 {
59 char *p;
60
61 #ifdef DEBUG_MEM
62 p = ((void *) (global[0] (size, MODULE_NAME, file, line)));
63 #else
64 p = nmalloc(size);
65 #endif
66 egg_bzero(p, size);
67 return p;
68 }
69
set_mode_protect(struct chanset_t * chan,char * set)70 static void set_mode_protect(struct chanset_t *chan, char *set)
71 {
72 int i, pos = 1;
73 char *s, *s1;
74
75 /* Clear old modes */
76 chan->mode_mns_prot = chan->mode_pls_prot = 0;
77 chan->limit_prot = 0;
78 chan->key_prot[0] = 0;
79 for (s = newsplit(&set); *s; s++) {
80 i = 0;
81 switch (*s) {
82 case '+':
83 pos = 1;
84 break;
85 case '-':
86 pos = 0;
87 break;
88 case 'i':
89 i = CHANINV;
90 break;
91 case 'p':
92 i = CHANPRIV;
93 break;
94 case 's':
95 i = CHANSEC;
96 break;
97 case 'm':
98 i = CHANMODER;
99 break;
100 case 'c':
101 i = CHANNOCLR;
102 break;
103 case 'C':
104 i = CHANNOCTCP;
105 break;
106 case 'R':
107 i = CHANREGON;
108 break;
109 case 'M':
110 i = CHANMODREG;
111 break;
112 case 'r':
113 i = CHANLONLY;
114 break;
115 case 'D':
116 i = CHANDELJN;
117 break;
118 case 'u':
119 i = CHANSTRIP;
120 break;
121 case 'N':
122 i = CHANNONOTC;
123 break;
124 case 'T':
125 i = CHANNOAMSG;
126 break;
127 case 't':
128 i = CHANTOPIC;
129 break;
130 case 'n':
131 i = CHANNOMSG;
132 break;
133 case 'a':
134 i = CHANANON;
135 break;
136 case 'q':
137 i = CHANQUIET;
138 break;
139 case 'l':
140 i = CHANLIMIT;
141 chan->limit_prot = 0;
142 if (pos) {
143 s1 = newsplit(&set);
144 if (s1[0])
145 chan->limit_prot = atoi(s1);
146 }
147 break;
148 case 'k':
149 i = CHANKEY;
150 chan->key_prot[0] = 0;
151 if (pos) {
152 s1 = newsplit(&set);
153 if (s1[0])
154 strlcpy(chan->key_prot, s1, sizeof chan->key_prot);
155 }
156 break;
157 }
158 if (i) {
159 if (pos) {
160 chan->mode_pls_prot |= i;
161 chan->mode_mns_prot &= ~i;
162 } else {
163 chan->mode_pls_prot &= ~i;
164 chan->mode_mns_prot |= i;
165 }
166 }
167 }
168 /* Prevents a +s-p +p-s flood (fixed by drummer) */
169 if (chan->mode_pls_prot & CHANSEC && !allow_ps)
170 chan->mode_pls_prot &= ~CHANPRIV;
171 }
172
get_mode_protect(struct chanset_t * chan,char * s)173 static void get_mode_protect(struct chanset_t *chan, char *s)
174 {
175 char *p = s, s1[122];
176 int i, tst;
177
178 s1[0] = 0;
179 for (i = 0; i < 2; i++) {
180 if (i == 0) {
181 tst = chan->mode_pls_prot;
182 if ((tst) || (chan->limit_prot != 0) || (chan->key_prot[0]))
183 *p++ = '+';
184 if (chan->limit_prot != 0) {
185 *p++ = 'l';
186 snprintf(s1 + strlen(s1), (sizeof s1) - strlen(s1), "%d ", chan->limit_prot);
187 }
188 if (chan->key_prot[0]) {
189 *p++ = 'k';
190 snprintf(s1 + strlen(s1), (sizeof s1) - strlen(s1), "%s ", chan->key_prot);
191 }
192 } else {
193 tst = chan->mode_mns_prot;
194 if (tst)
195 *p++ = '-';
196 if (tst & CHANKEY)
197 *p++ = 'k';
198 if (tst & CHANLIMIT)
199 *p++ = 'l';
200 }
201 if (tst & CHANINV)
202 *p++ = 'i';
203 if (tst & CHANPRIV)
204 *p++ = 'p';
205 if (tst & CHANSEC)
206 *p++ = 's';
207 if (tst & CHANMODER)
208 *p++ = 'm';
209 if (tst & CHANNOCLR)
210 *p++ = 'c';
211 if (tst & CHANNOCTCP)
212 *p++ = 'C';
213 if (tst & CHANREGON)
214 *p++ = 'R';
215 if (tst & CHANMODREG)
216 *p++ = 'M';
217 if (tst & CHANLONLY)
218 *p++ = 'r';
219 if (tst & CHANDELJN)
220 *p++ = 'D';
221 if (tst & CHANSTRIP)
222 *p++ = 'u';
223 if (tst & CHANNONOTC)
224 *p++ = 'N';
225 if (tst & CHANNOAMSG)
226 *p++ = 'T';
227 if (tst & CHANTOPIC)
228 *p++ = 't';
229 if (tst & CHANNOMSG)
230 *p++ = 'n';
231 if (tst & CHANANON)
232 *p++ = 'a';
233 if (tst & CHANQUIET)
234 *p++ = 'q';
235 }
236 *p = 0;
237 if (s1[0]) {
238 s1[strlen(s1) - 1] = 0;
239 strcat(s, " ");
240 strcat(s, s1);
241 }
242 }
243
244 /* Returns true if this is one of the channel masks
245 */
ismodeline(masklist * m,char * user)246 static int ismodeline(masklist *m, char *user)
247 {
248 for (; m && m->mask[0]; m = m->next)
249 if (!rfc_casecmp(m->mask, user))
250 return 1;
251 return 0;
252 }
253
254 /* Returns true if user matches one of the masklist -- drummer
255 */
ismasked(masklist * m,char * user)256 static int ismasked(masklist *m, char *user)
257 {
258 for (; m && m->mask[0]; m = m->next)
259 if (match_addr(m->mask, user))
260 return 1;
261 return 0;
262 }
263
264 /* Unlink chanset element from chanset list.
265 */
chanset_unlink(struct chanset_t * chan)266 static int chanset_unlink(struct chanset_t *chan)
267 {
268 struct chanset_t *c, *c_old = NULL;
269
270 for (c = chanset; c; c_old = c, c = c->next) {
271 if (c == chan) {
272 if (c_old)
273 c_old->next = c->next;
274 else
275 chanset = c->next;
276 return 1;
277 }
278 }
279 return 0;
280 }
281
282 /* Completely removes a channel.
283 *
284 * This includes the removal of all channel-bans, -exempts and -invites, as
285 * well as all user flags related to the channel.
286 */
remove_channel(struct chanset_t * chan)287 static void remove_channel(struct chanset_t *chan)
288 {
289 int i;
290 module_entry *me;
291
292 /* Remove the channel from the list, so that no one can pull it
293 * away from under our feet during the check_tcl_part() call. */
294 (void) chanset_unlink(chan);
295
296 if ((me = module_find("irc", 1, 3)) != NULL)
297 (me->funcs[IRC_DO_CHANNEL_PART]) (chan);
298
299 clear_channel(chan, 0);
300 noshare = 1;
301 /* Remove channel-bans */
302 while (chan->bans)
303 u_delban(chan, chan->bans->mask, 1);
304 /* Remove channel-exempts */
305 while (chan->exempts)
306 u_delexempt(chan, chan->exempts->mask, 1);
307 /* Remove channel-invites */
308 while (chan->invites)
309 u_delinvite(chan, chan->invites->mask, 1);
310 /* Remove channel specific user flags */
311 user_del_chan(chan->dname);
312 noshare = 0;
313 nfree(chan->channel.key);
314 for (i = 0; i < MODES_PER_LINE_MAX && chan->cmode[i].op; i++)
315 nfree(chan->cmode[i].op);
316 if (chan->key)
317 nfree(chan->key);
318 if (chan->rmkey)
319 nfree(chan->rmkey);
320 nfree(chan);
321 }
322
323 /* Bind this to chon and *if* the users console channel == ***
324 * then set it to a specific channel
325 */
channels_chon(char * handle,int idx)326 static int channels_chon(char *handle, int idx)
327 {
328 struct flag_record fr = { FR_CHAN | FR_ANYWH | FR_GLOBAL, 0, 0, 0, 0, 0 };
329 int find, found = 0;
330 struct chanset_t *chan = chanset;
331
332 if (dcc[idx].type == &DCC_CHAT) {
333 if (!findchan_by_dname(dcc[idx].u.chat->con_chan) &&
334 ((dcc[idx].u.chat->con_chan[0] != '*') ||
335 (dcc[idx].u.chat->con_chan[1] != 0))) {
336 get_user_flagrec(dcc[idx].user, &fr, NULL);
337 if (glob_op(fr))
338 found = 1;
339 if (chan_owner(fr))
340 find = USER_OWNER;
341 else if (chan_master(fr))
342 find = USER_MASTER;
343 else
344 find = USER_OP;
345 fr.match = FR_CHAN;
346 while (chan && !found) {
347 get_user_flagrec(dcc[idx].user, &fr, chan->dname);
348 if (fr.chan & find)
349 found = 1;
350 else
351 chan = chan->next;
352 }
353 if (!chan)
354 chan = chanset;
355 if (chan)
356 strcpy(dcc[idx].u.chat->con_chan, chan->dname);
357 else
358 strcpy(dcc[idx].u.chat->con_chan, "*");
359 }
360 }
361 return 0;
362 }
363
convert_element(char * src,char * dst)364 static char *convert_element(char *src, char *dst)
365 {
366 int flags;
367
368 Tcl_ScanElement(src, &flags);
369 /* Work around Tcl bug 3371644 (only present in 8.5.10) */
370 #ifdef TCL_DONT_QUOTE_HASH
371 flags |= TCL_DONT_QUOTE_HASH;
372 #endif
373 Tcl_ConvertElement(src, dst, flags);
374 return dst;
375 }
376
377 #define PLSMNS(x) (x ? '+' : '-')
378
379 /*
380 * Note:
381 * - We write chanmode "" too, so that the bot won't use default-chanmode
382 * instead of ""
383 * - We will write empty need-xxxx too, why not? (less code + laziness)
384 */
write_channels()385 static void write_channels()
386 {
387 FILE *f;
388 char s[sizeof chanfile + 4], w[1024], w2[1024], name[163];
389 char need1[242], need2[242], need3[242], need4[242], need5[242];
390 struct chanset_t *chan;
391 struct udef_struct *ul;
392
393 if (!chanfile[0])
394 return;
395 egg_snprintf(s, sizeof s, "%s~new", chanfile);
396 f = fopen(s, "w");
397 chmod(s, userfile_perm);
398 if (f == NULL) {
399 putlog(LOG_MISC, "*", "ERROR writing channel file.");
400 return;
401 }
402 if (!quiet_save)
403 putlog(LOG_MISC, "*", "Writing channel file...");
404 fprintf(f, "#Dynamic Channel File for %s (%s) -- written %s\n",
405 botnetnick, ver, ctime(&now));
406 for (chan = chanset; chan; chan = chan->next) {
407 convert_element(chan->dname, name);
408 get_mode_protect(chan, w);
409 convert_element(w, w2);
410 convert_element(chan->need_op, need1);
411 convert_element(chan->need_invite, need2);
412 convert_element(chan->need_key, need3);
413 convert_element(chan->need_unban, need4);
414 convert_element(chan->need_limit, need5);
415 fprintf(f,
416 "channel add %s { chanmode %s idle-kick %d stopnethack-mode %d "
417 "revenge-mode %d need-op %s need-invite %s need-key %s "
418 "need-unban %s need-limit %s flood-chan %d:%d flood-ctcp %d:%d "
419 "flood-join %d:%d flood-kick %d:%d flood-deop %d:%d "
420 "flood-nick %d:%d aop-delay %d:%d ban-type %d ban-time %d "
421 "exempt-time %d invite-time %d %cenforcebans %cdynamicbans "
422 "%cuserbans %cautoop %cautohalfop %cbitch %cgreet %cprotectops "
423 "%cprotecthalfops %cprotectfriends %cdontkickops %cstatuslog "
424 "%crevenge %crevengebot %cautovoice %csecret %cshared %ccycle "
425 "%cseen %cinactive %cdynamicexempts %cuserexempts %cdynamicinvites "
426 "%cuserinvites %cnodesynch %cstatic }" "\n",
427 name, w2, chan->idle_kick, chan->stopnethack_mode,
428 chan->revenge_mode, need1, need2, need3, need4, need5,
429 chan->flood_pub_thr, chan->flood_pub_time,
430 chan->flood_ctcp_thr, chan->flood_ctcp_time,
431 chan->flood_join_thr, chan->flood_join_time,
432 chan->flood_kick_thr, chan->flood_kick_time,
433 chan->flood_deop_thr, chan->flood_deop_time,
434 chan->flood_nick_thr, chan->flood_nick_time,
435 chan->aop_min, chan->aop_max, chan->ban_type, chan->ban_time,
436 chan->exempt_time, chan->invite_time,
437 PLSMNS(channel_enforcebans(chan)),
438 PLSMNS(channel_dynamicbans(chan)),
439 PLSMNS(!channel_nouserbans(chan)),
440 PLSMNS(channel_autoop(chan)),
441 PLSMNS(channel_autohalfop(chan)),
442 PLSMNS(channel_bitch(chan)),
443 PLSMNS(channel_greet(chan)),
444 PLSMNS(channel_protectops(chan)),
445 PLSMNS(channel_protecthalfops(chan)),
446 PLSMNS(channel_protectfriends(chan)),
447 PLSMNS(channel_dontkickops(chan)),
448 PLSMNS(channel_logstatus(chan)),
449 PLSMNS(channel_revenge(chan)),
450 PLSMNS(channel_revengebot(chan)),
451 PLSMNS(channel_autovoice(chan)),
452 PLSMNS(channel_secret(chan)),
453 PLSMNS(channel_shared(chan)),
454 PLSMNS(channel_cycle(chan)),
455 PLSMNS(channel_seen(chan)),
456 PLSMNS(channel_inactive(chan)),
457 PLSMNS(channel_dynamicexempts(chan)),
458 PLSMNS(!channel_nouserexempts(chan)),
459 PLSMNS(channel_dynamicinvites(chan)),
460 PLSMNS(!channel_nouserinvites(chan)),
461 PLSMNS(channel_nodesynch(chan)),
462 PLSMNS(channel_static(chan)));
463 for (ul = udef; ul; ul = ul->next) {
464 if (ul->defined && ul->name) {
465 if (ul->type == UDEF_FLAG)
466 fprintf(f, "channel set %s %c%s%s\n", name, getudef(ul->values,
467 chan->dname) ? '+' : '-', "udef-flag-", ul->name);
468 else if (ul->type == UDEF_INT)
469 fprintf(f, "channel set %s %s%s %d\n", name, "udef-int-", ul->name,
470 (int) getudef(ul->values, chan->dname));
471 else if (ul->type == UDEF_STR) {
472 char *p = (char *) getudef(ul->values, chan->dname);
473
474 if (!p)
475 p = "{}";
476
477 fprintf(f, "channel set %s udef-str-%s %s\n", name, ul->name, p);
478 } else
479 debug1("UDEF-ERROR: unknown type %d", ul->type);
480 }
481 }
482 if (fflush(f)) {
483 putlog(LOG_MISC, "*", "ERROR writing channel file.");
484 fclose(f);
485 return;
486 }
487 }
488 fclose(f);
489 unlink(chanfile);
490 movefile(s, chanfile);
491 }
492
read_channels(int create,int reload)493 static void read_channels(int create, int reload)
494 {
495 struct chanset_t *chan, *chan_next;
496
497 if (!chanfile[0])
498 return;
499
500 if (reload)
501 for (chan = chanset; chan; chan = chan->next)
502 chan->status |= CHAN_FLAGGED;
503
504 chan_hack = 1;
505 if (!readtclprog(chanfile) && create) {
506 FILE *f;
507
508 /* Assume file isnt there & therefore make it */
509 putlog(LOG_MISC, "*", "Creating channel file");
510 f = fopen(chanfile, "w");
511 if (!f)
512 putlog(LOG_MISC, "*", "Couldn't create channel file: %s. Dropping",
513 chanfile);
514 else
515 fclose(f);
516 }
517 chan_hack = 0;
518 if (!reload)
519 return;
520 for (chan = chanset; chan; chan = chan_next) {
521 chan_next = chan->next;
522 if (chan->status & CHAN_FLAGGED) {
523 putlog(LOG_MISC, "*", "No longer supporting channel %s", chan->dname);
524 remove_channel(chan);
525 }
526 }
527 }
528
backup_chanfile()529 static void backup_chanfile()
530 {
531 char s[sizeof chanfile + 4];
532
533 if (quiet_save < 2)
534 putlog(LOG_MISC, "*", "Backing up channel file...");
535 egg_snprintf(s, sizeof s, "%s~bak", chanfile);
536 copyfile(chanfile, s);
537 }
538
channels_prerehash()539 static void channels_prerehash()
540 {
541 write_channels();
542 }
543
channels_rehash()544 static void channels_rehash()
545 {
546 /* add channels from the chanfile but don't remove missing ones */
547 read_channels(1, 0);
548 write_channels();
549 }
550
551 static cmd_t my_chon[] = {
552 {"*", "", (IntFunc) channels_chon, "channels:chon"},
553 {NULL, NULL, NULL, NULL}
554 };
555
channels_report(int idx,int details)556 static void channels_report(int idx, int details)
557 {
558 int i;
559 char s[1024], s1[100], s2[100];
560 struct chanset_t *chan;
561 struct flag_record fr = { FR_CHAN | FR_GLOBAL, 0, 0, 0, 0, 0 };
562
563 for (chan = chanset; chan; chan = chan->next) {
564
565 /* Get user's flags if output isn't going to stdout */
566 if (idx != DP_STDOUT)
567 get_user_flagrec(dcc[idx].user, &fr, chan->dname);
568
569 /* Don't show channel information to someone who isn't a master */
570 if ((idx != DP_STDOUT) && !glob_master(fr) && !chan_master(fr))
571 continue;
572
573 s[0] = 0;
574
575 sprintf(s, " %-20s: ", chan->dname);
576
577 if (channel_inactive(chan))
578 strcat(s, "(inactive)");
579 else if (channel_pending(chan))
580 strcat(s, "(pending)");
581 else if (!channel_active(chan))
582 strcat(s, "(not on channel)");
583 else {
584
585 s1[0] = 0;
586 sprintf(s1, "%3d member%s", chan->channel.members,
587 (chan->channel.members == 1) ? "" : "s");
588 strcat(s, s1);
589
590 s2[0] = 0;
591 get_mode_protect(chan, s2);
592
593 if (s2[0]) {
594 int len = strlen(s);
595 egg_snprintf(s + len, (sizeof s) - len, ", enforcing \"%s\"", s2); /* Concatenation */
596 }
597
598 s2[0] = 0;
599
600 if (channel_greet(chan))
601 strcat(s2, "greet, ");
602 if (channel_autoop(chan))
603 strcat(s2, "auto-op, ");
604 if (channel_bitch(chan))
605 strcat(s2, "bitch, ");
606
607 if (s2[0]) {
608 int len = strlen(s);
609 s2[strlen(s2) - 2] = 0;
610 egg_snprintf(s + len, (sizeof s) - len, " (%s)", s2); /* Concatenation */
611 }
612
613 /* If it's a !chan, we want to display it's unique name too <cybah> */
614 if (chan->dname[0] == '!') {
615 int len = strlen(s);
616 egg_snprintf(s + len, (sizeof s) - len, ", unique name %s", chan->name); /* Concatenation */
617 }
618 }
619
620 dprintf(idx, "%s\n", s);
621
622 if (details) {
623 s[0] = 0;
624 i = 0;
625
626 if (channel_enforcebans(chan))
627 i += my_strcpy(s + i, "enforcebans ");
628 if (channel_dynamicbans(chan))
629 i += my_strcpy(s + i, "dynamicbans ");
630 if (!channel_nouserbans(chan))
631 i += my_strcpy(s + i, "userbans ");
632 if (channel_autoop(chan))
633 i += my_strcpy(s + i, "autoop ");
634 if (channel_bitch(chan))
635 i += my_strcpy(s + i, "bitch ");
636 if (channel_greet(chan))
637 i += my_strcpy(s + i, "greet ");
638 if (channel_protectops(chan))
639 i += my_strcpy(s + i, "protectops ");
640 if (channel_protecthalfops(chan))
641 i += my_strcpy(s + i, "protecthalfops ");
642 if (channel_protectfriends(chan))
643 i += my_strcpy(s + i, "protectfriends ");
644 if (channel_dontkickops(chan))
645 i += my_strcpy(s + i, "dontkickops ");
646 if (channel_logstatus(chan))
647 i += my_strcpy(s + i, "statuslog ");
648 if (channel_revenge(chan))
649 i += my_strcpy(s + i, "revenge ");
650 if (channel_revenge(chan))
651 i += my_strcpy(s + i, "revengebot ");
652 if (channel_secret(chan))
653 i += my_strcpy(s + i, "secret ");
654 if (channel_shared(chan))
655 i += my_strcpy(s + i, "shared ");
656 if (!channel_static(chan))
657 i += my_strcpy(s + i, "dynamic ");
658 if (channel_autovoice(chan))
659 i += my_strcpy(s + i, "autovoice ");
660 if (channel_autohalfop(chan))
661 i += my_strcpy(s + i, "autohalfop ");
662 if (channel_cycle(chan))
663 i += my_strcpy(s + i, "cycle ");
664 if (channel_seen(chan))
665 i += my_strcpy(s + i, "seen ");
666 if (channel_dynamicexempts(chan))
667 i += my_strcpy(s + i, "dynamicexempts ");
668 if (!channel_nouserexempts(chan))
669 i += my_strcpy(s + i, "userexempts ");
670 if (channel_dynamicinvites(chan))
671 i += my_strcpy(s + i, "dynamicinvites ");
672 if (!channel_nouserinvites(chan))
673 i += my_strcpy(s + i, "userinvites ");
674 if (channel_inactive(chan))
675 i += my_strcpy(s + i, "inactive ");
676 if (channel_nodesynch(chan))
677 my_strcpy(s + i, "nodesynch ");
678
679 dprintf(idx, " Options: %s\n", s);
680
681 if (chan->need_op[0])
682 dprintf(idx, " To get ops, I do: %s\n", chan->need_op);
683
684 if (chan->need_invite[0])
685 dprintf(idx, " To get invited, I do: %s\n", chan->need_invite);
686
687 if (chan->need_limit[0])
688 dprintf(idx, " To get the channel limit raised, I do: %s\n",
689 chan->need_limit);
690
691 if (chan->need_unban[0])
692 dprintf(idx, " To get unbanned, I do: %s\n", chan->need_unban);
693
694 if (chan->need_key[0])
695 dprintf(idx, " To get the channel key, I do: %s\n",
696 chan->need_key);
697
698 if (chan->idle_kick)
699 dprintf(idx, " Kicking idle users after %d minute%s\n",
700 chan->idle_kick, (chan->idle_kick != 1) ? "s" : "");
701
702 if (chan->stopnethack_mode)
703 dprintf(idx, " stopnethack-mode: %d\n", chan->stopnethack_mode);
704
705 if (chan->revenge_mode)
706 dprintf(idx, " revenge-mode: %d\n", chan->revenge_mode);
707
708 dprintf(idx, " ban-type: %d\n", chan->ban_type);
709 dprintf(idx, " Bans last %d minute%s.\n", chan->ban_time,
710 (chan->ban_time == 1) ? "" : "s");
711 dprintf(idx, " Exemptions last %d minute%s.\n", chan->exempt_time,
712 (chan->exempt_time == 1) ? "" : "s");
713 dprintf(idx, " Invitations last %d minute%s.\n", chan->invite_time,
714 (chan->invite_time == 1) ? "" : "s");
715 }
716 }
717 }
718
expmem_masklist(masklist * m)719 static int expmem_masklist(masklist *m)
720 {
721 int result = 0;
722
723 for (; m; m = m->next) {
724 result += sizeof(masklist);
725 if (m->mask)
726 result += strlen(m->mask) + 1;
727 if (m->who)
728 result += strlen(m->who) + 1;
729 }
730 return result;
731 }
732
channels_expmem()733 static int channels_expmem()
734 {
735 int tot = 0, i;
736 struct chanset_t *chan;
737
738 for (chan = chanset; chan; chan = chan->next) {
739 tot += sizeof(struct chanset_t);
740
741 tot += strlen(chan->channel.key) + 1;
742 if (chan->channel.topic)
743 tot += strlen(chan->channel.topic) + 1;
744 tot += (sizeof(struct memstruct) * (chan->channel.members + 1));
745
746 tot += expmem_masklist(chan->channel.ban);
747 tot += expmem_masklist(chan->channel.exempt);
748 tot += expmem_masklist(chan->channel.invite);
749
750 for (i = 0; i < MODES_PER_LINE_MAX && chan->cmode[i].op; i++)
751 tot += strlen(chan->cmode[i].op) + 1;
752 if (chan->key)
753 tot += strlen(chan->key) + 1;
754 if (chan->rmkey)
755 tot += strlen(chan->rmkey) + 1;
756 }
757 tot += expmem_udef(udef);
758 if (lastdeletedmask)
759 tot += strlen(lastdeletedmask) + 1;
760 return tot;
761 }
762
traced_globchanset(ClientData cdata,Tcl_Interp * irp,EGG_CONST char * name1,EGG_CONST char * name2,int flags)763 static char *traced_globchanset(ClientData cdata, Tcl_Interp *irp,
764 EGG_CONST char *name1,
765 EGG_CONST char *name2, int flags)
766 {
767 int i, items;
768 char *t, *s;
769 EGG_CONST char **item, *s2;
770
771 if (flags & (TCL_TRACE_READS | TCL_TRACE_UNSETS)) {
772 Tcl_SetVar2(interp, name1, name2, glob_chanset, TCL_GLOBAL_ONLY);
773 if (flags & TCL_TRACE_UNSETS) {
774 Tcl_TraceVar(interp, "global-chanset",
775 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
776 traced_globchanset, NULL); /* keep for backward compatibility */
777 Tcl_TraceVar(interp, "default-chanset",
778 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
779 traced_globchanset, NULL);
780 }
781 } else { /* Write */
782 s2 = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
783 Tcl_SplitList(interp, s2, &items, &item);
784 for (i = 0; i < items; i++) {
785 if (!(item[i]) || (strlen(item[i]) < 2))
786 continue;
787 s = glob_chanset;
788 while (s[0]) {
789 t = strchr(s, ' '); /* Can't be NULL coz of the extra space */
790 t[0] = 0;
791 if (!strcmp(s + 1, item[i] + 1)) {
792 s[0] = item[i][0]; /* +- */
793 t[0] = ' ';
794 break;
795 }
796 t[0] = ' ';
797 s = t + 1;
798 }
799 }
800 if (item) /* hmm it cant be 0 */
801 Tcl_Free((char *) item);
802 Tcl_SetVar2(interp, name1, name2, glob_chanset, TCL_GLOBAL_ONLY);
803 }
804 return NULL;
805 }
806
807 static tcl_ints my_tcl_ints[] = {
808 {"share-greet", NULL, 0},
809 {"use-info", &use_info, 0},
810 {"quiet-save", &quiet_save, 0},
811 {"allow-ps", &allow_ps, 0},
812 {"default-stopnethack-mode", &global_stopnethack_mode, 0},
813 {"default-revenge-mode", &global_revenge_mode, 0},
814 {"default-idle-kick", &global_idle_kick, 0},
815 {"default-ban-time", &global_ban_time, 0},
816 {"default-exempt-time", &global_exempt_time, 0},
817 {"default-invite-time", &global_invite_time, 0},
818 {"default-ban-type", &global_ban_type, 0},
819 /* keep global-* for backward compatibility */
820 {"global-stopnethack-mode", &global_stopnethack_mode, 0},
821 {"global-revenge-mode", &global_revenge_mode, 0},
822 {"global-idle-kick", &global_idle_kick, 0},
823 {"global-ban-time", &global_ban_time, 0},
824 {"global-exempt-time", &global_exempt_time, 0},
825 {"global-invite-time", &global_invite_time, 0},
826 {"global-ban-type", &global_ban_type, 0},
827 /* keeping [ban|exempt|invite]-time for compatibility <Wcc[07/20/02]> */
828 {"ban-time", &global_ban_time, 0},
829 {"exempt-time", &global_exempt_time, 0},
830 {"invite-time", &global_invite_time, 0},
831 {NULL, NULL, 0}
832 };
833
834 static tcl_coups mychan_tcl_coups[] = {
835 {"default-flood-chan", &gfld_chan_thr, &gfld_chan_time},
836 {"default-flood-deop", &gfld_deop_thr, &gfld_deop_time},
837 {"default-flood-kick", &gfld_kick_thr, &gfld_kick_time},
838 {"default-flood-join", &gfld_join_thr, &gfld_join_time},
839 {"default-flood-ctcp", &gfld_ctcp_thr, &gfld_ctcp_time},
840 {"default-flood-nick", &gfld_nick_thr, &gfld_nick_time},
841 {"default-aop-delay", &global_aop_min, &global_aop_max},
842 /* keep global-* for backward compatibility */
843 {"global-flood-chan", &gfld_chan_thr, &gfld_chan_time},
844 {"global-flood-deop", &gfld_deop_thr, &gfld_deop_time},
845 {"global-flood-kick", &gfld_kick_thr, &gfld_kick_time},
846 {"global-flood-join", &gfld_join_thr, &gfld_join_time},
847 {"global-flood-ctcp", &gfld_ctcp_thr, &gfld_ctcp_time},
848 {"global-flood-nick", &gfld_nick_thr, &gfld_nick_time},
849 {"global-aop-delay", &global_aop_min, &global_aop_max},
850 {NULL, NULL, NULL}
851 };
852
853 static tcl_strings my_tcl_strings[] = {
854 {"chanfile", chanfile, 120, STR_PROTECT},
855 {"default-chanmode", glob_chanmode, 64, 0},
856 /* keep global-chanmode for backward compatibility */
857 {"global-chanmode", glob_chanmode, 64, 0},
858 {NULL, NULL, 0, 0}
859 };
860
channels_close()861 static char *channels_close()
862 {
863 write_channels();
864 free_udef(udef);
865 if (lastdeletedmask)
866 nfree(lastdeletedmask);
867 rem_builtins(H_chon, my_chon);
868 rem_builtins(H_dcc, C_dcc_irc);
869 rem_tcl_commands(channels_cmds);
870 rem_tcl_strings(my_tcl_strings);
871 rem_tcl_ints(my_tcl_ints);
872 rem_tcl_coups(mychan_tcl_coups);
873 del_hook(HOOK_USERFILE, (Function) channels_writeuserfile);
874 del_hook(HOOK_BACKUP, (Function) backup_chanfile);
875 del_hook(HOOK_REHASH, (Function) channels_rehash);
876 del_hook(HOOK_PRE_REHASH, (Function) channels_prerehash);
877 del_hook(HOOK_MINUTELY, (Function) check_expired_bans);
878 del_hook(HOOK_MINUTELY, (Function) check_expired_exempts);
879 del_hook(HOOK_MINUTELY, (Function) check_expired_invites);
880 Tcl_UntraceVar(interp, "global-chanset",
881 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
882 traced_globchanset, NULL); /* keep for backward compatibility */
883 Tcl_UntraceVar(interp, "default-chanset",
884 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
885 traced_globchanset, NULL);
886 rem_help_reference("channels.help");
887 rem_help_reference("chaninfo.help");
888 module_undepend(MODULE_NAME);
889 return NULL;
890 }
891
892 EXPORT_SCOPE char *channels_start();
893
894 static Function channels_table[] = {
895 /* 0 - 3 */
896 (Function) channels_start,
897 (Function) channels_close,
898 (Function) channels_expmem,
899 (Function) channels_report,
900 /* 4 - 7 */
901 (Function) u_setsticky_mask,
902 (Function) u_delban,
903 (Function) u_addban,
904 (Function) write_bans,
905 /* 8 - 11 */
906 (Function) get_chanrec,
907 (Function) add_chanrec,
908 (Function) del_chanrec,
909 (Function) set_handle_chaninfo,
910 /* 12 - 15 */
911 (Function) channel_malloc,
912 (Function) u_match_mask,
913 (Function) u_equals_mask,
914 (Function) clear_channel,
915 /* 16 - 19 */
916 (Function) set_handle_laston,
917 (Function) NULL, /* [17] used to be ban_time <Wcc[07/19/02]> */
918 (Function) & use_info,
919 (Function) get_handle_chaninfo,
920 /* 20 - 23 */
921 (Function) u_sticky_mask,
922 (Function) ismasked,
923 (Function) add_chanrec_by_handle,
924 (Function) NULL, /* [23] used to be isexempted() <cybah> */
925 /* 24 - 27 */
926 (Function) NULL, /* [24] used to be exempt_time <Wcc[07/19/02]> */
927 (Function) NULL, /* [25] used to be isinvited() <cybah> */
928 (Function) NULL, /* [26] used to be ban_time <Wcc[07/19/02]> */
929 (Function) NULL,
930 /* 28 - 31 */
931 (Function) NULL, /* [28] used to be u_setsticky_exempt() <cybah> */
932 (Function) u_delexempt,
933 (Function) u_addexempt,
934 (Function) NULL,
935 /* 32 - 35 */
936 (Function) NULL, /* [32] used to be u_sticky_exempt() <cybah> */
937 (Function) NULL,
938 (Function) NULL, /* [34] used to be killchanset(). */
939 (Function) u_delinvite,
940 /* 36 - 39 */
941 (Function) u_addinvite,
942 (Function) tcl_channel_add,
943 (Function) tcl_channel_modify,
944 (Function) write_exempts,
945 /* 40 - 43 */
946 (Function) write_invites,
947 (Function) ismodeline,
948 (Function) initudef,
949 (Function) ngetudef,
950 /* 44 - 47 */
951 (Function) expired_mask,
952 (Function) remove_channel,
953 (Function) & global_ban_time,
954 (Function) & global_exempt_time,
955 /* 48 - 51 */
956 (Function) & global_invite_time,
957 };
958
channels_start(Function * global_funcs)959 char *channels_start(Function *global_funcs)
960 {
961 global = global_funcs;
962
963 gfld_chan_thr = 15;
964 gfld_chan_time = 60;
965 gfld_deop_thr = 3;
966 gfld_deop_time = 10;
967 gfld_kick_thr = 3;
968 gfld_kick_time = 10;
969 gfld_join_thr = 5;
970 gfld_join_time = 60;
971 gfld_ctcp_thr = 3;
972 gfld_ctcp_time = 60;
973 gfld_nick_thr = 5;
974 gfld_nick_time = 60;
975 global_idle_kick = 0;
976 global_aop_min = 5;
977 global_aop_max = 30;
978 allow_ps = 0;
979 lastdeletedmask = 0;
980 use_info = 1;
981 strcpy(chanfile, "chanfile");
982 chan_hack = 0;
983 quiet_save = 0;
984 strcpy(glob_chanmode, "nt");
985 udef = NULL;
986 global_stopnethack_mode = 0;
987 global_revenge_mode = 0;
988 global_ban_type = 3;
989 global_ban_time = 120;
990 global_exempt_time = 60;
991 global_invite_time = 60;
992 strcpy(glob_chanset,
993 "-enforcebans "
994 "+dynamicbans "
995 "+userbans "
996 "-autoop "
997 "-bitch "
998 "+greet "
999 "+protectops "
1000 "-statuslog "
1001 "-revenge "
1002 "-secret "
1003 "-autovoice "
1004 "+cycle "
1005 "+dontkickops "
1006 "-inactive "
1007 "-protectfriends "
1008 "+shared "
1009 "-seen "
1010 "+userexempts "
1011 "+dynamicexempts "
1012 "+userinvites "
1013 "+dynamicinvites "
1014 "-revengebot "
1015 "-protecthalfops "
1016 "-autohalfop "
1017 "-nodesynch "
1018 "-static ");
1019 module_register(MODULE_NAME, channels_table, 1, 2);
1020 if (!module_depend(MODULE_NAME, "eggdrop", 108, 0)) {
1021 module_undepend(MODULE_NAME);
1022 return "This module requires Eggdrop 1.8.0 or later.";
1023 }
1024 add_hook(HOOK_MINUTELY, (Function) check_expired_bans);
1025 add_hook(HOOK_MINUTELY, (Function) check_expired_exempts);
1026 add_hook(HOOK_MINUTELY, (Function) check_expired_invites);
1027 add_hook(HOOK_USERFILE, (Function) channels_writeuserfile);
1028 add_hook(HOOK_BACKUP, (Function) backup_chanfile);
1029 add_hook(HOOK_REHASH, (Function) channels_rehash);
1030 add_hook(HOOK_PRE_REHASH, (Function) channels_prerehash);
1031 Tcl_TraceVar(interp, "global-chanset",
1032 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1033 traced_globchanset, NULL); /* keep for backward compatibility */
1034 Tcl_TraceVar(interp, "default-chanset",
1035 TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1036 traced_globchanset, NULL);
1037 add_builtins(H_chon, my_chon);
1038 add_builtins(H_dcc, C_dcc_irc);
1039 add_tcl_commands(channels_cmds);
1040 add_tcl_strings(my_tcl_strings);
1041 add_help_reference("channels.help");
1042 add_help_reference("chaninfo.help");
1043 my_tcl_ints[0].val = &share_greet;
1044 add_tcl_ints(my_tcl_ints);
1045 add_tcl_coups(mychan_tcl_coups);
1046 read_channels(0, 0);
1047 return NULL;
1048 }
1049