# This code is a part of Slash, and is released under the GPL.
# Copyright 1997-2001 by Open Source Development Network. See README
# and COPYING for more information, or see http://slashcode.com/.
# $Id: Install.pm,v 1.2.2.28 2001/10/29 20:41:24 pudge Exp $

package Slash::Install;
use strict;
use DBIx::Password;
use Slash;
use Slash::DB;
use File::Copy;
use File::Find;
use File::Path;
use vars qw($VERSION);
use base 'Slash::DB::Utility';

# BENDER: Like most of life's problems, this one can be solved with bending.

($VERSION) = ' $Revision: 1.2.2.28 $ ' =~ /\$Revision:\s+([^\s]+)/;

sub new {
	my($class, $user) = @_;
	my $self = {};
	bless($self, $class);
	$self->{virtual_user} = $user;
	$self->sqlConnect;
	$self->{slashdb} = Slash::DB->new($user);

	return $self;
}

sub create {
	my($self, $values) = @_;
	$self->sqlInsert('site_info', $values);
}

sub delete {
	my($self, $key) = @_;
	my $sql = "DELETE from site_info WHERE name = " . $self->sqlQuote($key);
	$self->sqlDo($sql);
}

sub deleteByID  {
	my($self, $key) = @_;
	my $sql = "DELETE from site_info WHERE param_id=$key";
	$self->sqlDo($sql);
}

sub get {
	my($self, $key) = @_;
	my $count = $self->sqlCount('site_info', "name=" . $self->sqlQuote($key));
	my $hash;
	if ($count > 1) {
		$hash = $self->sqlSelectAllHashref('param_id', '*', 'site_info', "name=" . $self->sqlQuote($key));
	} else {
		$hash = $self->sqlSelectHashref('*', 'site_info', "name=" . $self->sqlQuote($key));
	}

	return $hash;
}

sub exists {
	my($self, $key, $value) = @_;
	return unless $key;
	my $where;
	$where .= "name=" . $self->sqlQuote($key);
	$where .= " AND value=" . $self->sqlQuote($value) if $value;
	my $count = $self->sqlCount('site_info', $where);

	return $count;
}

sub getValue {
	my($self, $key) = @_;
	my $count = $self->sqlCount('site_info', "name=" . $self->sqlQuote($key));
	my $value;
	unless ($count > 1) {
		($value) = $self->sqlSelect('value', 'site_info', "name=" . $self->sqlQuote($key));
	} else {
		$value = $self->sqlSelectColArrayref('value', 'site_info', "name=" . $self->sqlQuote($key));
	}

	return $value;
}

sub getByID {
	my($self, $id) = @_;
	my $return = $self->sqlSelectHashref('*', 'site_info', "param_id = $id");

	return $return;
}

sub readTemplateFile {
	my($self, $filename) = @_;
	return unless -f $filename;
	my $fh = gensym;
	open($fh, "< $filename\0") or die "Can't open $filename to read from: $!";
	my @file = <$fh>;
	my %val;
	my $latch;
	for (@file) {
		if (/^__(.*)__$/) {
			$latch = $1;
			next;
		}
		$val{$latch} .= $_ if $latch;
	}
	$val{'tpid'} = undef if $val{'tpid'};
	for (qw| name page section lang seclev description title template |) {
		chomp($val{$_}) if $val{$_};
	}

	return \%val;
}

sub writeTemplateFile {
	my($self, $filename, $template) = @_;
	my $fh = gensym;
	open($fh, "> $filename\0") or die "Can't open $filename to write to: $!";
	for (qw(section description title page lang name template seclev)) { #(keys %$template) {
		next if ($_ eq 'tpid');
		print $fh "__${_}__\n";
		$template->{$_} =~ s/\015\012/\n/g;
		print $fh "$template->{$_}\n";
	}
	close $fh;
}

sub installTheme {
	my($self, $answer, $themes, $symlink) = @_;
	$themes ||= $self->{'_themes'};

	$self->_install($themes->{$answer}, $symlink);
}

sub installThemes {
	my($self, $answers, $themes, $symlink) = @_;
	$themes ||= $self->{'_themes'};

	for my $answer (@$answers) {
		for (keys %$themes) {
			if ($answer eq $themes->{$_}{order}) {
				$self->_install($themes->{$_}, $symlink, 0);
			}
		}
	}
}

sub installPlugin {
	my($self, $answer, $plugins, $symlink) = @_;
	$plugins ||= $self->{'_plugins'};

	$self->_install($plugins->{$answer}, $symlink, 1);
}

sub installPlugins {
	my($self, $answers, $plugins, $symlink) = @_;
	$plugins ||= $self->{'_plugins'};

	for my $answer (@$answers) {
		for (keys %$plugins) {
			if ($answer eq $plugins->{$_}{order}) {
				$self->_install($plugins->{$_}, $symlink, 1);
			}
		}
	}
}

sub _install {
	my($self, $hash, $symlink, $flag) = @_;
	# Yes, performance wise this is questionable, if getValue() was
	# cached.... who cares this is the install. -Brian
	if ($self->exists('hash', $hash->{name})) {
		print STDERR "Plugin $hash->{name} has already been installed\n";
		return;
	}
	if ($flag) {
		return if $self->exists('plugin', $hash->{name});

		$self->create({
			name            => 'plugin',
			value           => $hash->{'name'},
			description     => $hash->{'description'},
		});
	} else {
		# not sure if this is what we want, but leave it
		# in until someone complains.  really, we should
		# have reinstall theme/plugin methods or
		# something.  -- pudge
		return if $self->exists('theme', $hash->{name});

		$self->create({
			name            => 'theme',
			value           => $hash->{'name'},
			description     => $hash->{'description'},
		});
	}
	my $hostname = $self->getValue('basedomain');
	my $email = $self->getValue('adminmail');
	my $driver = $self->getValue('db_driver');
	my $prefix_site = $self->getValue('site_install_directory');

	my %stuff = ( # [relative directory, executable]
		htdoc		=> ["htdocs",			1],
		htdoc_code	=> ["htdocs/code",		0],
		htdoc_faq	=> ["htdocs/faq",		0],
		sbin		=> ["sbin",			1],
		image		=> ["htdocs/images",		0],
		image_award	=> ["htdocs/images/awards",	0],
		image_banner	=> ["htdocs/images/banners",	0],
		topic		=> ["htdocs/images/topics",	0],
		task		=> ["tasks",			1],
		misc		=> ["misc",			1],
	);

	for my $section (keys %stuff) {
		next unless exists $hash->{$section} && @{$hash->{$section}};
		my $instdir = "$prefix_site/$stuff{$section}[0]";
		mkpath $instdir, 0, 0755;

		for (@{$hash->{$section}}) {
			(my $filename = $_) =~ s/^.*\/(.*)$/$1/;
			my $old = "$hash->{dir}/$_";
			my $new = "$instdir/$filename";

			# I don't think we should delete the file first,
			# but it is a thought. -- pudge
			# unlink $new;
			if ($symlink) {
				symlink $old, $new;
			} else {
				copy $old, $new;
				my $mode = $stuff{$section}[1] ? 0755 : 0644;
				chmod $mode, $new;
			}
		}
	}

	my($sql, @sql, @create);

	if ($hash->{"${driver}_schema"}) {
		my $schema_file = "$hash->{dir}/" . $hash->{"${driver}_schema"};
		my $fh = gensym;
		if (open($fh, "< $schema_file\0")) {
			while (<$fh>) {
				chomp;
				next if /^#/;
				next if /^$/;
				next if /^ $/;
				push @create, $_;
			}
			close $fh;
		} else {
			warn "Can't open $schema_file: $!";
		}

		$sql = join '', @create;
		@sql = split /;/, $sql;
	}

	if ($hash->{"${driver}_dump"}) {
		my $dump_file = "$hash->{dir}/" . $hash->{"${driver}_dump"};
		my $fh = gensym;
		if (open($fh, "< $dump_file\0")) {
			while (<$fh>) {
				# A theme's dump.sql may wish to override
				# the main defaults.sql, so we should allow
				# REPLACE and UPDATE here. - Jamie
				next unless /^(INSERT|DELETE|REPLACE|UPDATE)\b/i;
				chomp;
				s/www\.example\.com/$hostname/g;
				s/admin\@example\.com/$email/g;
				push @sql, $_;
			}
			close $fh;
		} else {
 			warn "Can't open $dump_file: $!";
 		}
 	}

	for (@sql) {
		next unless $_;
		s/;$//;
		unless ($self->sqlDo($_)) {
			print "Failed on :$_:\n";
		}
	}
	@sql = ();

	if ($hash->{'plugin'}) {
		for (keys %{$hash->{'plugin'}}) {
			$self->installPlugin($_, 0, $symlink);
		}
	}

	if ($hash->{'template'}) {
		for (@{$hash->{'template'}}) {
			my $id;
			my $template = $self->readTemplateFile("$hash->{'dir'}/$_");
			if ($template and ($id = $self->{slashdb}->existsTemplate($template))) {
				$self->{slashdb}->setTemplate($id, $template);
			} elsif ($template) {
				$self->{slashdb}->createTemplate($template);
			} else {
				warn "Can't open template file $_: $!";
			}
		}
	}

	if ($hash->{"${driver}_prep"}) {
		my $prep_file = "$hash->{dir}/" . $hash->{"${driver}_prep"};
		my $fh = gensym;
		if (open($fh, "< $prep_file\0")) {
			while (<$fh>) {
				next unless (/^INSERT/i or /^UPDATE/i or /^DELETE/i or /^REPLACE/i or /^ALTER/i or /^CREATE/i);
				chomp;
				s/www\.example\.com/$hostname/g;
				s/admin\@example\.com/$email/g;
				push @sql, $_;
			}
			close $fh;
		} else {
 			warn "Can't open $prep_file: $!";
 		}
 	}

	for (@sql) {
		next unless $_;
		s/;$//;
		unless ($self->sqlDo($_)) {
			print "Failed on :$_:\n";
		}
	}
	@sql = ();

	if ($hash->{note}) {
		my $file = "$hash->{dir}/$hash->{note}";
		my $fh = gensym;
		if (open($fh, "< $file\0")) {
			print <$fh>;
			close $fh;
		} else {
			warn "Can't open $file: $!";
		}
	}
}

sub getPluginList {
	return _getList(@_, 'plugins', 'PLUGIN');
}

sub getThemeList {
	return _getList(@_, 'themes', 'THEME');
}

sub _getList {
	my($self, $prefix, $subdir, $type) = @_;
	$self->{'_install_dir'} = $prefix;

	my $dh = gensym;
	unless (opendir($dh, "$prefix/$subdir")) {
		warn "Can't opendir $prefix/$subdir: $!";
		return;
	}

	my %hash;
	while (my $dir = readdir($dh)) {
		next if $dir =~ /^\.$/;
		next if $dir =~ /^\.\.$/;
		next if $dir =~ /^CVS$/;
		my $fh = gensym;
		open($fh, "< $prefix/$subdir/$dir/$type\0") or next;
		$hash{$dir}->{'dir'} = "$prefix/$subdir/$dir";
		#This should be overridden by the actual name of the plugin
		$hash{$dir}->{'name'} = $dir;

		my @info;
		{
			local $/;
			@info = split /\015\012?|\012/, <$fh>;
		}

		for (@info) {
			next if /^#/;
			my($key, $val) = split(/=/, $_, 2);
			$key = lc $key;
			if ($key =~ /^(htdoc|htdoc_code|htdoc_faq|template|image|image_award|image_banner|task|sbin|misc|topic)s?$/) {
				push @{$hash{$dir}->{$key}}, $val;
			} elsif ($key =~ /^(plugin)s?$/) {
				$hash{$dir}->{plugin}{$val} = 1;
			} else {
				$hash{$dir}->{$key} = $val;
			}
		}
	}
	my $x = 0;
	for (sort keys %hash) {
		$x++;
		$hash{$_}->{'order'} = $x;
	}

	$self->{"_" . $subdir} = \%hash;
	return \%hash;
}

sub reloadArmors {
	my($self, $armors) = @_;
	my $count = 0;

	$self->sqlDo('DELETE FROM spamarmors');
	for (@{$armors}) {
		$_->{'-armor_id'} = 'null';
		$self->sqlInsert('spamarmors', $_) && $count++;
	}

	return $count;
}

1;

__END__

=head1 NAME

Slash::Install - Install libraries for slash

=head1 SYNOPSIS

	use Slash::Install;

=head1 DESCRIPTION

This was deciphered from crop circles.

=head1 SEE ALSO

Slash(3).

=cut
