#!/usr/bin/perl

use strict;
use warnings;
use lib qw(/root/usr/share/perl/5.12.4/);
use Data::Dumper;
use File::Slurp;
use DBI;
use Proc::ProcessTable;
use MySQL::Config qw(parse_defaults);

## version string
my $omeka='omeka-1.5';

## here are the defined actions
my @actions=('install','reinstall','post_install','upgrade','check_myconf_file');


## common part of configuration for all apache users
my $base_conf='';

## common part of configuration for mysql
my $my_cnf_start='';
$my_cnf_start.="# generated by install_omeka\n"; 
$my_cnf_start.=" [client]\n";

my $action=$ARGV[0];
if(not defined($action)) {
  print "fatal: no action\n";
  exit;
}

## check action
my $valid_action=0;
foreach my $possible_action (@actions) {
  if($action eq $possible_action) {
    $valid_action=1;
    last; 
  }
}
if(not $valid_action) {
  print "fatal: '$action' is not a valid action\n";
  exit;
}

## get user_or_file
my $user_or_file=$ARGV[1];
if(not defined($user_or_file)) {
  print "fatal: no user_or_file\n";
  exit;
}

## work with user/password file, used here globally
my $in_file=$ARGV[1];

## if just for checking don't do anything else 
if(-f $user_or_file and $action eq 'check_myconf_file') {
  &check_mysql_passwords($in_file);
  exit;
}

## some serious work

## main db open
my $dbh=&open_mysql();
## download the omeka zip file
my $url='http://omeka.org/files/'.$omeka.'.zip';
if(not -f "/tmp/$omeka.zip") {
  system("lynx -source $url > /tmp/$omeka.zip");
}
else {
  print "found /tmp/$omeka.zip, not downloading\n";
}

if(defined($user_or_file) and -f $user_or_file) {
  ## read the passwords file, # are comments in it
  my $passwords=&get_passwords($user_or_file) or die;
  #print Dumper $passwords;
  if($action eq 'install') {
    &install_for_all($passwords);    
  }
  elsif($action eq 'post_install') {
    ## passwords are actually not required for the post_install 
    &post_install_for_all($passwords);
  }
  else {
    print "I am not sure what to do with the file $user_or_file and action '$action'\nFix me.\n";
  }
  ## done 
  exit;
}
## work with user
elsif(-l "/home/$user_or_file/omeka") {
  my $user=$user_or_file;
  my $secret='';
  if($action eq 'reinstall') {
    print "reinstalling for user $user\n";
    my $password=`grep password /home/$user/omeka/db.ini`;
    chomp $password;
    $password=~s|\s*password\s*=\s*["']([^"']+)["']|$1| or die "could not get password for $user\n";
    &remove_omeka_tables($user);
    ## delete files
    system("rm -rf /home/$user/$omeka");
    ## main install 
    system("unzip /tmp/$omeka.zip -d /home/$user > /dev/null");
    system("chown -R $user.$user /home/$user/$omeka");
    &customize_omeka($user,$password);
  }
  elsif($action eq 'upgrade') {
    print "upgrading for user $user\n";
    &upgrade_for_user($user); 
  }
  elsif($action eq 'post_install') {
    print "post_install for user $user\n";
    &post_install_for_user($user); 
  }
  else {
    print "I am not sure what to do with the user $user_or_file and action '$action'\nFix me.\n";
    exit;
  }  
}
else {
  print "nothing to do\n";
}
## just in case ;-)
&restart_web_servers();
$dbh->disconnect;

#### The End 

sub upgrade_for_user {
  my $user=shift;
  my $link=readlink("/home/$user/omeka");
  print "link is $link\n";
  $link=~s|/||g;
  my $installed_version=$link;
  $installed_version=~s|omeka-||;
  print "installed version is $installed_version\n";
  ## sanity check if the current version is newer
  my $current_version=$omeka;
  $current_version=~s|omeka-||;
  if($current_version eq $installed_version) {
    print "current and installed version are both $current_version\n";
    print "I abort\n";
    exit;
  }
  ## main install 
  system("unzip /tmp/$omeka.zip -d /home/$user > /dev/null");
  system("chown -R $user.$user /home/$user/$omeka");
  ## copy relevant directories and files
  foreach my $dir ('plugins','themes','archive') {
    system("rm -r /home/$user/$omeka/$dir");
    system("cp -ar /home/$user/omeka/$dir /home/$user/$omeka/$dir");
  }
  system("cp -ar /home/$user/omeka/db.ini /home/$user/$omeka");
  system("cp -ar /home/$user/omeka/.htaccess /home/$user/$omeka");
  ## set liberal permissions on the archiev
  system("chmod -R 777 /home/$user/$omeka/archive");
  ## delete the install directory 
  system("rm -r /home/$user/$omeka/install");
  ## make a new symlink with the updated omeka version
  &make_or_renew_apache_config_file($user);
  ## make symlink in the home directory 
  system("cd /home/$user ; ln -s $omeka omeka");
  ## renew the web link for the user, I don't know why
  &make_or_renew_web_link_for_user($user);
  ## set new symlink
  system("rm /home/$user/omeka");
  system("ln -s /home/$user/$omeka /home/$user/omeka");
}


sub remove_omeka_tables {
  my $user=shift;
  $dbh->do("use $user;");
  my $sth_show=$dbh->prepare("show tables");
  my $rv=$sth_show->execute();
  while(my $row = $sth_show->fetchrow_arrayref) {
    my $table=$row->[0];
    if(not $table=~m|^omeka_|) {
      next;
    }
    $dbh->do("drop table $table");
  }
}

## this was sites-avaiblable/omeka
my $apache_conf_dir="/etc/apache2/conf.d/";
if(-d "/etc/apache2" and not -d $apache_conf_dir) {
  system("mkdir $apache_conf_dir");
  system("ln -s $apache_conf_dir /etc/apache2/sites-enabled/omeka");
}

sub install_for_all {
  my $passwords=shift;
  ## main function, installation for all users
  foreach my $user (keys %{$passwords}) {
    my $secret=$passwords->{$user};
    ## is this an upgrade?
    my $upgrade=0;
    if(-l "/home/$user/omeka") {
      $upgrade=1;
    }
    &install_omeka($user,$secret,$upgrade);
  }
  $dbh->disconnect;
  ## restart apache
  &restart_web_servers();
}



sub post_install_for_all {
  my $passwords=shift;
  ## main function, installation for all users
  foreach my $user (keys %{$passwords}) {
    my $secret=$passwords->{$user};
    &post_install_for_user($user);
  }
  $dbh->disconnect;
  ## restart apache
  &restart_web_servers();
}




## install omeka for a user
sub install_omeka {
  my $user=shift;
  my $secret=shift;
  my $upgrade=shift;
  print "installing for user '$user'\n";
  if(-d "/home/$user/$omeka") {
    print "/home/$user/$omeka is already there, skipping\n";
    return;
  }
  ## main install 
  system("unzip /tmp/$omeka.zip -d /home/$user > /dev/null");
  system("chown -R $user.$user /home/$user/$omeka");
  ## create the database on creation, or copy old datafiles
  if(not $upgrade) {
    ## add a link to a student's www home, to enable it
    if(not -l "/var/www/home/$user") {
      system("ln -s /home/$user/public_html /var/www/home/$user");
    }
    $dbh->do("CREATE DATABASE $user;");
    $dbh->do("grant all on $user.* to '$user'\@'localhost' identified by '$secret' with grant option;");
    ## while not strictly necessary, let us create a .my.cnf
    my $my_cnf_file="/home/$user/.my.cnf";
    my $my_cnf=$my_cnf_start;
    $my_cnf.="user     = $user\n";
    $my_cnf.="password = $secret\n";
    write_file($my_cnf_file,$my_cnf);
    system("chown $user.$user $my_cnf_file");
    system("chmod 600 $my_cnf_file");
  }
  else {
    ## copy old archive
    system("cp -ra /home/$user/omeka/archive/ /home/$user/$omeka/.");
    ## remove old symlink
    system("rm -r /home/$user/omeka");
  }
  &customize_omeka($user,$secret);
  &make_or_renew_apache_config_file($user);
  ## make symlink in the home directory 
  system("cd /home/$user ; ln -s $omeka omeka");
  &make_or_renew_web_link_for_user($user);
}  

## make web symlink for omeka, I don't know why we need this
sub make_or_renew_web_link_for_user {
  my $user=shift;
  system("rm -f /var/www/omeka/$user");
  system("cd /var/www/omeka ; ln -s /home/$user/$omeka $user");
}

## make on renew the apache configuration snippet
sub make_or_renew_apache_config_file {
  my $user=shift;  
  ## make apache configuration
  my $conf_dir="/etc/apache2/conf.d";
  my $out_file="$conf_dir/$user".'_omeka';
  if(-f $out_file) {
    unlink $out_file;
  }
  my $conf="Alias /omeka/$user/ /home/$user/$omeka/\n";
  $conf.="<Directory /home/$user/$omeka/>\n";
  $conf.="  Options +FollowSymLinks\n";
  $conf.="  AllowOverride all\n";
  $conf.="  order allow,deny\n";
  $conf.="  allow from all\n";
  $conf.="</Directory>\n";
  &write_file($out_file,$conf);
}

## gets the user/password hash from file
sub get_passwords {
  my $user_or_file=shift;
  my $omeka_conf=read_file($user_or_file);
  my $passwords;
  foreach my $line (split(/\n/,$omeka_conf)) {
    ## hash marks comments
    chomp $line;
    if($line=~m|^\s*#|) {
      print "not dealing with commented password line '$line'\n";
      next;
    }
    ## trim blanks
    $line=~s|^\s+||;
    $line=~s|\s+$||;
    ## case of user space password
    my @data=split(/\s+/,$line);
    if(scalar @data == 2) {
      $passwords->{$data[0]}=$data[1];
    }
    ## case of username only
    else {
      my $user=$line;
      $passwords->{$user}=&get_password_from_my_conf($user,'no_write');
    }
  }
  return $passwords;
}

sub customize_omeka {
  my $user=shift;
  my $secret=shift;
  ## customize the db.ini file for the user
  my $db_file="/home/$user/$omeka/db.ini";
  my $db_config=read_file($db_file); 
  $db_config=~s|host *= *\"[^\"]+\"|host=\"localhost\"|;
  $db_config=~s|username *= *\"[^\"]+\"|username=\"$user\"|;
  $db_config=~s|password *= *\"[^\"]+\"|password=\"$secret\"|;
  $db_config=~s|dbname *= *\"[^\"]+\"|dbname=\"$user\"|;
  write_file($db_file,$db_config);
  ## set up the web permissions
  my ($file,$before,$after);
  ##  this is really horrible!
  system("chmod -R 777 /home/$user/$omeka/archive");
  ##  rewrite base has to change for our setup
  $before='# RewriteBase /';
  $file="/home/$user/$omeka/.htaccess";
  $after="RewriteBase /omeka/$user";
  my $system;
  $system="sed -i 's|$before|$after|' $file";
  #print "$system\n";
  if(-f $file) {
    system($system);
  }
  $before='# RewriteBase /install/';
  $file="/home/$user/$omeka/install/.htaccess";
  $after="RewriteBase /omeka/$user/install";
  $system="sed -i 's|$before|$after|' $file";
  #print "$system\n";
  if(-f $file) {
    system($system);
  }
}


## open mysql
sub open_mysql {
  my %cfg = parse_defaults "my", qw(client);
  my $dbh = DBI->connect("DBI:mysql:", $cfg{'user'}, $cfg{'password'}) or die;
  my $sth = $dbh->prepare("SET collation_connection = utf8_general_ci");
  my $rv = $sth->execute();
  return $dbh;
}

## what web servers?
sub find_web_servers {
  my $t = new Proc::ProcessTable( 'cache_ttys' => 1 );
  my @web_servers=('apache2','lighttpd');
  my $web_servers;
  foreach my $p (@{$t->table}){
    my $prog=$p->exec or next;    
    foreach my $server (@web_servers) {
      if($prog=~m|$server|) {         
        $web_servers->{$server}=1;
      }
    }
  }
  return $web_servers;
}

sub restart_web_servers {
  my $web_servers=shift // '';
  if(not $web_servers) {
    $web_servers=&find_web_servers();
  }
  foreach my $server (keys %$web_servers) {
    system("/etc/init.d/$server restart");
  }
}

## post installation: delete useless table entries
## and the installation directory 
sub post_install_for_user {
  my $user=shift;
  $dbh->do("delete from $user.omeka_elements where element_set_id=6;");
  $dbh->do("delete from $user.omeka_elements where element_set_id=4;");
  $dbh->do("delete from $user.omeka_elements where element_set_id=5;");
  $dbh->do("delete from $user.omeka_record_types where id=3;");
  $dbh->do("delete from $user.omeka_element_sets where id=4;");
  $dbh->do("delete from $user.omeka_element_sets where id=5;");
  $dbh->do("delete from $user.omeka_element_sets where id=6;");
  system("rm -rf /home/$user/omeka/install");
}


## gets passwords from .my.cnf, also used to check it
sub get_password_from_my_conf {
  my $user=shift;
  ## optional argument to prevent writes
  my $no_write=shift;
  my $file="/home/".$user.'/.my.cnf';
  if(not -f $file) {
    print "can not get password from .my.cnf: no such file '$file'\n";
  }
  my $my_conf=read_file($file);
  if($my_conf=~m|password\s*=\s*your_password\s*|) {
    print "user $user did not change the default password\n";
  }
  $my_conf=~s|\s*$|\n|;
  ## fill in the user
  $my_conf=~s|user\s*=\s*\S+\s*\n|user = $user\n|;
  ## remove comments
  $my_conf=~s|^#.*\n\s*||;
  $my_conf=~s|\#.*\n|\n|;
  ## find password
  my $password='';
  $my_conf=~m|password\s*=\s*(\S+)|;
  $password=$1;
  if(not $password) {
    print "no password for $user\n";
    return;
  }
  if(not defined($no_write)) {
    write_file($file,$my_conf);
    system("chmod 600 $file");
  }
  return $password;
}

## checks all mysql passwords by looking at the .my.cnf
## files
sub check_mysql_passwords {
  my $file=shift;
  my $users=read_file($file);
  foreach my $line (split(/\n/,$users)) {
    ## hash marks comments
    chomp $line;
    if($line=~m|^\s*#|) {
      print "not dealing with commented password line '$line'\n";
      next;
    }
    ## remove starting blanks
    $line=~s|\s+||g;
    ## just run the command without any write
    my $password=&get_password_from_my_conf($line,'no_write');
    print "found password $password\n";
  }
}
 
