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