# Copyright 1999-2012. Parallels IP Holdings GmbH. All Rights Reserved.
package Db::DbConnect;

use Db::Connection;
use Logging;
use XmlNode;

sub getDbConnect {
  my ($myDbType, $myDbUser, $myDbPasswd, $myDbDB, $myDbHost,
      $continueAfterError, $myDbSocket, $variables, $utf, $port) = @_;

  my $connection;

  if ($myDbType eq 'Pg') {
    $myDbType = 'postgresql';
  }

  my %params;
  $params{'name'} = $myDbDB;
  $params{'user'} = $myDbUser;
  $params{'password'} = $myDbPasswd;
  $params{'host'} = $myDbHost if $myDbHost;
  $params{'socket'} = $myDbSocket if $myDbSocket;
  $params{'port'} = $port if $port;
  $params{'preload_dirs'} = $variables if $variables;
  $params{'type'} = $myDbType;

  $connection = Db::Connection::getConnection(%params);

  my $this =
    {
     'DISCONNECT' => sub { return $connection->disconnect(@_); },
     'EXECUTE' => sub { return $connection->execute_rownum(@_); },
     'FETCHROW' => sub { return $connection->fetchrow(@_); },
     'FETCHHASH' => sub { return $connection->fetchhash(@_); },
     'FINISH' => sub { return $connection->finish(@_); },
     'PING' => sub { return $connection->ping(@_); },
     'USER' => sub { return $myDbUser; },
     'PASSWORD' => sub { return $myDbPasswd; },
     'HOST' => sub { return $myDbHost; }
    };
  return $this;
}

#
# end class for handle database
#
sub addDbUsers {
  my ($dbNode, $dbName, $wrapUserMysql, $excludeUsers) = @_;

  $dbName =~ s/_/\\_/g;

  my @users = getDbUsers( $dbName, $wrapUserMysql, $excludeUsers );

  foreach $dbUser (@users) {
    addDbUser($dbNode, $dbUser, $wrapUserMysql);
  }
}

sub getDbUsers {
  my ( $dbName, $wrapUserMysql, $excludeUsers) = @_;

  $dbName =~ s/_/\\_/g;

  my (@users, $dbUser, $ptrRow);
  my $sql = "SELECT DISTINCT User FROM db WHERE Db = '$dbName'";
  if (defined $excludeUsers && @{$excludeUsers}) {
    $sql .= " AND User NOT IN (" . join(", ", map {"'$_'"} @{$excludeUsers}) . ")";
  }

  if ($wrapUserMysql->{'EXECUTE'}->($sql) == 0) { # bug 94641
    my $sql = "SELECT DISTINCT User FROM db WHERE Db LIKE '$dbName'";
    $wrapUserMysql->{'FINISH'}->();
    $wrapUserMysql->{'EXECUTE'}->($sql);
  }

  while ($ptrRow = $wrapUserMysql->{'FETCHROW'}->()) {
    push @users, (@{$ptrRow})[0];
  }
  $wrapUserMysql->{'FINISH'}->();
  return @users;
}

sub addDbUser {
  my ($dbNode, $user, $wrapUserMysql, $passwordNode, $accessHosts) = @_;

  Logging::trace("Dumping mysql user '$user'");

  my ( $item );

  if ( ref( $wrapUserMysql ) =~ /HASH/ &&
       ref( $wrapUserMysql->{'EXECUTE'} ) =~ /CODE/ ) {

    my ($sql,$ptrRow,@accesshosts,$accesshost);

    $sql = "SELECT password,host FROM user WHERE user='$user'";

    if ( $wrapUserMysql->{'EXECUTE'}->( $sql ) ) {
      $item = XmlNode->new('dbuser', 'attributes' => {'name' => $user} );
      while ( $ptrRow = $wrapUserMysql->{'FETCHROW'}->() ) {
        unless ( ref($passwordNode) =~ /HASH/ ) {
          $passwordNode = makePasswordNode( $ptrRow->[0] );
        }
        push @accesshosts, $ptrRow->[1];
      }

      if ( $passwordNode ) {
        $item->{'ADDCHILD'}->( $passwordNode );
      }

      if (defined($accessHosts) && ref($accessHosts) =~ /ARRAY/) {
        @accesshosts = @{$accessHosts};
      }

      foreach $accesshost ( @accesshosts ) {
        $item->{'ADDCHILD'}->( XmlNode->new( 'accesshost', 'content' => $accesshost ) );
      }

      $dbServerNode = getDefaultDbServerNode();
      $item->{'ADDCHILD'}->($dbServerNode);

      $dbNode->{'ADDCHILD'}->( $item );
    }

    $wrapUserMysql->{'FINISH'}->();
  }

  return $item;
}

sub getDbServerNode {
  my ($type, $host, $port) = @_;
  my $item = XmlNode->new('db-server',
    'attributes' => {
      'type' => $type,
    },
    'children' => [
      XmlNode->new('host', 'content' => $host),
      XmlNode->new('port', 'content' => $port),
    ],
  );
  return $item;
}

sub getDefaultDbServerNode {
  return getDbServerNode('mysql', 'localhost', '3306');
}

sub makePasswordNode {
  my ($password, $type) = @_;

  if (!defined $password) {
    Logging::error("'undef' password passed to makePasswordNode. Set to empty");
    $password = '';
  }

  if (!$password) {
    $type = 'plain';
  } 
  else {
    if ($type ne 'plain') {
      $type = 'encrypted';
    }
  }

  return XmlNode->new('password',
                      'content' => $password,
                      'attributes' => {'type' => $type} );
}


1;
