#!/usr/bin/perl
#

my @errors;
my $err = sub {
    return unless @_;
    die "Problem:\n" . join('', map { "  * $_\n" } @_);
};

my %dochecks;   # these are the ones we'll actually do
my @checks = (  # put these in the order they should be checked in
    "modules", 
    "modules_print", 
    "env", 
    "database" 
);
foreach my $check (@checks) { $dochecks{$check} = 1; }

my $only = 0;

arg: foreach my $arg (@ARGV) {
    ($w, $c) = ($arg =~ /^-(no|only)(.*)/) or die "unknown option $arg";
    die "only one '-onlyfoo' option may be specified" if $w eq "only" and $only++;
    foreach my $check (@checks) {
        if ($check eq $c) {
            if ($w eq "only") { %dochecks = ( $check => 1 ); }
            else { $dochecks{$check} = 0 }
            next arg;
        }
    }
    die "unknown check '$c' (known checks: " . join(", ", @checks) . ")\n";
}

my %modules = (
               "DBI" => { 'deb' => 'libdbi-perl',  },
               "DBD::mysql" => { 'deb' => 'libdbd-mysql-perl', },
               "Digest::MD5" => { 'deb' => 'libdigest-md5-perl', },
               "Digest::SHA1" => { 'deb' => 'libdigest-sha1-perl', },
               "Image::Size" => { 'deb' => 'libimage-size-perl', },
               "MIME::Lite" => { 'deb' => 'libmime-lite-perl', },
               "MIME::Words" => { 'deb' => 'libmime-perl', },
               "Compress::Zlib" => { 'deb' => 'libcompress-zlib-perl', },
               "Net::SMTP" => {
                   'deb' => 'libnet-perl',
                   'opt' => "Alternative to piping into sendmail to send mail.",
               },
               "Net::DNS" => {
                   'deb' => 'libnet-dns-perl',
               },
               "MIME::Base64" => { 'deb' => 'libmime-base64-perl' },
               "URI::URL" => { 'deb' => 'liburi-perl' },
               "HTML::Tagset" => { 'deb' => 'libhtml-tagset-perl' },
               "HTML::Parser" => { 'deb' => 'libhtml-parser-perl', },
               "LWP::Simple" => { 'deb' => 'libwww-perl', },
               "LWP::UserAgent" => { 'deb' => 'libwww-perl', },
               "GD" => { 'deb' => 'libgd-perl' },
               "GD::Graph" => {
                   'deb' => 'libgd-graph-perl',
                   'opt' => 'Required to make graphs for the statistics page.',
               },
               "Mail::Address" => { 'deb' => 'libmailtools-perl', },
               "Proc::ProcessTable" => {
                   'deb' => 'libproc-process-perl',
                   'opt' => "Better reliability for starting daemons necessary for high-traffic installations.",
               },
               "RPC::XML" => {
                   'deb' => 'librpc-xml-perl',
                   'opt' => 'Required for outgoing XMLRPC support',
               },
               "SOAP::Lite" => {
                   'deb' => 'libsoap-lite-perl',
                   'opt' => 'Required for XML-RPC support.',
               },
               "Unicode::MapUTF8" => { 'deb' => 'libunicode-maputf8-perl', },
               "Storable" => {
                   'deb' => 'libstorable-perl',
               },
               "XML::RSS" => {
                   'deb' => 'libxml-rss-perl',
                   'opt' => 'Required for retrieving RSS off of other sites (syndication).',
               },
               "XML::Simple" => {
                   'deb' => 'libxml-simple-perl',
                   'ver' => 2.12,
               },
               "String::CRC32" => {
                   'deb' => 'libstring-crc32-perl',
                   'opt' => 'Required for palette-altering of PNG files.  Only necessary if you plan to make your own S2 styles that use PNGs, not GIFs.',
               },
               "Time::HiRes" => { 'deb' => 'libtime-hires-perl' },
               "IO::WrapTie" => { 'deb' => 'libio-stringy-perl' },
               "XML::Atom" => {
                   'deb' => 'libxml-atom-perl',
                   'opt' => 'Required for AtomAPI support.',
               },
               "Math::BigInt::GMP" => {
                   'opt' => 'Aides Crypt::DH so it isn\'t crazy slow.',
               },
               "URI::Fetch" => {
                   'opt' => 'Required for OpenID support.',
               },
               "Crypt::DH" => {
                   'opt' => 'Required for OpenID support.',
               },
               );

sub check_modules_print {
  print "[Printing required Perl Modules...]\n";
  foreach my $mod (sort keys %modules) {
    print $mod .
      ($modules{$mod}->{'ver'} ? " >= " . $modules{$mod}->{'ver'} : "") .
      ($modules{$mod}->{'opt'} ? " -- " . $modules{$mod}->{'opt'} : "") .
      "\n";
  }
}

sub check_modules {
    print "[Checking for Perl Modules....]\n";

    my @debs;

    foreach my $mod (sort keys %modules) {
        my $rv = eval "use $mod;";
        if ($@) {
            my $dt = $modules{$mod};
            if ($dt->{'opt'}) {
                print STDERR "Missing optional module $mod: $dt->{'opt'}\n";
            } else {
                push @errors, "Missing perl module: $mod";
            }
            push @debs, $dt->{'deb'} if $dt->{'deb'};
            next;
        }

        my $ver_want = $modules{$mod}{ver};
        my $ver_got = $mod->VERSION;
        if ($ver_want && $ver_got && $ver_got < $ver_want) {
            push @errors, "Out of date module: $mod (need $ver_want, $ver_got installed)";
        }
    }
    if (@debs && -e '/etc/debian_version') {
        print STDERR "\n# apt-get install ", join(' ', @debs), "\n\n";
    }

    $err->(@errors);
}

sub check_env {
    print "[Checking LJ Environment...]\n";

    $err->("\$LJHOME environment variable not set.")
        unless $ENV{'LJHOME'};
    $err->("\$LJHOME directory doesn't exist ($ENV{'LJHOME'})")
        unless -d $ENV{'LJHOME'};

    # before ljconfig.pl is called, we want to call the site-local checkconfig,
    # otherwise ljconfig.pl might load ljconfig-local.pl, which maybe load
    # new modules to implement site-specific hooks.
    my $local_config = "$ENV{'LJHOME'}/bin/checkconfig-local.pl";
    if (-e $local_config) {
        my $good = eval { require $local_config; };
        exit 1 unless $good;
    }

    $err->("No ljconfig.pl file found at $ENV{'LJHOME'}/cgi-bin/ljconfig.pl")
        unless -e "$ENV{'LJHOME'}/cgi-bin/ljconfig.pl";

    eval { require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl"; };
    $err->("Failed to load ljlib.pl: $@") if $@;

    # if SMTP_SERVER is set, then Net::SMTP is required, not optional.
    if ($LJ::SMTP_SERVER && ! defined $Net::SMTP::VERSION) {
        $err->("Net::SMTP isn't available, and you have \$LJ::SMTP_SERVER set.");
    }
}

sub check_database {
    print "[Checking Database...]\n";

    require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl";
    my $dbh = LJ::get_dbh("master");
    unless ($dbh) {
        $err->("Couldn't get master database handle.");
    }
    foreach my $c (@LJ::CLUSTERS) {
        my $dbc = LJ::get_cluster_master($c);
        next if $dbc;
        $err->("Couldn't get db handle for cluster \#$c");
    }

    if (%LJ::MOGILEFS_CONFIG && $LJ::MOGILEFS_CONFIG{hosts}) {
        print "[Checking MogileFS client.]\n";
        my $mog = LJ::mogclient();
        die "Couldn't create mogilefs client." unless $mog;
    }
}

foreach my $check (@checks) {
    next unless $dochecks{$check};
    my $cn = "check_".$check;
    &$cn;
}
print "All good.\n";
print "NOTE: checkconfig.pl doesn't check everything yet\n";


