[BACK]Return to botnow.pl CVS log [TXT][DIR] Up to [local] / botnow

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