]> git.donarmstrong.com Git - infobot.git/blob - src/IRC/IrcHelpers.pl
- if a forked process "died", forked{}{PID} somehow became $$ - fixed.
[infobot.git] / src / IRC / IrcHelpers.pl
1 #
2 # IrcHooks.pl: IRC Hooks stuff.
3 #      Author: dms
4 #     Version: 20010413
5 #     Created: 20010413
6 #        NOTE: Based on code by Kevin Lenzo & Patrick Cole  (c) 1997
7 #
8
9 if (&IsParam("useStrict")) { use strict; }
10
11 #######################################################################
12 ####### IRC HOOK HELPERS   IRC HOOK HELPERS   IRC HOOK HELPERS ########
13 #######################################################################
14
15 #####
16 # Usage: &hookMode($nick, $modes, @targets);
17 sub hookMode {
18     my ($nick, $modes, @targets) = @_;
19     my $parity  = 0;
20
21     if ($chan =~ tr/A-Z/a-z/) {
22         &VERB("hookMode: cased $chan.",2);
23     }
24
25     my $mode;
26     foreach $mode (split(//, $modes)) {
27         # sign.
28         if ($mode =~ /[-+]/) {
29             $parity = 1         if ($mode eq "+");
30             $parity = 0         if ($mode eq "-");
31             next;
32         }
33
34         # mode with target.
35         if ($mode =~ /[bklov]/) {
36             my $target = shift @targets;
37
38             if ($parity) {
39                 $chanstats{$chan}{'Op'}++    if ($mode eq "o");
40                 $chanstats{$chan}{'Ban'}++   if ($mode eq "b");
41             } else {
42                 $chanstats{$chan}{'Deop'}++  if ($mode eq "o");
43                 $chanstats{$chan}{'Unban'}++ if ($mode eq "b");
44             }
45
46             # modes w/ target affecting nick => cache it.
47             if ($mode =~ /[bov]/) {
48                 if ($mode eq "o" and $nick eq "ChanServ" and $target =~ /^\Q$ident$\E/i) {
49                     &DEBUG("hookmode: chanserv deopped us! asking");
50                     &chanServCheck($chan);
51                 }
52
53                 $channels{$chan}{$mode}{$target}++      if  $parity;
54                 delete $channels{$chan}{$mode}{$target} if !$parity;
55             }
56
57             if ($mode =~ /[l]/) {
58                 $channels{$chan}{$mode} = $target       if  $parity;
59                 delete $channels{$chan}{$mode}          if !$parity;
60             }
61         }
62
63         # important channel modes, targetless.
64         if ($mode =~ /[mt]/) {
65             $channels{$chan}{$mode}++                   if  $parity;
66             delete $channels{$chan}{$mode}              if !$parity;
67         }
68     }
69 }
70
71 sub hookMsg {
72     ($msgType, $chan, $who, $message) = @_;
73     my $skipmessage     = 0;
74     $addressed          = 0;
75     $addressedother     = 0;
76     $orig{message}      = $message;
77     $orig{who}          = $who;
78     $addrchar           = 0;
79
80     $message    =~ s/[\cA-\c_]//ig;     # strip control characters
81     $message    =~ s/^\s+//;            # initial whitespaces.
82     $who        =~ tr/A-Z/a-z/;         # lowercase.
83
84     &showProc();
85
86     # addressing.
87     if ($msgType =~ /private/) {
88         # private messages.
89         $addressed = 1;
90     } else {
91         # public messages.
92         # addressing revamped by the xk.
93         ### below needs to be fixed...
94         if (&IsParam("addressCharacter")) {
95             if ($message =~ s/^\Q$param{'addressCharacter'}\E//) {
96                 $addrchar  = 1;
97                 $addressed = 1;
98             }
99         }
100
101         if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
102             my $newmessage = $';
103             if ($1 =~ /^\Q$ident\E$/i) {
104                 $message   = $newmessage;
105                 $addressed = 1;
106             } else {
107                 # ignore messages addressed to other people or unaddressed.
108                 $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
109             }
110         }
111     }
112
113     # Determine floodwho.
114     my $c       = "_default";
115     if ($msgType =~ /public/i) {                # public.
116         $floodwho = $c = lc $chan;
117     } elsif ($msgType =~ /private/i) {  # private.
118         $floodwho = lc $who;
119     } else {                            # dcc?
120         &DEBUG("FIXME: floodwho = ???");
121     }
122
123     my $val = &getChanConfDefault("floodRepeat", "2:10", $c);
124     my ($count, $interval) = split /:/, $val;
125
126     # flood repeat protection.
127     if ($addressed) {
128         my $time = $flood{$floodwho}{$message} || 0;
129
130         if ($msgType eq "public" and (time() - $time < $interval)) {
131             ### public != personal who so the below is kind of pointless.
132             my @who;
133             foreach (keys %flood) {
134                 next if (/^\Q$floodwho\E$/);
135                 next if (defined $chan and /^\Q$chan\E$/);
136
137                 push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
138             }
139
140             return if ($lobotomized);
141
142             if (scalar @who) {
143                 &msg($who, "you already said what ".
144                                 join(' ', @who)." have said.");
145             } else {
146                 &msg($who,"Someone already said that ". (time - $time) ." seconds ago" );
147             }
148
149             ### TODO: delete old floodwarn{} keys.
150             my $floodwarn = 0;
151             if (!exists $floodwarn{$floodwho}) {
152                 $floodwarn++;
153             } else {
154                 $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
155             }
156
157             if ($floodwarn) {
158                 &status("FLOOD repetition detected from $floodwho.");
159                 $floodwarn{$floodwho} = time();
160             }
161
162             return;
163         }
164
165         if ($addrchar) {
166             &status("$b_cyan$who$ob is short-addressing me");
167         } elsif ($msgType eq "private") {       # private.
168             &status("$b_cyan$who$ob is /msg'ing me");
169         } else {                                # public?
170             &status("$b_cyan$who$ob is addressing me");
171         }
172
173         $flood{$floodwho}{$message} = time();
174     } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat")) {
175         # unaddressed, public only.
176
177         ### TODO: use a separate "short-time" hash.
178         my @data;
179         @data   = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
180     }
181
182     $val = &getChanConfDefault("floodMessages", "5:30", $c);
183     ($count, $interval) = split /:/, $val;
184
185     # flood overflow protection.
186     if ($addressed) {
187         foreach (keys %{ $flood{$floodwho} }) {
188             next unless (time() - $flood{$floodwho}{$_} > $interval);
189             delete $flood{$floodwho}{$_};
190         }
191
192         my $i = scalar keys %{ $flood{$floodwho} };
193         if ($i > $count) {
194             &msg($who,"overflow of messages ($i > $count)");
195             &status("FLOOD overflow detected from $floodwho; ignoring");
196
197             my $expire = $param{'ignoreAutoExpire'} || 5;
198             &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
199             return;
200         }
201
202         $flood{$floodwho}{$message} = time();
203     }
204
205     my @ignore;
206     if ($msgType =~ /public/i) {                    # public.
207         $talkchannel    = $chan;
208         &status("<$orig{who}/$chan> $orig{message}");
209         push(@ignore, keys %{ $ignore{$chan} }) if (exists $ignore{$chan});
210     } elsif ($msgType =~ /private/i) {             # private.
211         &status("[$orig{who}] $orig{message}");
212         $talkchannel    = undef;
213         $chan           = "_default";
214     } else {
215         &DEBUG("unknown msgType => $msgType.");
216     }
217     push(@ignore, keys %{ $ignore{"*"} }) if (exists $ignore{"*"});
218
219     if ((!$skipmessage or &IsChanConf("seenStoreAll")) and
220         &IsChanConf("seen") and
221         $msgType =~ /public/
222     ) {
223         $seencache{$who}{'time'} = time();
224         $seencache{$who}{'nick'} = $orig{who};
225         $seencache{$who}{'host'} = $uh;
226         $seencache{$who}{'chan'} = $talkchannel;
227         $seencache{$who}{'msg'}  = $orig{message};
228         $seencache{$who}{'msgcount'}++;
229     }
230
231     return if ($skipmessage);
232     return unless (&IsParam("minVolunteerLength") or $addressed);
233
234     foreach (@ignore) {
235         s/\*/\\S*/g;
236
237         next unless (eval { $nuh =~ /^$_$/i });
238
239         &status("IGNORE <$who> $message");
240         return;
241     }
242
243     if (defined $nuh) {
244         if (!defined $userHandle) {
245             &DEBUG("line 1074: need verifyUser?");
246             &verifyUser($who, $nuh);
247         }
248     } else {
249         &DEBUG("hookMsg: 'nuh' not defined?");
250     }
251
252 ### For extra debugging purposes...
253     if ($_ = &process()) {
254 #       &DEBUG("IrcHooks: process returned '$_'.");
255     }
256
257     return;
258 }
259
260 sub chanLimitVerify {
261     my($chan)   = @_;
262     my $l       = $channels{$chan}{'l'};
263
264     if (scalar keys %netsplitservers) {
265         &WARN("clV: netsplit active (1); skipping. (netsplit => $netsplit)");
266         return;
267     }
268
269     # only change it if it's not set.
270     if (defined $l and &IsChanConf("chanlimitcheck")) {
271         my $plus  = &getChanConfDefault("chanlimitcheckPlus", 5, $chan);
272         my $count = scalar(keys %{ $channels{$chan}{''} });
273         my $int   = &getChanConfDefault("chanlimitcheckInterval", 10, $chan);
274
275         my $delta = $count + $plus - $l;
276         $delta    =~ s/^\-//;
277
278         if ($plus <= 3) {
279             &WARN("clc: stupid to have plus at $plus, fix it!");
280         }
281
282         if (exists $cache{chanlimitChange}{$chan}) {
283             if (time() - $cache{chanlimitChange}{$chan} < $int*60) {
284                 return;
285             }
286         }
287
288         &chanServCheck($chan);
289
290         ### todo: unify code with chanlimitcheck()
291         if ($delta > 5) {
292             &status("clc: big change in limit; changing.");
293             &rawout("MODE $chan +l ".($count+$plus) );
294             $cache{chanlimitChange}{$chan} = time();
295         }
296     }
297 }
298
299 sub chanServCheck {
300     ($chan) = @_;
301
302     if (!defined $chan or $chan =~ /^$/) {
303         &WARN("chanServCheck: chan == NULL.");
304         return 0;
305     }
306
307     if ($chan =~ tr/A-Z/a-z/) {
308         &DEBUG("chanServCheck: lowercased chan ($chan)");
309     }
310
311     if (! &IsChanConf("chanServ_ops") ) {
312         return 0;
313     }
314
315     &DEBUG("chanServCheck($chan) called.");
316
317     if ( &IsParam("nickServ_pass") and !$nickserv) {
318         &DEBUG("chanServ_ops($chan): nickserv enabled but not alive? (ircCheck)");
319         return 0;
320     }
321     return 0 if (exists $channels{$chan}{'o'}{$ident});
322
323     &status("ChanServ ==> Requesting ops for $chan. (chanServCheck)");
324     &rawout("PRIVMSG ChanServ :OP $chan $ident");
325     return 1;
326 }
327
328 1;