Annotation of botnow/botnow.pl, Revision 1.2
1.1 bountyht 1: #!/usr/bin/perl
2:
3: use strict;
4: no strict 'refs';
5: use warnings;
6: use IO::Socket;
7: use IO::Select;
8: use OpenBSD::Pledge;
9: use OpenBSD::Unveil;
10:
11: my $confpath = "botnow.conf";
12: our %conf;
13: foreach my $line (readarray($confpath)) {
14: if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace
15: next;
16: } elsif ($line =~ /^([-_a-zA-Z0-9]+)\s*=\s*([[:print:]]+)$/) {
17: $conf{$1} = $2;
18: } else {
19: die "ERROR: botnow.conf format invalid: $line";
20: }
21: }
22:
23: # Name of local network
24: $conf{localnet} = $conf{localnet} || "ircnow";
25:
26: # Internal IPv4 address and plaintext port
27: $conf{host} = $conf{host} || "127.0.0.1";
28: $conf{port} = $conf{port} || 1337;
29:
30: # Bouncer hostname
31: chomp($conf{hostname} = $conf{hostname} || `hostname`);
32:
33: # External IPv4 address, plaintext and ssl port
34: $conf{ip4} = $conf{ip4} or die "ERROR: botnow.conf: ip4";
1.2 ! bountyht 35: $conf{ip6} = $conf{ip6} or die "ERROR: botnow.conf: ip6";
! 36: $conf{ip6subnet} = $conf{ip6subnet} or die "ERROR: botnow.conf: ip6subnet";
1.1 bountyht 37: $conf{plainport} = $conf{plainport} || 1337;
38: $conf{sslport} = $conf{sslport} || 31337;
39:
40: # Nick and password of bot -- Make sure to add to oper block
41: $conf{nick} = $conf{nick} || "botnow";
42: $conf{pass} = $conf{pass} or die "ERROR: botnow.conf: pass";
43:
44: # Comma-separated list of channels for requesting bouncers
45: $conf{chans} = $conf{chans} || "#ircnow";
46:
47: #Join chans on localnet?
48: $conf{localchans} = defined($conf{localchans}) && ($conf{localchans} =~ /^true/i);
49:
50: # Number of words in password
51: $conf{passlength} = $conf{passlength} || 3;
52:
53: # Mail from address
54: if (!defined($conf{mailname})) {
55: if ($conf{mailfrom} =~ /^([^@]+)@/) {
56: $conf{mailname} = $1 or die "ERROR: botnow.conf mailname";
57: }
58: }
59:
60: # rDNS keys from Stallion in BuyVM
61: $conf{key} = $conf{key} or die "ERROR: botnow.conf: key";
62: $conf{hash} = $conf{hash} or die "ERROR: botnow.conf: hash";
63:
64: # ZNC install directory
65: $conf{zncdir} = $conf{zncdir} || "/home/znc/home/znc";
66:
67: # NSD zone dir
68: $conf{zonedir} = $conf{zonedir} || "/var/nsd/zones/master/";
69:
70: # Network Interface Config File
71: $conf{hostnameif} = $conf{hostnameif} || "/etc/hostname.vio0";
72:
73: # Verbosity: 0 (no errors), 1 (errors), 2 (warnings), 3 (diagnostic)
74: use constant {
75: NONE => 0,
76: ERRORS => 1,
77: WARNINGS => 2,
78: ALL => 3,
79: };
80: $conf{verbose} = $conf{verbose} || ERRORS;
81:
82: # Terms of Service; don't edit lines with the word EOF
83: $conf{terms} = $conf{terms} || "IRCNow: Of the User, By the User, For the User. Rules: no profanity, no porn, no illegal drugs, no gambling, no slander, no warez, no promoting violence, no spam, illegal cracking, or DDoS. Only one account per person. Don't share passwords. Full terms: https://ircnow.org/terms.php";
84:
85: $conf{ipv6path} = "ipv6s"; # ipv6 file path
86: $conf{netpath} = "networks"; # networks file path
87: $conf{expires} = $conf{expires} || 1800; # time before captcha expires
88:
89: if(defined($conf{die})) { die $conf{die}; }
90:
91: my @modules;
92: if (defined($conf{modules})) {
93: @modules = split(/\s+/, $conf{modules});
94: }
95: use lib './';
96: foreach my $mod (@modules) {
97: require "$mod.pm";
98: }
99: foreach my $mod (@modules) {
100: my $init = "${mod}::init";
101: $init->();
102: }
103:
104: our @networks;
105: my @bots;
106: my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
107: my @days = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
108: my @chans = split /[,\s]+/m, $conf{chans};
109: my @teamchans;
110: if (defined($conf{teamchans})) { @teamchans = split /[,\s]+/m, $conf{teamchans}; }
111: my $call;
112: my $botnick = $conf{nick};
113: my $host = $conf{host};
114: my $port = $conf{port};
115: my $pass = $conf{pass};
116: my $localnet = $conf{localnet};
117: my $staff = $conf{staff};
118: my @stafflist = split(/ /,$staff);
119: my $verbose = $conf{verbose};
120: my $ipv6path = $conf{ipv6path};
121: my $netpath = $conf{netpath};
122: my $expires = $conf{expires};
123: my $localchans = $conf{localchans};
124:
125: unveil("./", "r") or die "Unable to unveil $!";
126: unveil("$confpath", "r") or die "Unable to unveil $!";
127: unveil("$netpath", "r") or die "Unable to unveil $!";
128: unveil("$ipv6path", "rwc") or die "Unable to unveil $!";
129: unveil() or die "Unable to lock unveil $!";
130:
131: #dns and inet for sockets, proc and exec for figlet
132: #rpath for reading file, wpath for writing file, cpath for creating path
133: #flock, fattr for sqlite
134: pledge( qw(stdio rpath wpath cpath inet dns proc exec flock fattr) ) or die "Unable to pledge: $!";
135:
136: # Read from filename and return array of lines without trailing newlines
137: sub readarray {
138: my ($filename) = @_;
139: open(my $fh, '<', $filename) or die "Could not read file '$filename' $!";
140: chomp(my @lines = <$fh>);
141: close $fh;
142: return @lines;
143: }
144:
145: # Read from filename and return as string
146: sub readstr {
147: my ($filename) = @_;
148: open my $fh, '<', $filename or die "Could not read file '$filename' $!";
149: my $str = do { local $/; <$fh> };
150: close $fh;
151: return $str;
152: }
153:
154: # Write str to filename
155: sub writefile {
156: my ($filename, $str) = @_;
157: open(my $fh, '>', "$filename") or die "Could not write to $filename";
158: print $fh $str;
159: close $fh;
160: }
161:
162: # Append str to filename
163: sub appendfile {
164: my ($filename, $str) = @_;
165: open(my $fh, '>>', "$filename") or die "Could not append to $filename";
166: print $fh $str;
167: close $fh;
168: }
169:
170: # Return list of networks from filename
171: # To add multiple servers for a single network, simply create a new entry with
172: # the same net name; znc ignores addnetwork commands when a network already exists
173: sub readnetworks {
174: my ($filename) = @_;
175: my @lines = readarray($filename);
176: my @networks;
177: foreach my $line (@lines) {
178: if ($line =~ /^#/ or $line =~ /^\s*$/) { # skip comments and whitespace
179: next;
180: } elsif ($line =~ /^\s*([-a-zA-Z0-9]+)\s*([-_.:a-zA-Z0-9]+)\s*(~|\+)?([0-9]+)\s*$/) {
181: my ($name, $server, $port) = ($1, $2, $4);
182: my $trustcerts;
183: if (!defined($3)) {
184: $trustcerts = 0;
185: } elsif ($3 eq "~") { # Use SSL but trust all certs
186: $port = "+".$port;
187: $trustcerts = 1;
188: } else { # Use SSL and verify certs
189: $port = "+".$port;
190: $trustcerts = 0;
191: }
192: push(@networks, {"name" => $name, "server" => $server, "port" => $port, "trustcerts" => $trustcerts });
193: } else {
194: die "network format invalid: $line\n";
195: }
196: }
197: return @networks;
198: }
199:
200: @networks = readnetworks($netpath);
201:
202: # networks must be sorted to avoid multiple connections
203: @networks = sort @networks;
204:
205: # create sockets
206: my $sel = IO::Select->new( );
207: my $lastnet = "";
208: foreach my $network (@networks) {
209: # avoid duplicate connections
210: if ($lastnet eq $network->{name}) { next; }
211: $lastnet = $network->{name};
212: my $socket = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port, Proto=>'tcp', Timeout=>'300') || print "Failed to establish connection\n";
213: $sel->add($socket);
214: my $bot = {("sock" => $socket), %$network};
215: push(@bots, $bot);
216: putserv($bot, "NICK $botnick");
217: putserv($bot, "USER $botnick * * :$botnick");
218: }
219:
220: while(my @ready = $sel->can_read) {
221: my ($bot, $response);
222: my ($sender, $val);
223: foreach my $socket (@ready) {
224: foreach my $b (@bots) {
225: if($socket == $b->{sock}) {
226: $bot = $b;
227: last;
228: }
229: }
230: if (!defined($response = <$socket>)) {
231: debug(ERRORS, "ERROR ".$bot->{name}." has no response:");
232: next;
233: }
234: if ($response =~ /^PING :(.*)\r\n$/i) {
235: putserv($bot, "PONG :$1");
236: } elsif ($response =~ /^:irc.znc.in (.*) (.*) :(.*)\r\n$/) {
237: my ($type, $target, $text) = ($1, $2, $3);
238: if ($type eq "001" && $target =~ /^$botnick.?$/ && $text eq "Welcome to ZNC") {
239: } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** To connect now, you can use /quote PASS <username>:<password>, or /quote PASS <username>/<network>:<password> to connect to a specific network.") {
240: } elsif ($type eq "NOTICE" && $target =~ /^$botnick.?$/ && $text eq "*** You need to send your password. Configure your client to send a server password.") {
241: } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Password required") {
242: putserv($bot, "PASS $botnick/$bot->{name}:$pass");
243: if ($bot->{name} =~ /^$localnet$/i) {
244: putserv($bot, "OPER $botnick $pass");
245: putserv($bot, "PRIVMSG *status :LoadMod --type=user controlpanel");
246: putserv($bot, "PRIVMSG *controlpanel :get Admin $botnick");
247: putserv($bot, "PRIVMSG *controlpanel :get Nick cloneuser");
248: foreach my $chan (@teamchans) {
249: putserv($bot, "JOIN $chan");
250: }
251: }
252: if ($bot->{name} !~ /^$localnet$/i or $localchans) {
253: foreach my $chan (@chans) {
254: putserv($bot, "JOIN $chan");
255: }
256: }
257: } elsif ($type eq "464" && $target =~ /^$botnick.?$/ && $text eq "Invalid Password") {
258: die "ERROR: Wrong Username/Password: $bot->{name}";
259: } else {
260: debug(ERRORS, "Unexpected bncnow.pl 257: type: $type, target: $target, text: $text");
261: }
262: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PRIVMSG ([^ ]+) :(.*)\r\n$/i) {
263: my ($hostmask, $nick, $host, $target, $text) = ($1, $2, $3, $4, $5);
264: if ($hostmask eq '*status!znc@znc.in' && $target =~ /^$botnick.?$/) {
265: if ($text =~ /Network ([[:ascii:]]+) doesn't exist./) {
266: debug(ERRORS, "nonexistent: $1");
267: } elsif ($text eq "You are currently disconnected from IRC. Use 'connect' to reconnect.") {
268: debug(ERRORS, "disconnected: $bot->{name}");
269: } elsif ($text =~ /Unable to load module (.*): Module (.*) already loaded./) {
270: debug(ALL, "Module $1 already loaded\n");
271: } elsif ($text =~ /^Disconnected from IRC.*$/) {
272: debug(ERRORS, "$bot->{name}: $text");
273: } elsif ($text =~ /^|/) {
274: debug(ERRORS, "$bot->{name}: $text");
275: } else {
276: debug(ERRORS, "Unexpected bncnow.pl 273: $response");
277: }
278: } elsif ($text =~ /^!([[:graph:]]+)\s*(.*)/) {
279: my ($cmd, $text) = ($1, $2);
280: my $hand = $staff; # TODO fix later
281: if ($target =~ /^#/) {
282: foreach my $c (@{$call->{pub}}) {
283: if ($cmd eq $c->{cmd}) {
284: my $proc = $c->{proc};
285: $proc->($bot, $nick, $host, $hand, $target, $text);
286: }
287: }
288: } else {
289: foreach my $c (@{$call->{msg}}) {
290: if ($cmd eq $c->{cmd}) {
291: my $proc = $c->{proc};
292: $proc->($bot, $nick, $host, $hand, $text);
293: }
294: }
295: }
296: } else {
297: my $hand = $staff; # TODO fix later
298: if ($target =~ /^#/) {
299: foreach my $c (@{$call->{pubm}}) {
300: my $proc = $c->{proc};
301: $proc->($bot, $nick, $host, $hand, $target, $text);
302: }
303: } else {
304: foreach my $c (@{$call->{msgm}}) {
305: my $proc = $c->{proc};
306: $proc->($bot, $nick, $host, $hand, $text);
307: }
308: }
309: }
310: debug(ALL, "$hostmask $target $text");
311: } elsif($response =~ /^:([^ ]+) NOTICE ([^ ]+) :(.*)\r\n$/i) {
312: my ($hostmask, $target, $text) = ($1, $2, $3);
313: if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) {
314: my ($nick, $host) = ($1, $2);
315: my $hand = $staff; # TODO fix later
316: foreach my $c (@{$call->{notc}}) {
317: # if ($text eq $c->{mask}) { # TODO fix later
318: my $proc = $c->{proc};
319: $proc->($bot, $nick, $host, $hand, $text, $target);
320: # }
321: }
322: # TODO use CTCR
323: # CTCP replies
324: if ($hostmask ne '*status!znc@znc.in') {
325: if ($text =~ /^&(PING|VERSION|TIME|USERINFO) (.*)&$/i) {
326: my ($key, $val) = ($1, $2);
327: my $id = SQLite::id("irc", "nick", $nick, $expires);
328: SQLite::set("irc", "id", $id, "ctcp".lc($key), $val);
329: SQLite::set("irc", "id", $id, "localtime", time());
330: }
331: }
332: }
333: debug(ALL, "$hostmask NOTICE $target $text");
334: #:portlane.se.quakenet.org NOTICE guava :Highest connection count: 1541 (1540 clients)
335: #:portlane.se.quakenet.org NOTICE guava :on 2 ca 2(4) ft 20(20) tr
336: } elsif($response =~ /^:([^ ]+) MODE ([^ ]+) ([^ ]+)\s*(.*)\r\n$/i) {
337: my ($hostmask, $chan, $change, $targets) = ($1, $2, $3, $4);
338: if ($hostmask =~ /([^!]+)!([^@]+@[^@ ]+)/) {
339: my ($nick, $host) = ($1, $2);
340: my $hand = $staff; # TODO fix later
341: foreach my $c (@{$call->{mode}}) {
342: # TODO filter by mask
343: my $proc = $c->{proc};
344: $proc->($bot, $nick, $host, $hand, $chan, $change, $targets);
345: }
346: }
347: debug(ALL, "$hostmask MODE $chan $change $targets");
348: #:guava!guava@guava.guava.ircnow.org MODE guava :+Ci
349: #:ChanServ!services@services.irc.ircnow.org MODE #testing +q jrmu
350: #:jrmu!jrmu@jrmu.staff.ircnow.org MODE #testing +o jrmu
351: #Unexpected bncnow.pl 460: :irc.guava.ircnow.org MODE guava :+o
352: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) JOIN :?(.*)\r\n$/i) {
353: my ($hostmask, $nick, $host, $chan) = ($1, $2, $3, $4);
354: my $hand = $staff; # TODO fix later
355: foreach my $c (@{$call->{join}}) {
356: my $proc = $c->{proc};
357: $proc->($bot, $nick, $host, $hand, $chan);
358: }
359: debug(ALL, "$hostmask JOIN $chan");
360: #:jrmu!jrmu@jrmu.staff.ircnow.org JOIN :#testing
361: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) PART ([^ ]+) :(.*)\r\n$/i) {
362: my ($hostmask, $nick, $host, $chan, $text) = ($1, $2, $3, $4, $5);
363: my $hand = $staff; # TODO fix later
364: foreach my $c (@{$call->{part}}) {
365: # if ($text eq $c->{mask}) { # TODO fix later
366: my $proc = $c->{proc};
367: $proc->($bot, $nick, $host, $hand, $chan, $text);
368: # }
369: }
370: debug(ALL, "$hostmask PART $chan :$text");
371: #:jrmu!jrmu@jrmu.staff.ircnow.org PART #testing :
372: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) KICK (#[^ ]+) ([^ ]+) :(.*)\r\n$/i) {
373: my ($hostmask, $nick, $host, $chan, $kicked, $text) = ($1, $2, $3, $4, $5, $6);
374: my $hand = $staff; # TODO fix later
375: foreach my $c (@{$call->{kick}}) {
376: # if ($text eq $c->{mask}) { # TODO fix later
377: my $proc = $c->{proc};
378: $proc->($bot, $nick, $host, $hand, $chan, $text);
379: # }
380: }
381: debug(ALL, "$hostmask KICK $chan $kicked :$text");
382: #jrmu!jrmu@jrmu.users.undernet.org KICK #ircnow guava :this is a test
383: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) NICK :?(.*)\r\n$/i) {
384: my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4);
385: debug(ALL, "$hostmask NICK $text");
386: #:Fly0nDaWaLL|dal!psybnc@do.not.h4ck.me NICK :nec|dal
387: } elsif($response =~ /^:(([^!]+)!([^@]+@[^@ ]+)) QUIT :(.*)\r\n$/i) {
388: my ($hostmask, $nick, $host, $text) = ($1, $2, $3, $4);
389: debug(ALL, "$hostmask QUIT :$text");
390: #:Testah!~sid268081@aa38a510 QUIT :Client closed connection
391: } elsif($response =~ /^NOTICE AUTH :(.*)\r\n$/i) {
392: my ($text) = ($1);
393: debug(ALL, "NOTICE AUTH: $text");
394: #NOTICE AUTH :*** Looking up your hostname
395: #NOTICE AUTH: *** Looking up your hostname
396: #NOTICE AUTH: *** Checking Ident
397: #NOTICE AUTH: *** Got ident response
398: #NOTICE AUTH: *** Found your hostname
399: } elsif ($response =~ /^:([[:graph:]]+) (\d\d\d) $botnick.? :?(.*)\r?\n?\r$/i) {
400: my ($server, $code, $text) = ($1, $2, $3);
401: if ($code =~ /^001$/) { # Server Info
402: debug(ERRORS, "connected: $bot->{name}");
403: } elsif ($code =~ /^0\d\d$/) { # Server Info
404: debug(ALL, "$server $code $text");
405: } elsif ($code =~ /^2\d\d$/) { # Server Stats
406: debug(ALL, "$server $code $text");
407: } elsif ($code == 301 && $text =~ /^([-_\|`a-zA-Z0-9]+) :([[:graph:]]+)/) {
408: debug(ALL, "$text");
409: } elsif ($code == 307 && $text =~ /^([-_\|`a-zA-Z0-9]+) (.*)/) {
410: my ($sender, $key) = ($1, "registered");
411: $val = $2 eq ":is a registered nick" ? "True" : "$2";
412: my $id = SQLite::id("irc", "nick", $sender, $expires);
413: SQLite::set("irc", "id", $id, "identified", $val);
414: debug(ALL, "$key: $val");
415: } elsif ($code == 311 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+)\s+([^:]+) \* :([^:]*)/) {
416: my ($sender, $key, $val) = ($1, "hostmask", "$1\!$2\@$3");
417: my $id = SQLite::id("irc", "nick", $sender, $expires);
418: SQLite::set("irc", "id", $id, $key, $val);
419: debug(ALL, "$key: $val");
420: } elsif ($code == 312 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([^:]+) :([^:]+)/) {
421: my ($sender, $key, $val) = ($1, "server", $2);
422: my $id = SQLite::id("irc", "nick", $sender, $expires);
423: SQLite::set("irc", "id", $id, $key, $val);
424: debug(ALL, "$key: $val");
425: } elsif ($code == 313 && $text =~ /^([-_\|`a-zA-Z0-9]+) :?(.*)/) {
426: my ($sender, $key, $val) = ($1, "oper", ($2 eq "is an IRC operator" ? "True" : "$2"));
427: my $id = SQLite::id("irc", "nick", $sender, $expires);
428: SQLite::set("irc", "id", $id, $key, $val);
429: debug(ALL, "$key: $val");
430: } elsif ($code == 315 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHO(IS)? list/) {
431: debug(ALL, "End of WHOIS");
432: } elsif ($code == 317 && $text =~ /^([-_\|`a-zA-Z0-9]+) (\d+) (\d+) :?(.*)/) {
433: ($sender, my $idle, my $epochtime) = ($1, $2, $3);
434: my $id = SQLite::id("irc", "nick", $sender, $expires);
435: SQLite::set("irc", "id", $id, "idle", $idle);
436: # SQLite::set("irc", "id", $id, "epochtime", time());
437: debug(ALL, "idle: $idle, epochtime: $epochtime");
438: } elsif ($code == 318 && $text =~ /^([-_\|`a-zA-Z0-9]+) :End of \/?WHOIS list/) {
439: debug(ALL, "End of WHOIS");
440: } elsif ($code == 319 && $text =~ /^([-_\|`a-zA-Z0-9]+) :(.*)/) {
441: my ($sender, $key, $val) = ($1, "chans", $2);
442: my $id = SQLite::id("irc", "nick", $sender, $expires);
443: SQLite::set("irc", "id", $id, $key, $val);
444: debug(ALL, "$key: $val");
445: } elsif ($code == 330 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([-_\|`a-zA-Z0-9]+) :?(.*)/) {
446: my ($sender, $key, $val) = ($1, "identified", ($3 eq "is logged in as" ? "True" : $2));
447: my $id = SQLite::id("irc", "nick", $sender, $expires);
448: SQLite::set("irc", "id", $id, $key, $val);
449: debug(ALL, "$key: $val");
450: } elsif ($code == 338 && $text =~ /^([-_\|`a-zA-Z0-9]+) ([0-9a-fA-F:.]+) :actually using host/) {
451: my ($sender, $key, $val) = ($1, "ip", $2);
452: my $id = SQLite::id("irc", "nick", $sender, $expires);
453: SQLite::set("irc", "id", $id, $key, $val);
454: debug(ALL, "$key: $val");
455: #Unexpected: efnet.port80.se 338 jrmu 206.253.167.44 :actually using host
456: } elsif ($code == 378 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is connecting from ([^ ]+)\s*([0-9a-fA-F:.]+)?/) {
457: my ($sender, $key, $val) = ($1, "ip", $3);
458: my $id = SQLite::id("irc", "nick", $sender, $expires);
459: SQLite::set("irc", "id", $id, $key, $val);
460: debug(ALL, "$key: $val");
461: } elsif ($code == 671 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is using a secure connection/) {
462: my ($sender, $key, $val) = ($1, "ssl", "True");
463: my $id = SQLite::id("irc", "nick", $sender, $expires);
464: SQLite::set("irc", "id", $id, $key, $val);
465: debug(ALL, "$key: $val");
466: } elsif ($code =~ /^332$/) { # Topic
467: # print "$text\r\n";
468: } elsif ($code =~ /^333$/) { #
469: # print "$server $text\r\n";
470: #karatkievich.freenode.net 333 #ircnow jrmu!znc@206.253.167.44 1579277253
471: } elsif ($code =~ /^352$/) { # Hostmask
472: #:datapacket.hk.quakenet.org 352 * znc guava.guava.ircnow.org *.quakenet.org guava H :0 guava
473: # print "$server $code $text\r\n";
474: } elsif ($code =~ /^353$/) { # Names
475: # print "$server $code $text\r\n";
476: } elsif ($code =~ /^366$/) { # End of names
477: # print "$server $code $text\r\n";
478: } elsif ($code =~ /^37\d$/) { # MOTD
479: # print "$server $code $text\r\n";
480: } elsif ($code =~ /^381$/) { # IRC Operator Verified
481: # print "IRC Oper Verified\r\n";
482: } elsif ($code =~ /^401$/) { # IRC Operator Verified
483: # print "IRC Oper Verified\r\n";
484: } elsif ($code =~ /^403$/) { # No such channel
485: # debug(ERRORS, "$text");
486: } elsif ($code =~ /^422$/) { # MOTD missing
487: # print "$server $code $text\r\n";
488: } elsif ($code =~ /^396$/) { # Display hostname
489: # print "$server $code $text\r\n";
490: #Unexpected bncnow.pl 454: irc.guava.ircnow.org 396 guava.guava.ircnow.org :is your displayed hostname now
491: } elsif ($code =~ /^464$/) { # Invalid password for oper
492: foreach my $chan (@teamchans) {
493: putserv($bot, "PRIVMSG $chan :$botnick oper password failed; the bot will be unable to view uncloaked IP addresses");
494: }
495: } elsif ($code =~ /^477$/) { # Can't join channel
496: foreach my $chan (@teamchans) {
497: putserv($bot, "PRIVMSG $chan :ERROR: $botnick on $server: $text");
498: }
499: } elsif ($code == 716 && $text =~ /^([-_\|`a-zA-Z0-9]+) :is in \+g mode \(server-side ignore.\)/) {
500: debug(ALL, "$text");
501: } else {
502: debug(ERRORS, "Unexpected bncnow.pl 454: $server $code $text");
503: }
504: } else {
505: debug(ERRORS, "Unexpected bncnow.pl 460: $response");
506: }
507: }
508: }
509:
510: sub putserv {
511: my( $bot, $text )=@_;
512: my $socket = $bot->{sock};
513: if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) {
514: my ($cmd, $line) = ($1, $2);
515: my @lines = split /\r?\n/m, $line;
516: foreach my $l (@lines) {
517: print $socket "$cmd:$l\r\n";
518: }
519: } else {
520: print $socket "$text\r\n";
521: }
522: }
523:
524: sub putserv {
525: my( $bot, $text )=@_;
526: my $socket = $bot->{sock};
527: if ($text =~ /^([^:]+):([[:ascii:]]*)$/m) {
528: my ($cmd, $line) = ($1, $2);
529: my @lines = split /\r?\n/m, $line;
530: foreach my $l (@lines) {
531: print $socket "$cmd:$l\r\n";
532: }
533: } else {
534: print $socket "$text\r\n";
535: }
536: }
537:
538: sub putservlocalnet {
539: my( $bot, $text )=@_;
540: my $botlocalnet;
541: foreach my $b (@bots) {
542: if($b->{name} =~ /^$localnet$/i) {
543: $botlocalnet = $b;
544: last;
545: }
546: }
547: putserv($botlocalnet, $text);
548: }
549:
550:
551: sub date {
552: my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
553: my $localtime = sprintf("%04d%02d%02d", $year+1900, $mon+1, $mday);
554: return $localtime;
555: }
556: sub gettime {
557: my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
558: my $localtime = sprintf("%s %s %d %02d:%02d:%02d", $days[$wday], $months[$mon], $mday, $hour, $min, $sec);
559: return $localtime;
560: }
561:
562: sub whois {
563: my( $socket, $target )=@_;
564: print $socket "WHOIS $target $target\r\n";
565: }
566:
567: sub ctcp {
568: my( $socket, $target )=@_;
569: # print $socket "PRIVMSG $target :".chr(01)."CLIENTINFO".chr(01)."\r\n";
570: # print $socket "PRIVMSG $target :".chr(01)."FINGER".chr(01)."\r\n";
571: # print $socket "PRIVMSG $target :".chr(01)."SOURCE".chr(01)."\r\n";
572: print $socket "PRIVMSG $target :".chr(01)."TIME".chr(01)."\r\n";
573: # print $socket "PRIVMSG $target :".chr(01)."USERINFO".chr(01)."\r\n";
574: print $socket "PRIVMSG $target :".chr(01)."VERSION".chr(01)."\r\n";
575: # print $socket "PRIVMSG $target :".chr(01)."PING".chr(01)."\r\n";
576: }
577:
578: sub cbind {
579: my ($type, $flags, $cmd, $proc) = @_;
580: if ($type eq "pub") {
581: push(@{$call->{pub}}, {cmd => $cmd, proc => $proc});
582: } elsif ($type eq "msg") {
583: push(@{$call->{msg}}, {cmd => $cmd, proc => $proc});
584: } elsif ($type eq "notc") {
585: push(@{$call->{notc}}, {mask => $cmd, proc => $proc});
586: } elsif ($type eq "mode") {
587: push(@{$call->{mode}}, {mask => $cmd, proc => $proc});
588: } elsif ($type eq "join") {
589: push(@{$call->{join}}, {mask => $cmd, proc => $proc});
590: } elsif ($type eq "partcall") {
591: push(@{$call->{part}}, {mask => $cmd, proc => $proc});
592: } elsif ($type eq "pubm") {
593: push(@{$call->{pubm}}, {mask => $cmd, proc => $proc});
594: } elsif ($type eq "msgm") {
595: push(@{$call->{msgm}}, {mask => $cmd, proc => $proc});
596: }
597: }
598:
599: sub debug {
600: my ($level, $msg) = @_;
601: if ($verbose >= $level) { print "$msg\n"; }
602: }
603:
604: sub isstaff {
605: my( $bot, $nick ) = @_;
606: if( !( $bot->{name} =~ /^$localnet$/i ) )
607: {
608: return 0;
609: }
610: my $lnick = lc $nick;
611: foreach( @stafflist ) {
612: if( $lnick eq $_ ) {
613: return 1;
614: }
615: }
616: return 0;
617: }
CVSweb