#!/usr/bin/perl

use Net::IRC;
use Net::Jabber;
use Time::Local;
use LWP::Simple;
use IO::Socket;
use XML::Simple;
use Authen::Simple::Kerberos;
use Switch;
use DBI;
use SVN::SVNLook;
use Data::Dumper;

($sec, $min, $hour, $day, $month, $year) = (localtime())[0..5];
$timenow = timelocal($sec, $min, $hour, $day, $month, $year);
$midnight = timelocal(0, 0, 0, $day, $month, $year);
$midnight = $midnight * 60 * 60 * 24;
$secmidnight = $midnight - $timenow;

require "jam-config";

open(DE, ">debug2"); #DEBUG CODE
DE->autoflush(1);    #DEBUG CODE

if ( -f "jam-features" ) {
  open(FF, "<jam-features");
  while(<FF>) {
    ($feature, $chanlist) = $_=~/([A-Za-z_#-]+):(.*)/;
    $first = substr($feature, 0, 1);
    @chans = split(/,/, $chanlist);
    if ($first eq "J") {
      @jchan = @chans;
    } elsif ($first eq "N") {
      @{$dlist{$feature}} = @chans;
    } else {
      @{$alist{$feature}} = @chans;
    }
  }
  close(FF);
}

if ( -f "jam-sslog" ) {
  open(SSLOG, "<jam-sslog");
  while(<SSLOG>) {
    ($event, $nick, $time, $msg) = $_=~/(seen|stats):([A-Za-z0-9-_|~]+):([0-9]+):(.*)/;
    if ($event eq "seen") {
      $seen{$nick} = $time;
      $seenmsg{$nick} = $msg;
    } elsif ($event eq "stats") {
      $characters{$nick} = $time;
    }
  }
  close SSLOG;
}

if ( -f "jam-tell" ) {
  open(TF, "<jam-tell");
  while(<TF>) {
    ($nick, $msg) = $_=~/([A-Za-z0-9-_|]+):(.*)/;
    push(@{$tellmsg{$nick}}, $msg);
  }
  close TF;
}

if ( ! -d syslogs ) {
  mkdir syslogs;
}

if ( ! -d irclogs ) {
  mkdir irclogs;
}

($lday, $lmonth, $lyear) = (localtime())[3..5];
$lmonth = $lmonth + 1;
$lyear = $lyear + 1900;
$lfile = $lyear . $lmonth . $lday;
open(SLOG, ">>syslogs/$lfile");
open(ILOG, ">>irclogs/$lfile");
open(SSLOG, ">jam-sslog");
SLOG->autoflush(1);
ILOG->autoflush(1);
SSLOG->autoflush(1);
slogger("The Jam IRC bot has started");

my $xs1 = XML::Simple->new();
$kerberos = Authen::Simple::Kerberos->new(
  realm => $realm
);

irc_connect();

jabber_connect();

shed_rotate();

sub onConnect {
  $self=shift;
  slogger("Trying to join channel and register with NickServ");
  $conn->privmsg("NickServ", "IDENTIFY $nickpass");
  sleep(2);
  foreach (@jchan) {
    $self->join($_);
  }
  #&check_news();
  load_group();
  open(RF, "<jam-remind");
  while(<RF>) {
    my $atime = time();
    ($nick, $time, $mesg) = $_=~/([A-Za-z0-9-_|~]+):([0-9]+):(.*)/;
    if ($time > $atime) {
      $remindcount{$nick}++;
      $conn->schedule($time, \&onRemind, $nick, $nick, $mesg);
    } else {
      Message($nick, "$nick, You asked me to remind you about: $mesg.  Unfortuantly, I was not running when the reminder was due", "IRC");
    }
  }
  close RF;
  unlink("jam-remind");
}

sub InMessage {
        my $sid = shift;
        my $message = shift;
        my $type = $message->GetType();
        my $user = $message->GetFrom();
        my $subject = $message->GetSubject();
        my $text = $message->GetBody();
	($er, $resource) = split(/\//, $user);
	&Process($user, $user, $text, 'Jabber');
	&JabberMessage($user, $text);
}

sub InPresence {
  my $sid = shift;
  my $presence = shift;
  my $type = $presence->GetType();
  my $from = $presence->GetFrom();
  my $show = $presence->GetShow();
  ($from, $resource) = split(/\//, $from);
  if ($type eq "subscribe") {
    $reply = $presence->Reply(template=>"client",
                              type=>"subscribed");
    $Con->Send($reply);
    $reply = $presence->Reply(template=>"client",
                              type=>"subscribe");
    $Con->Send($reply);
  }
  if ($type eq "unavailable") {
    $jabstat{$from} = "offline";
  }
  if ($type eq "" && $show eq "") {
    $jabstat{$from} = "Online";
  }
  if ($show ne "") {
    switch($show) {
      case "away" { $jabstat{$from} = "Away" }
      case "xa" { $jabstat{$from} = "Extended away" }
      case "dnd" { $jabstat{$from} = "Do no disturb" }
      case "chat" { $jabstat{$from} = "Free for chat" }
    }
  }
}

sub InIQ {
}

sub onRequest {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->{args}[0];
	#my $user=$event->user;
	#my $host=$event->host;
	$chan=$event->{to}[0];
	$snick = $nick;
	$snick =~ tr/[A-Z]/[a-z]/;
	$seen{$snick} = time();
	print SSLOG "seen:$snick:$seen{$snick}:They said: $text\n";
	$seenmsg{$snick} = "They said: $text";
	ilogger($nick, $text);
	$len = length($text);
	$characters{$snick} = $characters{$snick} + $len;
	print SSLOG "stats:$snick:$characters{$snick}:\n";
	&tell($nick, $chan, 'IRC');
	&Process($nick, $chan, $text, 'IRC');
}

sub Process {
	my $nick = shift;
	my $chan = shift;
	my $text = shift;
	my $source = shift;

	if ($text=~/.*http:\/\/.*.(jpg|jpeg).*/) {
	  foreach(@{$alist{"porn"}}) {
	    if ($_ eq $chan) {
	      $conn->privmsg($chan, "Stop looking at porn!");
	    }
	  }
	}

        #if ($text=~/^!calc.*/) {
        #  $conn->privmsg($chan, "A perfectly functional calculator ships with most moderm OSs!");
        #}

	if (($cmd) = $text=~/^(Test|!time|Joshua|!tictactoe|!about|!calc.*|Jam\!)$/) {
	  switch($cmd) {
	    case "Test" { Message($chan, "TUESDAY", $source) }
	    case "!time" { Message($chan, "<--", $source) }
	    case "Joshua" { Message($chan, "Greetings, Professor Falken", $source) }
	    case "!tictactoe" { Message($chan, "A strange game.  The only winning move is not to play.", $source) }
	    case "!about" { Message($chan, "Jam, written by BOfH from Bob's Basement | www.bobsbasement.co.uk", $source) }
	    case "!calc.*" { Message($chan, "A perfectly functional calculator ships with most moderm OSs!", $source) }
	    case "Jam!" { Message($chan, "Hey, $nick", $source) }
	  }
	}

	if ($text=~/^!help/) {
	  bot_help($chan, $text);
	}

	if (($weathercmd, $id) = $text=~/^(!weather|!temp|!miscweather|!sun|!cur weather|!cur miscweather) ([A-Za-z0-9 ]+)$/) {
	  if ($id=~/^[0-9]+$/) {
	    $wid = "id/" . $id . ".xml";
	  } elsif (($main) = $id=~/(\b[A-PR-UWYZa-pr-uwyz][A-HK-Ya-hk-y0-9][A-HJKSTUWa-hjkstuw0-9]?[ABEHMNPRVWXYabehmnprvwxy0-9]?) {0,1}[0-9][ABD-HJLN-UW-Zabd-hjln-uw-z]{2}\b/) {
	    $main=~tr/[a-z]/[A-Z]/;
	    open(PO, $post);
 	    while(<PO>) {
              @line = split(/,/, $_);
              if ($main eq @line[1]) {
	        $wid = @line[2];
              }
            }
	    chomp $wid;
            close PO;
	  } else {
            open(PO, $post);
            while(<PO>) {
              @line = split(/,/, $_);
              if ($id eq @line[0]) {
                $wid = @line[2];
              }
            }
	    close PO;
	    open(CO, $country);
    	    while(<CO>) {
              @line = split(/,/, $_);
              if ($id eq @line[0] || $id eq @line[1]) {
                $wid = @line[2]; 
      	      }
            }
	    chomp $wid;
            close CO;
          }
	  if ($weathercmd eq "!cur weather" || $weathercmd eq "!cur miscweather") {
	    $doc = get $curweatherfeed . $wid;
	  } else {
	    $test = $weatherfeed . $wid;
	    $doc = get $weatherfeed . $wid;
	  }
	  $doc=~s/&#xB0;//g;
	  if ($wid eq '') {
	    Message($chan, "No results were found", $source);
	  } elsif ($doc eq '') {
	    Message($chan, "An error occured :-(", $source);
	  } else {
	    my $weatherxml = $xs1->XMLin($doc, ForceArray=>[item]);
	    foreach (@{$weatherxml->{channel}->{item}}) {
	      $weather = $_->{description};
	      $weather1 = $_->{title};
	      $weather1=~s/\n//g;
	      ($day, $weather2) = $weather1=~/^([A-Za-z0-9 ]+): ([a-z ]+)[,.].*/;
	      $weather=~s/\n//g;
	      $weather=~s/, /,/g;
	      @wa = split(/,/, $weather);
	      switch($weathercmd) {
	        case "!weather" { Message($chan, "Day: $day, Weather: $weather2", $source); }
	        case "!temp" { Message($chan, "Day: $day, @wa[0], @wa[1]", $source); }
	        case "!miscweather" { Message($chan, "Day: $day, @wa[2], @wa[3], @wa[4], @wa[5], @wa[6]", $source); }
	        case "!sun" { Message($chan, "Day: $day, @wa[9], @wa[10]", $source); }
	        case "!current weather" { Message($chan, "$day: $weather2", $source); }
	        case "!current misc weather" { Message($chan, "$day: @wa[0], @wa[1], @wa[2], @wa[3], @wa[4], @wa[5], @wa[6]", $source); }
	      }
	    }
	  $wid='';
	  }
	}

	if ($text=~/^!breaking news$/) {
	  $doc = get $breakingnewsfeed;
	  if ($doc eq "") {
	    Message($chan, "Sorry, an error occured :-(", $source);
	  } else {
	    my $news = $xs1->XMLin($doc);
            Message($chan, "Breaking news: $news->{story}->{headline}", $source);
	  }
	}

	if (($data) = $text=~m/!rot13 (.*)/) {
	  $data=~y/A-Za-z0-9/N-ZA-Mn-za-m5-90-4/;
	  Message($chan, $data, $source);
	}

        if (($user) = $text=~/^!seen ([a-zA-Z0-9_-|]+)/) {
	  $luser = $user;
	  $luser =~ tr/[A-Z]/[a-z]/;
	  if (exists $seen{$luser}) {
            $seentime = time() - $seen{$luser};
	    if ($seentime >= 604800) {
	      $seentime = $seentime / 604800;
	      $units = "week";
	    } elsif ($seentime >= 86400) {
	      $seentime = $seentime / 86400;
	      $units = "day";
	    } elsif ($seentime >= 3600) {
	      $seentime = $seentime / 3600;
	      $units = "hour";
	    } elsif ($seentime >= 60) {
	      $seentime = $seentime / 60;
	      $units = "minute";
	    } else {
	      $units = "second";
	    }

	    if ($seentime != 1) {
	      $units = $units . "s";
	    }
	    
            Message($chan, "I last saw $user $seentime $units ago", $source);
	    Message($chan, $seenmsg{$luser}, $source);
	  } else {
	    Message($chan, "I have never seen $user", $source);
	  }
        }

        if ((my $user) = $text=~/^!stats ([a-zA-Z0-9_-|^]+)/) {
	  my $luser = $user;
	  $luser =~ tr/[A-Z]/[a-z]/;
          if (exists $characters{$luser}) {
	    Message($chan, "$user has said $characters{$luser} characters", $source);
	  } else {
	    Message($chan, "Who?!", $source);
	  }
	}

	if (($kickme) = $text=~/!deal_with ([a-zA-Z0-9_-|^]+)/) {
	  $kgrp = "k" . $chan;
	  $result = check_group($kgrp, $nick);
	  if ($result eq "true") {
	    $conn->kick($chan, $kickme);
	  }
	}

	if (($recipient, $message) = $text=~/^!tell ([a-zA-Z0-9_-|^]+) (.*)/) {
	  $recipient =~ tr/[A-Z]/[a-z]/;
	  push(@{$tellmsg{$recipient}}, "$nick asked me to tell you: $message");
	  Message($chan, "$nick, I'll tell $recipient when I next see them", $source);
	  write_tf();
	}

        if ($text=~/^!news$/) {
          my $doc = get $bbcfrontpage;
          if ($doc eq "") {
            Message($chan, "Sorry, an error occured :-(", $source);
          } else {
            my $news = $xs1->XMLin($doc);
            my @newsdate="";
            foreach (@{$news->{channel}->{item}}) {
	      ($day, $month, $year, $hour, $minute, $second) = $_->{pubDate}=~/[A-Z][a-z]{2}, ([0-9]{2}) ([A-Z][a-z]{2}) ([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2}) GMT/;
              $epoch = timelocal($second, $minute, $hour, $day, $months{$month}, $year);
              my $url = $_->{link};
              print "$epoch: $_->{title}\n";
              push(@newsdate, $epoch);
              push(@{$story{$epoch}}, $_->{title});
              push(@{$story{$epoch}}, $url);
            }
            my @sortdate = reverse sort(@newsdate);
            for ($i=0; $i<5; $i++) {
              my $tinyurl = tiny(@{$story{@sortdate[$i]}}[1]);
              Message($chan, "BBC: @{$story{@sortdate[$i]}}[0] <$tinyurl>", $source);
            }
          }
        }

	if (($feature) = $text=~/!feature ([A-Za-z0-9-_]+) on/) {
	  $dfeature = "N" . $feature;
	  if (!exists $alist{$feature}) {
	    $conn->privmsg($chan, "This is not a valid feature");
	  } else {
	    my $a=0;
	    foreach (@{$alist{$feature}}) {
	      if ($_ eq $chan) {
	        $a = 1;
	      }
	    }
	    if ($a == 1) {
	      $conn->privmsg($chan, "This feature is already turned on!");
	    } else {
	      push(@{$alist{$dfeature}}, $chan);
	      undef @temp;
	      foreach (@{$dlist{$dfeature}}) {
	        if ($_ ne $chan) {
	          push(@temp, $_);
	        }
	      }
	    @{$dlist{$dfeature}} = @temp;
	    write_ff();
	    $conn->privmsg($chan, "$feature feature enabled");
	    }
	  }	
	}

	if (($feature) = $text=~/!feature ([A-Za-z0-9-_]+) off/) {
	  $dfeature = "N" . $feature;
	  if (!exists $dlist{$dfeature}) {
	    $conn->privmsg($chan, "This is not a valid feature");
	  } else {
            my $a=0;
            foreach (@{$dlist{$dfeature}}) {
              if ($_ eq $chan) {
                $a = 1;
              }
            }
            if ($a == 1) {
              $conn->privmsg($chan, "This feature is already turned off!");
            } else {
              push(@{$dlist{$dfeature}}, $chan);
              undef @temp;
              foreach (@{$alist{$feature}}) {
                if ($_ ne $chan) {
                  push(@temp, $_);
                }
              }
            @{$alist{$feature}} = @temp;
            write_ff();
            $conn->privmsg($chan, "$feature feature disabled");
	    }
          }
        }
 
        if (($a, $b, $c) = $text=~/^!res col ([0-9]+\.{0,1}[0-9]{0,2})([RKM]) ([0-9]+)%/) {
          if ($b eq 'K') {
            $a = $a * 1000;
          } elsif ($b eq 'M') {
            $a = $a * 1000000;
          }
          $e = $a;
          $a = $a * 1000000;
          my $three = substr($a, 2, 1);
          $one = substr($a, 0, 1);
          $two = substr($a, 1, 1);
          if ($three > 0 && $three < 10) {
            $d = 3;
            $next = substr($a, 2, 1);
	    $reply = @colour[$one] . " " . @colour[$two] . " " . @colour[$next];
          } else {
            $d = 2;
	    $reply = @colour[$one] . " " . @colour[$two] . " ";
          }
          if ($e < 10 && $e > 0) {
	    $reply = $reply . "Gold";
          } elsif ($e < 0) {
	    $reply = $reply . "Silver";;
          } elsif ($e == 0) {
	    $reply = $reply . "Black";
          } else {
            $len = length($e) - $d;
	    $reply = $reply . @colour[$len];
          }
	  $reply = $reply . " " . $tolerance{$c};
	  Message($chan, $reply, $source);
	}

	if (($a, $b, $c, $d, $e) = $text=~/^!res num ([A-Za-z]+) ([A-Za-z]+) ([A-Za-z]+) ([A-Za-z]+) {0,1}([A-Za-z]*)/) {
          $code = $resnumcol{$a} . $resnumcol{$b};
          print "$code\n";
          if ($e eq "") {
            $f = length($c);
            $code = $code * $resnumcol1{$c};
            $tol = $resnumtol{$d};
          } else {
            $code = $code . $c;
            $code = $code * $resnumcol1{$c};
            $tol = $restolnum{$e};
          }
	  if ($code > 1000000) {
	    $code = $code / 1000000;
	    $code = $code . "M ";
	  } elsif ($code > 1000) {
	    $code = $code / 1000;
	    $code = $code . "K ";
	  } else {
	    $code = $code . "R ";
	  }
          $reply = $code . $tol . "%\n";
	  Message($chan, $reply, $source);
        }

	if ((my $ip, my $mask) = $text=~/!ip ([1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9])\/([0-9.]+)/) {
	  my $b = 0;
	  if ($mask=~/[1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9]\.[1-2]{0,1}[0-9]{0,1}[0-9]/) {
	    Message($chan, "Mask receieved: $mask", $source);
	    @moctet = split(/\./, $mask);
	    $mask = @moctet[0] * 16777216 + @moctet[1] * 65536 + @moctet[2] * 256 + @moctet[3];
	    $b = 1;
	  }
	  if ($mask >= 0 && $mask <= 32) {
	    $mask = (2 ** 32) - (2 ** (32 - $mask));
	    $b = 1;
	  }
	  if ($b == 1) {
	    @ioctet = split(/\./, $ip);
	    $dec = @ioctet[0] * 16777216 + @ioctet[1] * 65536 + @ioctet[2] * 256 + @ioctet[3];
	    $numhosts = (2 ** 32) - $mask;
	    $netnum = dec2dot($mask & $dec);
	    $bcast = dec2dot(($mask & $dec) + $numhosts - 1);
	    Message($chan, "Number of hosts: $numhosts", $source);
	    Message($chan, "Network number: $netnum Broadcast address: $bcast", $source);
	  }
	}

	if ($text=~/^!moon$/) {
	  Message($chan, get_moon(), $source);
	}

	if ((my $report) = $text=~/!(wiki|phonedb|svn) stats/) {
	  stats($chan, $report, $source);
	}

	if (my (@var) = $text=~/!vir ([0-9]+[AKMRV]) ([0-9]+[AKMRV])/) {
	  vir($chan, @var);
	}
}

sub onMsg {
	my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->{args}[0];
        my $user=$event->user;
        my $host=$event->host;
	&tell($nick, $nick, "IRC");
	
	slogger("$nick PM'd me with: $text");

	if (($echan, $echo) = $text=~/^!echo (#[A-Za-z0-9-|_]+) (.*)/) {
	  my $fchan = substr($echan, 1);
	  $group = "k" . $fchan;
	  if (check_group($group, $nick) eq "true") {
	    $conn->privmsg($echan, $echo);
	  }
 	}

	if (($time, $unit, $exec) = $text=~/^!remind me in ([0-9]+) ((?:second|minute|hour|day|week|month)s{0,1}) to (.*)/) {
	  $otime = $time;
	  if ($unit eq "minute" || $unit eq "minutes") {
	    $time = $time * 60;
	  }
	  if ($unit eq "hour" || $unit eq "hours") {
	    $time = $time * 3600;
	  }
	  if ($unit eq "day" || $unit eq "days") {
	    $time = $time * 86400;
	  }
	  if ($unit eq "week" || $unit eq "weeks") {
	    $time = $time * 604800;
	  }
	  if ($unit eq "month" || $unit eq "months") {
	    $time = $time * 2592000;
	  }
	  if ($remindcount{$nick} > 20) {
	    $conn->privmsg($nick, "Sorry, you are only allowed 20 active reminders");
	  } else {
	    open(RF, ">>jam-remind");
	    my $rtime = time() + $time;
	    print RF "$nick:$rtime:$exec\n";
	    close RF;
	    $remindcount{$nick}++;
	    $conn->schedule($time, \&onRemind, $nick, $nick, $exec);
	    $conn->privmsg($nick, "OK $nick, I'll remind you about that in $otime $unit");
	  }
	}

	if ($text=~/^!quit$/ && $user eq "~mike" && $host=~/bobsbasement.co.uk$/) {
	  $quitter = $nick . "!" . $user . "@" . $host;
	  slogger("!quit called by $quitter");
	  open(JD, ">jam-data");
	  foreach $key (keys %seen) {
  	    $value = $seen{$key};
  	    print JD "seen:$key:$value\n";
	  }
	  close(JD);
	  close(SLOG);
	  close(ILOG);
	  $Con->Disconnect();
          $conn->quit();
	  exit 0;
        }

	if (($tchan, $url) = $text=~/^!tiny (#[A-Za-z0-9-_|]+) (.*)/) {
	  $doc = tiny($url);
	  $conn->privmsg($tchan, "URL from $nick: $doc");
	}

	if (($rchan) = $text=~/!join (#[A-Za-z0-9-_]+)/) {
	  slogger("Join of $rchan requested by $nick");
	  foreach $key (keys %dlist) {
	    my $a = 0;
	    foreach (@{$dlist{$key}}) {
	      if ($_ eq $rchan) {
	        $a = 1;
	      }
	    }
	    if ($a == 0) {
	      $akey = substr($key, 1);
	      push(@{$alist{$akey}}, $rchan);
	    }
	  }
	  push(@jchan, $rchan);
	  write_ff();
	  $self->join($rchan);
	}

	if (($rchan) = $text=~/!part (#[A-Za-z0-9-_]+)/) {
	  slogger("Part from $rchan requested by $nick");
	  foreach $key (keys %alist) {
	    undef @temp;
	    foreach (@{$alist{$key}}) {
	      if ($_ ne $rchan) {
	        push(@temp, $_);
	      }
	    }
	    @{$alist{$key}} = @temp;
	  }
	  undef @temp;
	  foreach(@jchan) {
	    if ($_ ne $rchan) {
	      push(@temp, $_);
	    }
	  }
	  @jchan = @temp;
	  write_ff();
          $self->part($rchan);
        }

	if ($text=~/^!tfl$/) {
	  foreach $key (keys %tfl) {
            $value = $tfl{$key};
            $conn->privmsg($nick, "$key: $value");
	  }
        }

	if ((my $tline) = $text=~/^!tfl alert ([A-Za-z &]+)$/) {
	  if ($tfl{$tline} eq "") {
	    $conn->privmsg($nick, "Sorry, this line does not exist");
	  } else {
	    push(@{$tflalert{$tline}}, $nick);
	    $conn->privmsg($nick, "You will recieve alerts for the $tline line");
	  }
	}

	if ((my $tline) = $text=~/^!tfl remove ([A-Za-z &]+)$/) {
	  if ($tfl{$tline} eq "") {
            $conn->privmsg($nick, "Sorry, this line does not exist");
          } else {
	    foreach (@{$tflalert{$tline}}) {
	      if ($nick ne $_) {
	        push(@temparray, $_);
	      }
	    }
	  @{$tflalert{$tline}} = @temparray;
	  $conn->privmsg($nick, "You will no longer receive alerts for the $tline line");
	  }
	}

	if (($uid, $pass) = $text=~/^!auth ([A-Za-z0-9-_|]+) (.+)/) {
	  if ($kerberos->authenticate($uid, $pass)) {
	    $nickauth{$nick} = $uid;
	    $conn->privmsg($nick, "You are authenticated");
	  } else {
	    $conn->privmsg($nick, "Authentication failed");
	  }
	}

	if (($group) = $text=~/!group ([A-Za-z0-9]+)/) {
	  if (check_group($group, $nick) eq "true") {
	    $conn->privmsg($nick, "True");
	  } else {
	    $conn->privmsg($nick, "False");
	  }
	}

	if ($text=~/^!reload group$/) {
	  &load_group();
	  $conn->privmsg($nick, "Reload complete");
	}

	if (($kchan, $kuser) = $text=~/!deal_with #([A-Za-z0-9-]+) ([A-Za-z0-9-|_~]+)$/) {
	  $group = "k" . $kchan;
	  if (check_group($group, $nick) eq "true") {
	    $kchan = "#" . $kchan;
	    $conn->kick($kchan, $kuser);
	  }
	}

	if ($text=~/^!roster$/ && check_group("admin", $nick) eq "true") {
          foreach(@jabberroster) {
	    $conn->privmsg($nick, $_);
	  }
	}

	if (($user) = $text=~/!irc2jab ([A-Za-z0-9-|_~@\/\.]+)/) {
	  if (exists $irc2jab{$nick}) {
	    Message($nick, "You already appear to have an associate to $irc2jab{$nick}", "IRC");
	  } elsif ($tempj2i{$user} eq $nick) {
	    $jab2irc{$user} = $nick;
            $irc2jab{$nick} = $user;
	    Message($nick, "You are now paired with $nick", "IRC");
	  } else {
	    $tempi2j{$nick} = $user;
	    Message($nick, "OK, you now need to Jabber with \"!jab2irc $user\" to ensure you are who you say you are", "IRC");
	  }
	}	    
}

sub JabberMessage {
	my $user = shift;
	my $text = shift;
	if (($nick) = $text=~/!jab2irc ([A-Za-z0-9-|_~@]+)/) {
	  if (exists $jab2irc{$user}) {
	    Message($user, "You already appear to have an associate to $jab2irc{$user}", "Jabber");
	  } elsif ($tempi2j{$user} eq $nick) {
	    $jab2irc{$user} = $nick;
	    $irc2jab{$nick} = $user;
	    Message($user, "You are now paired with $nick", "Jabber");
	  } else {
	    $tempj2i{$user} = $nick;
	    Message($user, "OK, you now need to PM me on IRC with \"!irc2jab $user\" to ensure you are who you say you are", "Jabber");
	  }
	}
}

sub onJoin {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->{args}[0];
	my $chan=$event->to;
        $seen{$nick} = time();
        $seenmsg{$nick} = "$nick was last seen joining us";
	ilogger("$nick has joined $chan");
	&tell($nick, $chan, "IRC");
}

sub onNick {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
	my $victim = join(' ', $event->args);
        my $text=$event->{args}[0];
	my $chan=$event->to;
        $seen{$nick} = time();
        $seenmsg{$nick} = "$nick was last seen changing their nick to $victim";
	ilogger("$nick is now known as $victim");
	&tell($nick, $chan, "IRC");
}

sub onPart {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->args;
        $seen{$nick} = time();
        $seenmsg{$nick} = "$nick was last seen leaving us with the message: $text";
	ilogger("$nick has left $chan");
}

sub onTopic {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->{args}[0];
	my $chan=$event->to;
	if ($chan eq "") {
	  return 0;
	}
        $seen{$nick} = time();
        $seenmsg{$nick} = "$nick was last seen in $chan changing the topic to: $text!";
	ilogger("$nick has changed the topic to $text");
	&tell($nick, $chan, "IRC");
}

sub onQuit {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $text=$event->{args}[0];
        $seen{$nick} = time();
        $seenmsg{$nick} = "$nick was last seen quitting with message: $text!";
	ilogger("$nick has quit");
}

sub onKick {
        my ($conn, $event)=@_;
        my $nick=$event->nick;
        my $victim = ($event->to)[0];
        my $chan=$event->{args}[0];
	foreach(@{$alist{"gottahurt"}}) {
	  if ($_ eq $chan) {
	    $conn->privmsg($chan, "That's gotta hurt!");
	  }
	}
        $seen{$victim} = time();
        $seenmsg{$victim} = "$victim was last seen getting their arse kicked from $chan by $nick.  They were probably asking for it!";
	ilogger("$victim was kicked from $chan by $nick");
}

sub onInvite {
        my $connect = shift;
	my $event = shift;
	foreach (@{$event->{args}}) {
	  $connect->join($_);
	}
}

sub onInviteonlychan {
	my $connect = shift;
        my $event = shift;
	my $invitechan = @{$event->{args}}[1];
	my $msg = "invite $ircnick $invitechan";
	$conn->privmsg('BOfH', $msg);
	$conn->privmsg('ChanServ', $msg);
}

sub onNoinvite {
	my $connect = shift;
        my $event = shift;
}

sub onDisconnect {
	$irc_connected = "no";
	slogger("We go disconnected from the IRC server");
}

sub Message {
	my $nick = shift;
	my $text = shift;
	my $source = shift;
	if ($nick=~m/.+@.+/) {
	  my $msg = Net::Jabber::Message->new();
          $msg->SetMessage(    "to"   => "$nick",
                               "type" => "chat",
                               "body" => "$text" );
          $Con->Send($msg);
	} else {
	  $conn->privmsg($nick, $text);
	}
}

sub onRemind {
	my $remindself = shift;
	my $remindnick = shift;
	my $remindchan = shift;
	my $remindexec = shift;
	$remindcount{$remindnick}--;
	$conn->privmsg($remindchan, "$remindnick, you asked me to remind you: $remindexec");
}

sub tell {
	my $nick = shift;
	my $chan = shift;
	my $source = shift;
	$lnick = $nick;
	$lnick =~ tr/[A-Z]/[a-z]/;
	foreach(@{$tellmsg{$lnick}}) {
	  Message($chan, "$nick: $_", $source);
	}
	delete $tellmsg{$lnick};
	write_tf();
}

sub slogger {
	$message = shift;
	($sec, $min, $hour, $day, $month, $year) = (localtime())[0..5];
        $month = $month + 1;
        $year = $year + 1900;
	print SLOG "$day/$month/$year $hour:$min:$sec - $message\n";
}

sub ilogger {
	$nick = shift;
	$message = shift;
	($sec, $min, $hour) = (localtime())[0..2];
	$nick = "&lt;" . $nick . "&gt;";
	print ILOG "$hour:$min:$sec $nick $message\n";
}

sub shed_rotate {
	($sec, $min, $hour, $day, $month, $year) = (localtime())[0..5];
	$timenow = timelocal($sec, $min, $hour, $day, $month, $year);
	$midnight = timelocal(0, 0, 0, $day, $month, $year);
	$midnight = $midnight * 60 * 60 * 24;
	$secmidnight = $midnight - $timenow;
	$conn->schedule($secmidnight, \&log_rotate);
}

sub log_rotate {
	close SLOG;
	close ILOG;
	($lday, $lmonth, $lyear) = (localtime())[3..5];
	$lmonth = $lmonth + 1;
	$lyear = $lyear + 1900;
	$lfile = $lyear . $lmonth . $lday;
	open(SLOG, ">>syslogs/$lfile");
	open(ILOG, ">>irclogs/$lfile");
}

sub check_news {
	$doc = get $breakingnewsfeed;
	if ($doc ne "") {
	  my $news = $xs1->XMLin($doc);
	  $headline = $news->{story}->{headline};
	  if ($headline ne $oldheadline) {
	    foreach(@{$alist{"breaking_news"}}) {
	      $conn->privmsg($_, "Breaking news: $headline");
	    }
	    $oldheadline = $headline;
	  }
	}
        $doc = get $tflurl;
	if ($doc ne "") {
	  my $transport = $xs1->XMLin($doc);
	  foreach $line (keys %{$transport->{lines}->{line}}) {
	    $stype = $transport->{lines}->{line}->{$line}->{statustype};
	    $stext = $transport->{lines}->{line}->{$line}->{statustext};
	    $line=~s/&amp;/&/;
            if ($tfl{$line} ne $stext) {
	      foreach (@{$tflalert{$line}}) {
	        $conn->privmsg($_, "The $line line is now: $stext");
	      }
	      foreach(@{$alist{"tfl"}}) {
                $conn->privmsg($_, "The $line line is now: $stext");
	      }
            }
            $tfl{$line} = $stext;
	  }
	}
	($sec, $min, $hour, $day, $month, $year) = (localtime())[0..5];
	$nextnews = timelocal($sec, $min, $hour, $day, $month, $year) + 600;
}

sub load_group {
	open(GF, $groupfile);
	%grp = "";
	while(<GF>) {
	  ($group, $users) = $_=~/(.+):(.+)/;
	  @{$grp{$group}} = split(/,/, $users);
	}
	close GF;
}

sub check_group {
	my $sgroup = shift;
	my $user = shift;
	my $a = 0;
	foreach (@{$grp{$sgroup}}) {
	  if ($user eq $_) {
	    $a=1;
	  }
	}
	foreach (@{$grp{"admin"}}) {
	  if ($user eq $_) {
	    $a=1;
	  }
	}
	if ($a == 1 && $nickauth{$user} ne "") {
	  return "true";
	}
	return "false";
}

sub tiny {
	my $url = shift;
	my $doc = get $tinyurl . $url;
	return $doc;
}

sub dec2dot {
  my $address = shift;
  my $ad = $address % 256; $address -= $ad; $address /= 256;
  my $ac = $address % 256; $address -= $ac; $address /= 256;
  my $ab = $address % 256; $address -= $ab; $address /= 256;
  my $aa = $address;

  return "$aa.$ab.$ac.$ad";
}

sub get_moon {
  my $curtimemoon = time();
  my $curdiffmoon = $curtimemoon - $lasttimemoon;
  if ($curdiffmoon > 28800 || $moonstate eq "") {
    my $moon = get $moonfeed;
    my $moonxml = $xs1->XMLin($moon);
    $moonstate = $moonxml->{channel}->{item}->{title};
    $lasttimemoon = time();
  }
  return "The current moon cycle is $moonstate";
}

sub stats {
  my $chan = shift;
  my $report = shift;
  my $source = shift;
  
  if ($report eq "wiki") {
    my $dberr = 0;
    my $mysql_select = "SELECT * FROM (SELECT rev_user, rev_user_text, COUNT(rev_user) as total FROM revision GROUP BY rev_user ORDER BY total DESC) as x WHERE total > 10";
    my $dbh = DBI->connect("DBI:mysql:database=$dbwikidb;host=$dbhost", $dbuser, $dbpass, {'RaiseError' => 0}) or $dberr = 1;
    if ($dberr == 0) {
      $sth = $dbh->prepare($mysql_select);
      $sth->execute or $dberr = 1;
    }

    if ($dberr == 0) {
      while (my ($rev_user, $rev_user_text, $rev_user_count) = $sth->fetchrow_array()) {
        Message($chan, "User: $rev_user_text Number of edits: $rev_user_count", $source);
      }
    } else {
      Message($chan, "Sorry, an error occured", $source);
    }
  }

  if ($report eq "phonedb") {
    my $dberr = 0;
    my $mysql_select = "SELECT createdby, COUNT(createdby) FROM payphones GROUP BY createdby ORDER BY COUNT(createdby) DESC";
    my $dbh = DBI->connect("DBI:mysql:database=$dbphonedb;host=$dbhost", $dbuser, $dbpass, {'RaiseError' => 0}) or $dberr = 1;
    if ($dberr == 0) {
      $sth = $dbh->prepare($mysql_select);
      $sth->execute or $dberr = 1;
    }

    if ($dberr == 0) {
      while (my ($cby, $cbycount) = $sth->fetchrow_array()) {
        $cbycountindex{$cby} = $cbycount;
      }
    }

    $mysql_select = "SELECT updateby, COUNT(updateby) FROM payphones_archive GROUP BY updateby ORDER BY COUNT(updateby) DESC";
    my $dbh = DBI->connect("DBI:mysql:database=$dbphonedb;host=$dbhost", $dbuser, $dbpass, {'RaiseError' => 0}) or $dberr = 1;
    if ($dberr == 0) {
      $sth = $dbh->prepare($mysql_select);
      $sth->execute or $dberr = 1;
    }

    if ($dberr == 0) {
      my ($uby, $ubycount);
      while (($uby, $ubycount) = $sth->fetchrow_array()) {
        $ubycountindex{$uby} = $ubycount;
      }
      foreach $key (keys %ubycountindex) {
        my $edits = $ubycountindex{$key} - $cbycountindex{$key};
        Message($chan, "User: $key Number of phones added: $cbycountindex{$key} Number of edits: $edits", $source);
      }
    } else {
      Message($chan, "Sorry, an error occured", $source);
    }
    undef %cbycountindex;
    undef %ubycountindex;
  }

  if ($report eq "svn") {
    my $revlook = SVN::SVNLook->new(repo => $path, cmd => $svnbin);
    my $rev = $revlook->youngest();

    for ($i = 1; $i <= $rev; $i++) {
      my ($author, $date, $msg) = $revlook->info(revision => $i);
      $authcount{$author}++;
    }

    foreach $key (keys %authcount) {
      Message($chan, "User: $key Commits: $authcount{$key}", $source);
    }
  }
}

sub vir {
  my $chan = shift;
  my @var = @_;
  my ($unit, $amps, $volts, $ohms);
  foreach (@var) {
    my ($val, $unit) = $_=~/([0-9]+)([AKMRV])/;
    if ($unit eq "A") {
      $amps = $val;
    } elsif ($unit eq "V") {
      $volts = $val;
    } elsif ($unit eq "R") {
      $ohms = $val;
    } elsif ($unit eq "K") {
      $ohms = $val * 1000;
    } elsif ($unit eq "M") {
      $ohms = $val * 1000000;
    }
    if ($amps ne "" && $volts ne "" && $ohms eq "") {
      $ohms = $volts / $amps;
      if ($ohms >= 1000000) {
        $ohms = $ohms / 1000000 . "M";
      } elsif ($ohms >= 1000) {
        $ohms = $ohms / 1000 . "K";
      } else {
        $ohms = $ohms . "R";
      }
      Message($chan, $ohms, '');
    }
    if ($amps ne "" && $volts eq "" && $ohms ne "") {
      $volts = $amps * $ohms . "V";
      Message($chan, $volts, '');
    }
    if ($amps eq "" && $volts ne "" && $ohms ne "") {
      $amps = $volts / $ohms . "A";
      Message($chan, $amps, '');
    }
  }
}

sub bot_help {
  my $chan = shift;
  my $text = shift;
  if ($text=~/^!help$/) {
    $text = "!help !main";
  }
  if (my ($cmd) = $text=~/^!help (![a-z0-9]+)/) {
    my $a = 0;
    open(HF, "<$helpfile");
    while(<HF>) {
      my ($cmd1, $helpline) = $_=~/(![a-z0-9 ]+):(.*)/;
      if ($cmd eq $cmd1) {
        $a++;
        Message($chan, $helpline, '');
      }
    }
    close(HF);
    if ($a == 0) {
      Message($chan, "Sorry, there is no help on this topic", '');
    }
  }
}

sub check_eq {
  my $eqfile = get $eqfeed;
  my $eqxml = $xs1->XMLin($eqfile);

  foreach $key (keys %{$eqxml->{entry}}) {
    my $eqelev = $eqxml->{entry}->{$key}->{'georss:elev'};
    my $eqlatlon = $eqxml->{entry}->{$key}->{'georss:point'};
    my $eqtitle = $eqxml->{entry}->{$key}->{title};
    my $equpdate = $eqxml->{entry}->{$key}->{updated};

    my $eqelevkm = $eqelev / 1000;
    my ($eqlat, $eqlong) = $eqlatlon=~/(-{0,1}[0-9]+\.[0-9]*) (-{0,1}[0-9]+\.[0-9]*)/;
    my ($eqmag, $eqmsg) = $eqtitle=~/M ([0-9]+.[0-9]+), (.*)/;
    my ($eqyear, $eqmonth, $eqday, $eqhour, $eqmin, $eqsec) = $equpdate=~/([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+)/;
    #$eqmonth = $eqmonth - 1;
    my $equnixtime = timelocal($eqsec, $eqmin, $eqhour, $eqday, $eqmonth - 1, $eqyear);
    
    if ($equnixtime > $eqgttime && $eqmag >= 6.0) {
      foreach(@{$alist{"eq"}}) {
        Message($_, "There was an earthquake of magnitude $eqmag at: $eqmsg (Lat: $eqlat Long: $eqlong Elevation: $eqelev km) at $eqhour:$eqmin:$eqsec on $eqday/$eqmonth/$eqyear\n", "IRC");
      }
      if ($equnixtime > $eqtmtime) {
        $eqtmtime = $equnixtime;
      } 
    }
  }
  $eqgttime = $eqtmtime;
}

sub write_ff {
	open(FF, ">jam-features");
	my $join = 'Join:';
	foreach (@jchan) {
	  $join = $join . $_ . ",";
	}
	chop $join;
	print FF "$join\n";
	foreach $key (keys %alist) {
	  my $line = $key . ":";
	  foreach(@{$alist{$key}}) {
	    $line = $line . $_ . ",";
	  }
	  chop $line;
	  print FF "$line\n";
	}
        foreach $key (keys %dlist) {
          my $line = $key . ":";
          foreach(@{$dlist{$key}}) {
            $line = $line . $_ . ",";
          }
	  chop $line;
          print FF "$line\n";
        }
	close FF;
}

sub write_tf {
	open(TF, ">jam-tell");
	foreach $key (keys %tellmsg) {
	  foreach(@{$tellmsg{$key}}) {
	    print TF "$key:$_\n";
	  }
	}
	close TF;
}

sub irc_connect {
	$irc = new Net::IRC;
  	$conn = $irc->newconn(Nick 	=> $ircnick,
                        Server		=> $ircserver,
                        Port    	=> $ircport,
                        Ircname 	=> $ircname,
                        Username	=> $ircuser,
			Password	=> $ircpass,
	);
	$irc_connected = "yes";
	$conn->add_handler('msg', \&onMsg);
	$conn->add_handler('public', \&onRequest);
	$conn->add_handler('376', \&onConnect);
	$conn->add_handler('422', \&onConnect);
	$conn->add_handler('307', \&onIsRegistered);
	$conn->add_handler('join', \&onJoin);
	$conn->add_handler('nick', \&onNick);
	$conn->add_handler('part', \&onPart);
	$conn->add_handler('topic', \&onTopic);
	$conn->add_handler('quit', \&onQuit);
	$conn->add_handler('kick', \&onKick);
	$conn->add_handler('invite', \&onInvite);
	$conn->add_handler('inviteonlychan', \&onInviteonlychan);
	$conn->add_handler('noinvite', \&onNoinvite);
	$conn->add_handler('disconnect', \&onDisconnect);
}

sub jabber_connect {
	slogger("Attempting to connect to Jabber server $jabberserver");
	$Con = new Net::Jabber::Client();
	$Con->Connect(hostname => $jabberserver);

	if ($Con->Connected()) {
	  $jabber_connected = "yes";
    	  slogger("We are connected to Jabber server $jabberserver");
	}

	$Con->SetCallBacks("message" => \&InMessage,
          "presence" => \&InPresence,
          "iq" => \&InIQ
        );

	my @result = $Con->AuthSend(username => $jabberuser,
          password => $jabberpass,
          resource => "Bot"
        );

	$Con->PresenceSend();

	$Con->Process();

	%roster = $Con->RosterGet();
	foreach $key (keys %roster) {
  	  push(@jabberroster, $key);
	}
}

sub REAPER {
	1 until (-1 == waitpid(-1,WNOHANG));
	$SIG{CHLD} = \&REAPER;
}

sub call_socket {
        $pid = fork();
        if ($pid == 0) {
          select $fl;
          while (1) {
	    ($sec, $min, $hour, $day, $month, $year) = (localtime())[0..5];
	    $currenttime = timelocal($sec, $min, $hour, $day, $month, $year);
	    if ($irc_connected eq "yes") {
              $irc->do_one_loop();
	    } else {
	      irc_connect();
	    }
	    if ($jabber_connected eq "yes") {
	      if (!defined($Con->Process(2))) {
	        $jabber_connected = "no";
	      }
	    } else {
	      jabber_connect();
	    }
	    print DE "$data\n";
	    if ($currenttime >= $nextnews) {
	      &check_news();
	      &check_eq();
	    }
          }
        } else {
          $server = IO::Socket::INET->new( Proto     => 'tcp',
                                           LocalAddr => 'localhost',
                                           LocalPort => $port,
                                           Listen    => SOMAXCONN,
                                           Reuse     => 1);

          die "can't setup server" unless $server;

          $SIG{CHLD}= \&REAPER;

          while ($client = $server->accept()) {
            $pid1 = fork();
            die "fork: $!" unless defined $pid1;
            $client->autoflush(1);
            if ($pid1 != 0 ) {
	    $a = 0;
              while (<$client>) {
	        $msg = $_;
	        if ((($user, $pass) = $_=~/^USER ([a-zA-Z0-9_-|^]+) PASS (.*)$/) && $a == 0) {
	          chop($pass);
	          if (my $krb = $kerberos->authenticate($user, $pass)) { 
	            $a = 1;
	            print $client "+OK\n";
	          } else { 
	            $conn->privmsg("BOfH", "Kerberos debug: $krb");
	            print $client "-ERR\n";
	          }
	        } elsif ((($mchan) = $msg=~/^TALK (#[A-Za-z0-9-|_]+)\r$/) && $a == 1) { 
	          $nchan = $mchan;
	          $a=2;
	          print $client "+OK\n";
	        } elsif ($msg=~/^\r$/ && $a == 2) {
	          $a=1;
	          print $client "+OK\n";
	        } elsif ($a == 2) {
	          $conn->privmsg($nchan, $_);
	        } else {
	          $conn->privmsg("BOfH", "Fail!");
	          print $client "-ERR\n";
	        }
	      close(DF);
              }
	      close($client);
              exit;
            }
          }
          close (SERVER);
        }
}

call_socket();
