]> git.donarmstrong.com Git - infobot.git/blobdiff - src/core.pl
* Add vim formatting comments ( # vim:ts=4:sw=4:expandtab:tw=80 )
[infobot.git] / src / core.pl
index aec5affb884601762386f3c726e5851d7e5c9a92..936f31cc784ec8aab6fb43951b23f72a67c7cdac 100644 (file)
@@ -7,30 +7,35 @@
 
 use strict;
 
-# dynamic scalar. MUST BE REDUCED IN SIZE!!!
+# scalar. MUST BE REDUCED IN SIZE!!!
 ### TODO: reorder.
 use vars qw(
-       $answer $correction_plausible $talkchannel
+       $bot_misc_dir $bot_pid $bot_base_dir $bot_src_dir
+       $bot_data_dir $bot_config_dir $bot_state_dir $bot_run_dir
+       $answer $correction_plausible $talkchannel $bot_release
        $statcount $memusage $user $memusageOld $bot_version $dbh
-       $shm $host $msg $bot_misc_dir $bot_pid $bot_base_dir $noreply
-       $bot_src_dir $conn $irc $learnok $nick $ident $no_syscall
+       $shm $host $msg $noreply $conn $irc $learnok $nick $ident
        $force_public_reply $addrchar $userHandle $addressedother
        $floodwho $chan $msgtime $server $firsttime $wingaterun
-       $flag_quit $msgType
+       $flag_quit $msgType $no_syscall
        $utime_userfile $wtime_userfile $ucount_userfile
        $utime_chanfile $wtime_chanfile $ucount_chanfile
+       $pubsize $pubcount $pubtime
+       $msgsize $msgcount $msgtime
+       $notsize $notcount $nottime
+       $running
 );
 
-# dynamic hash.
-use vars qw(@joinchan @ircServers @wingateBad @wingateNow @wingateCache
+# array.
+use vars qw(@ircServers @wingateBad @wingateNow @wingateCache
 );
 
-### dynamic hash. MUST BE REDUCED IN SIZE!!!
-# 
+### hash. MUST BE REDUCED IN SIZE!!!
+#
 use vars qw(%count %netsplit %netsplitservers %flood %dcc %orig
            %nuh %talkWho %seen %floodwarn %param %dbh %ircPort
            %topic %moduleAge %last %time %mask %file
-           %forked %chanconf %channels
+           %forked %chanconf %channels %cache
 );
 
 # Signals.
@@ -42,10 +47,9 @@ $SIG{'__WARN__'} = 'doWarn';
 
 # initialize variables.
 $last{buflen}  = 0;
-$last{say}     = "";
-$last{msg}     = "";
-$userHandle    = "default";
-$msgtime       = time();
+$last{say}     = '';
+$last{msg}     = '';
+$userHandle    = "_default";
 $wingaterun    = time();
 $firsttime     = 1;
 $utime_userfile        = 0;
@@ -54,14 +58,45 @@ $ucount_userfile = 0;
 $utime_chanfile        = 0;
 $wtime_chanfile        = 0;
 $ucount_chanfile = 0;
-
-$bot_version   = "blootbot cvs (20010214) -- $^O";
-$noreply       = "NOREPLY";
+$running       = 0;
+### more variables...
+# static scalar variables.
+$mask{ip}      = '(\d+)\.(\d+)\.(\d+)\.(\d+)';
+$mask{host}    = '[\d\w\_\-\/]+\.[\.\d\w\_\-\/]+';
+$mask{chan}    = '[\#\&]\S*|_default';
+my $isnick1    = 'a-zA-Z\[\]\{\}\_\`\^\|\\\\';
+my $isnick2    = '0-9\-';
+$mask{nick}    = "[$isnick1]{1}[$isnick1$isnick2]*";
+$mask{nuh}     = '\S*!\S*\@\S*';
+$msgtime       = time();
+$msgsize       = 0;
+$msgcount      = 0;
+$pubtime       = 0;
+$pubsize       = 0;
+$pubcount      = 0;
+$nottime       = 0;
+$notsize       = 0;
+$notcount      = 0;
+###
+open(VERSION, '<VERSION');
+$bot_release   = <VERSION> || "(unknown version)";
+chomp($bot_release);
+close(VERSION);
+$bot_version   = "infobot $bot_release -- $^O";
+$noreply       = 'NOREPLY';
 
 ##########
 ### misc commands.
 ###
 
+sub whatInterface {
+    if (!&IsParam('Interface') or $param{'Interface'} =~ /IRC/) {
+       return 'IRC';
+    } else {
+       return 'CLI';
+    }
+}
+
 sub doExit {
     my ($sig)  = @_;
 
@@ -77,20 +112,31 @@ sub doExit {
        &status("parent caught SIG$sig (pid $$).") if (defined $sig);
 
        &status("--- Start of quit.");
+       $ident ||= 'infobot';   # lame hack.
+
+       &status("Memory Usage: $memusage KiB");
 
-       &closeDCC();
        &closePID();
-       &seenFlush();
-       &quit($param{'quitMsg'}) if (&whatInterface() =~ /IRC/);
+       &closeStats();
+       # shutdown IRC and related components.
+       if (&whatInterface() =~ /IRC/) {
+           &closeDCC();
+           &seenFlush();
+           &quit($param{'quitMsg'});
+       }
        &writeUserFile();
        &writeChanFile();
-       &uptimeWriteFile()      if (&ChanConfList("uptime"));
-       &News::writeNews()      if (&ChanConfList("news"));
-       &closeDB();
+       &uptimeWriteFile()      if (&IsParam('Uptime'));
+       &sqlCloseDB();
        &closeSHM($shm);
-       &dumpallvars()          if (&IsParam("dumpvarsAtExit"));
+
+       if (&IsParam('dumpvarsAtExit')) {
+           &loadMyModule('DumpVars');
+           &dumpallvars();
+       }
+       &symdumpAll()           if (&IsParam('symdumpAtExit'));
        &closeLog();
-       &closeSQLDebug()        if (&IsParam("SQLDebug"));
+       &closeSQLDebug()        if (&IsParam('SQLDebug'));
 
        &status("--- QUIT.");
     } else {                                   # child.
@@ -111,7 +157,7 @@ sub doWarn {
 }
 
 # Usage: &IsParam($param);
-# blootbot.config specific.
+# infobot.config specific.
 sub IsParam {
     my $param = $_[0];
 
@@ -150,8 +196,8 @@ sub getChanConfList {
 
     foreach (keys %chanconf) {
        my $chan        = $_;
-#      &DEBUG("chan => $chan");
        my @array       = grep /^$param$/, keys %{ $chanconf{$chan} };
+       #&DEBUG("gCCL param => $param, chan => $chan, keys => " . join(':',keys %{ $chanconf{$chan} }) . " array => " . join(':', @array)) if ($param eq 'whatever');
 
        next unless (scalar @array);
 
@@ -159,7 +205,7 @@ sub getChanConfList {
            &WARN("multiple items found?");
        }
 
-       if ($array[0] eq "0") {
+       if ($chanconf{$chan}{$param} eq '0') {
            $chan{$chan}        = -1;
        } else {
            $chan{$chan}        =  1;
@@ -175,61 +221,67 @@ sub getChanConfList {
 # Return: 1 for enabled, 0 for passive disable, -1 for active disable.
 sub IsChanConf {
     my($param) = shift;
-    my $debug  = 0;    # knocked tons of bugs with this! :)
+
+    # knocked tons of bugs with this! :)
+    my $debug  = 0; # 1 if ($param eq 'whatever');
 
     if (!defined $param) {
        &WARN("IsChanConf: param == NULL.");
        return 0;
     }
 
+    # these should get moved to your .chan file instead of the .config
+    # .config items overide any .chan entries
+    if (&IsParam($param)) {
+       &WARN("ICC: found '$param' option in main config file.");
+       return 1;
+    }
+
+    $chan ||= "_default";
+
+    my $old = $chan;
     if ($chan =~ tr/A-Z/a-z/) {
-       &WARN("IsChanConf: lowercased chan.");
+       &WARN("IsChanConf: lowercased chan. ($old)");
     }
 
     ### TODO: VERBOSITY on how chanconf returned 1 or 0 or -1.
     my %chan   = &getChanConfList($param);
-    if (!defined $msgType or $msgType eq "") {
-       if ($chan{$chan}) {
-           &DEBUG("ICC: !msgType: $chan{$chan} (_default/$param)") if ($debug);
-       } elsif ($chan{_default}) {
-           &DEBUG("ICC: !msgType: $chan{_default} (_default/$param)") if ($debug);
-       } else {
-           &DEBUG("ICC: !msgType: 0 ($param)") if ($debug);
-       }
-
-       return $chan{$chan} || $chan{_default} || 0;
+    my $nomatch = 0;
+    if (!defined $msgType) {
+       $nomatch++;
+    } else {
+       $nomatch++ if ($msgType eq '');
+       $nomatch++ unless ($msgType =~ /^(public|private)$/i);
     }
 
-    if ($msgType eq "public") {
+### debug purposes only.
+#    if ($debug) {
+#      &DEBUG("param => $param, msgType => $msgType.");
+#      foreach (keys %chan) {
+#          &DEBUG("   $_ => $chan{$_}");
+#      }
+#    }
+
+    if ($nomatch) {
        if ($chan{$chan}) {
-           &DEBUG("ICC: public: $chan{$chan} ($chan/$param)") if ($debug);
+           &DEBUG("ICC: other: $chan{$chan} (_default/$param)") if ($debug);
        } elsif ($chan{_default}) {
-           &DEBUG("ICC: public: $chan{_default} (_default/$param)") if ($debug);
+           &DEBUG("ICC: other: $chan{_default} (_default/$param)") if ($debug);
        } else {
-           &DEBUG("ICC: public: 0 ($param)") if ($debug);
+           &DEBUG("ICC: other: 0 ($param)") if ($debug);
        }
-
        return $chan{$chan} || $chan{_default} || 0;
-    }
-
-    if ($msgType eq "private") {
-       if ($chan{_default}) {
-           &DEBUG("ICC: private: $chan{_default} (_default/$param)") if ($debug);
-       } elsif ($chan{$chan}) {
-           &DEBUG("ICC: private: $chan{$chan} ($chan/$param) (hack)") if ($debug);
+    } elsif ($msgType =~ /^(public|private)$/i) {
+       if ($chan{$chan}) {
+           &DEBUG("ICC: $msgType: $chan{$chan} ($chan/$param)") if ($debug);
+       } elsif ($chan{_default}) {
+           &DEBUG("ICC: $msgType: $chan{_default} (_default/$param)") if ($debug);
        } else {
-           &DEBUG("ICC: private: 0 ($param)") if ($debug);
+           &DEBUG("ICC: $msgType: 0 ($param)") if ($debug);
        }
-
        return $chan{$chan} || $chan{_default} || 0;
     }
 
-### debug purposes only.
-#    &DEBUG("param => $param, msgType => $msgType.");
-#    foreach (keys %chan) {
-#      &DEBUG("   $_ => $chan{$_}");
-#    }
-
     &DEBUG("ICC: no-match: 0/$param (msgType = $msgType)");
 
     return 0;
@@ -240,63 +292,124 @@ sub IsChanConf {
 #  About: Retrieve value for 'param' value in current/default chan.
 # Return: scalar for success, undef for failure.
 sub getChanConf {
-    my($param,$chan)   = @_;
+    my($param,$c)      = @_;
 
     if (!defined $param) {
-       &WARN("param == NULL.");
+       &WARN("gCC: param == NULL.");
        return 0;
     }
 
+    # this looks evil...
+    if (0 and !defined $chan) {
+       &DEBUG("gCC: ok !chan... doing _default instead.");
+    }
+
+    $c         ||= $chan;
+    $c         ||= "_default";
+    $c         = "_default" if ($c eq "*");    # FIXME
+    my @c      = grep /^\Q$c\E$/i, keys %chanconf;
+
+    if (@c) {
+       if (0 and $c[0] ne $c) {
+           &WARN("c ne chan ($c[0] ne $chan)");
+       }
+       if (!defined $chanconf{$c[0]}{$param} and ($c ne '_default')) {
+           return &getChanConf($param, '_default');
+       }
+       &DEBUG("gCC: $param,$c \"" . $chanconf{$c[0]}{$param} . '"');
+       return $chanconf{$c[0]}{$param};
+    }
+
+    #&DEBUG("gCC: returning _default... " . $chanconf{"_default"}{$param});
+    return $chanconf{"_default"}{$param};
+}
+
+sub getChanConfDefault {
+    my($what, $default, $chan) = @_;
     $chan      ||= "_default";
-    my @c      = grep /^$chan$/i, keys %chanconf;
 
-    if (@c and $c[0] ne $chan) {
-       &WARN("c ne chan ($c[0] ne $chan)");
+    if (exists $param{$what}) {
+       if (!exists $cache{config}{$what}) {
+           &status("config ($chan): backward-compatible option: found param{$what} ($param{$what}) instead of chan option");
+           $cache{config}{$what} = 1;
+       }
+
+       return $param{$what};
     }
+    my $val = &getChanConf($what, $chan);
+    return $val if (defined $val);
 
-    return $chanconf{$c[0] || "_default"}{$param};
+    $param{$what}      = $default;
+    &status("config ($chan): auto-setting param{$what} = $default");
+    $cache{config}{$what} = 1;
+    return $default;
 }
 
-sub showProc {
-    my ($prefix) = $_[0] || "";
 
-    if (!open(IN, "/proc/$$/status")) {
-       &ERROR("cannot open '/proc/$$/status'.");
-       return;
+#####
+#  Usage: &findChanConf($param);
+#  About: Retrieve value for 'param' value from any chan.
+# Return: scalar for success, undef for failure.
+sub findChanConf {
+    my($param) = @_;
+
+    if (!defined $param) {
+       &WARN("param == NULL.");
+       return 0;
+    }
+
+    my $c;
+    foreach $c (keys %chanconf) {
+       foreach (keys %{ $chanconf{$c} }) {
+           next unless (/^$param$/);
+
+           return $chanconf{$c}{$_};
+       }
     }
 
-    if ($^O eq "linux") {
+    return;
+}
+
+sub showProc {
+    my ($prefix) = $_[0] || '';
+
+    if ($^O eq 'linux') {
+       if (!open(IN, "/proc/$$/status")) {
+           &ERROR("cannot open '/proc/$$/status'.");
+           return;
+       }
+
        while (<IN>) {
            $memusage = $1 if (/^VmSize:\s+(\d+) kB/);
        }
        close IN;
 
-    } elsif ($^O eq "netbsd") {
-       $memusage = (stat "/proc/$$/mem")[7]/1024;
+    } elsif ($^O eq 'netbsd') {
+       $memusage = int( (stat "/proc/$$/mem")[7]/1024 );
 
     } elsif ($^O =~ /^(free|open)bsd$/) {
        my @info  = split /\s+/, `/bin/ps -l -p $$`;
        $memusage = $info[20];
 
     } else {
-       $memusage = "UNKNOWN";
+       $memusage = 'UNKNOWN';
        return;
     }
 
-    if (defined $memusageOld and &IsParam("DEBUG")) {
+    if (defined $memusageOld and &IsParam('DEBUG')) {
        # it's always going to be increase.
        my $delta = $memusage - $memusageOld;
        my $str;
        if ($delta == 0) {
            return;
        } elsif ($delta > 500) {
-           $str = "MEM:$prefix increased by $delta kB. (total: $memusage kB)";
+           $str = "MEM:$prefix increased by $delta KiB. (total: $memusage KiB)";
        } elsif ($delta > 0) {
-           $str = "MEM:$prefix increased by $delta kB";
+           $str = "MEM:$prefix increased by $delta KiB";
        } else {        # delta < 0.
            $delta = -$delta;
            # never knew RSS could decrease, probably Size can't?
-           $str = "MEM:$prefix decreased by $delta kB. YES YES YES";
+           $str = "MEM:$prefix decreased by $delta KiB.";
        }
 
        &status($str);
@@ -313,37 +426,38 @@ sub setup {
     &openLog();                # write, append.
     &status("--- Started logging.");
 
-    foreach ("debian") {
-       my $dir = "$bot_base_dir/$_/";
-       next if ( -d $dir);
-       &status("Making dir $_");
-       mkdir $dir, 0755;
-    }
-
     # read.
-    &loadLang($bot_misc_dir.           "/blootbot.lang");
+    &loadLang($bot_data_dir. "/infobot.lang");
     &loadIRCServers();
     &readUserFile();
     &readChanFile();
     &loadMyModulesNow();       # must be after chan file.
 
     $shm = &openSHM();
-    &openSQLDebug()    if (&IsParam("SQLDebug"));
-    &openDB($param{'DBName'}, $param{'SQLUser'}, $param{'SQLPass'});
-
-    &status("Setup: ". &countKeys("factoids") ." factoids.");
-    &News::readNews() if (&ChanConfList("news"));
+    &openSQLDebug()    if (&IsParam('SQLDebug'));
+    &sqlOpenDB($param{'DBName'}, $param{'DBType'}, $param{'SQLUser'},
+       $param{'SQLPass'});
+    &checkTables();
+
+    &status("Setup: ". &countKeys('factoids') ." factoids.");
+    &getChanConfDefault('sendPrivateLimitLines', 3, $chan);
+    &getChanConfDefault('sendPrivateLimitBytes', 1000, $chan);
+    &getChanConfDefault('sendPublicLimitLines', 3, $chan);
+    &getChanConfDefault('sendPublicLimitBytes', 1000, $chan);
+    &getChanConfDefault('sendNoticeLimitLines', 3, $chan);
+    &getChanConfDefault('sendNoticeLimitBytes', 1000, $chan);
 
     $param{tempDir} =~ s#\~/#$ENV{HOME}/#;
 
-    &status("Initial memory usage: $memusage kB");
+    &status("Initial memory usage: $memusage KiB");
+    &status("-------------------------------------------------------");
 }
 
 sub setupConfig {
     $param{'VERBOSITY'} = 1;
-    &loadConfig($bot_misc_dir."/blootbot.config");
+    &loadConfig($bot_config_dir."/infobot.config");
 
-    foreach ("ircNick", "ircUser", "ircName", "DBType", "tempDir") {
+    foreach ( qw(ircNick ircUser ircName DBType tempDir) ) {
        next if &IsParam($_);
        &ERROR("Parameter $_ has not been defined.");
        exit 1;
@@ -360,18 +474,18 @@ sub setupConfig {
 
     if (! -d $param{tempDir}) {
        &status("making $param{tempDir}...");
-       system("mkdir $param{tempDir}");
+       mkdir $param{tempDir}, 0755;
     }
 
     # static scalar variables.
-    $file{utm} = "$bot_base_dir/$param{'ircUser'}.uptime";
-    $file{PID} = "$bot_base_dir/$param{'ircUser'}.pid";
+    $file{utm} = "$bot_state_dir/$param{'ircUser'}.uptime";
+    $file{PID} = "$bot_run_dir/$param{'ircUser'}.pid";
 }
 
 sub startup {
-    if (&IsParam("DEBUG")) {
+    if (&IsParam('DEBUG')) {
        &status("enabling debug diagnostics.");
-       ### I thought disabling this reduced memory usage by 1000 kB.
+       # I thought disabling this reduced memory usage by 1000 KiB.
        use diagnostics;
     }
 
@@ -382,17 +496,24 @@ sub startup {
 }
 
 sub shutdown {
+    my ($sig) = @_;
     # reverse order of &setup().
-    &DEBUG("shutdown called.");
+    &status("--- shutdown called.");
+
+    # hack.
+    $ident ||= 'infobot';
 
-    # opened files must be written to on shutdown/hup/whatever
-    # unless they're write-only, like uptime.
-    &writeUserFile();
-    &writeChanFile();
-    &News::writeNews() if (&ChanConfList("news"));
+    if (!&isFileUpdated("$bot_state_dir/infobot.users", $wtime_userfile)) {
+       &writeUserFile()
+    }
 
-    &closeDB();
-    &closeSHM($shm);   # aswell. TODO: use this in &doExit?
+    if (!&isFileUpdated("$bot_state_dir/infobot.chan", $wtime_chanfile)) {
+       &writeChanFile();
+    }
+
+    &sqlCloseDB();
+    # aswell. TODO: use this in &doExit?
+    &closeSHM($shm);
     &closeLog();
 }
 
@@ -403,17 +524,22 @@ sub restart {
        &status("--- $sig called.");
 
        ### crappy bug in Net::IRC?
-       if (!$conn->connected and time - $msgtime > 900) {
-           &status("reconnecting because of uncaught disconnect.");
-##         $irc->start;
+       my $delta = time() - $msgtime;
+       &DEBUG("restart: dtime = $delta");
+       if (!$conn->connected or time() - $msgtime > 900) {
+           &status("reconnecting because of uncaught disconnect \@ ".scalar(gmtime) );
+###        $irc->start;
+           &clearIRCVars();
            $conn->connect();
-           return;
+###        return;
        }
 
-       &DCCBroadcast("-HUP called.","m");
-       &shutdown();
-       &loadConfig($bot_misc_dir."/blootbot.config");
-       &reloadAllModules() if (&IsParam("DEBUG"));
+       &ircCheck();    # heh, evil!
+
+       &DCCBroadcast("-HUP called.",'m');
+       &shutdown($sig);
+       &loadConfig($bot_config_dir."/infobot.config");
+       &reloadAllModules() if (&IsParam('DEBUG'));
        &setup();
 
        &status("--- End of $sig.");
@@ -427,9 +553,8 @@ sub loadConfig {
     my ($file) = @_;
 
     if (!open(FILE, $file)) {
-       &ERROR("FAILED loadConfig ($file): $!");
-       &status("Please copy files/sample.config to files/blootbot.config");
-       &status("  and edit files/blootbot.config, modify to tastes.");
+       &ERROR("Failed to read configuration file ($file): $!");
+       &status("Please read the INSTALL file on how to install and setup this file.");
        exit 0;
     }
 
@@ -440,7 +565,7 @@ sub loadConfig {
        next unless /\S/;
        my ($set,$key,$val) = split(/\s+/, $_, 3);
 
-       if ($set ne "set") {
+       if ($set ne 'set') {
            &status("loadConfig: invalid line '$_'.");
            next;
        }
@@ -459,3 +584,5 @@ sub loadConfig {
 }
 
 1;
+
+# vim:ts=4:sw=4:expandtab:tw=80