#!/bin/bash

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Ldap Synchronization Connector provides tools to synchronize electronic
# identities from a list of data sources including any database with a JDBC
# connector, another LDAP directory, flat files...
#
# LSC automatically embeds HSQLDB library. So, this script can:
# - Create a table from a CSV file header in a HSQLDB database;
# - Inject CSV data into a HSQLDB database;
# - Read data from a HSQLDB database table.
#
# ---------------------------------------------------------------------------------
#
# Copyright (c) 2008 - 2011 LSC Project 
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice, this
#     list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#   * Neither the name of the LSC Project nor the names of its contributors may be
#     used to endorse or promote products derived from this software without
#     specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#       (c) 2008 - 2011 LSC Project
#   Sebastien Bahloul <seb@lsc-project.org>
#   Thomas Chemineau <thomas@lsc-project.org>
#   Jonathan Clarke <jon@lsc-project.org>
#   Remy-Christophe Schermesser <rcs@lsc-project.org>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# ---------------------------------------------------------------------------------
# CONFIGURATION
# ---------------------------------------------------------------------------------

# work out where LSC sample lives
SAMPLE_HOME=$(dirname "${BASH_SOURCE}")/..
if [ ${SAMPLE_HOME:0:1} != "/" ] ; then
	SAMPLE_HOME="${PWD}/${SAMPLE_HOME}"
fi

# HSQLDB jar file
HSQLDB_LIB=`ls $SAMPLE_HOME/../../lib/hsqldb*.jar`
# Temporary directory so that HSQLDB server could store files
HSQLDB_DIR=/tmp/lsc/hsqldb
# HSQLDB configuration file
HSQLDB_RC=$SAMPLE_HOME/etc/hsqldb.rc
# Table name
HSQLDB_TABLE=csvdata
# Default CSV separator
CSV_SEPARATOR=";"

# Configuration for sample
SAMPLE_CONFIG_DIR="$SAMPLE_HOME/etc"
# is this Cygwin? if so, convert path to Windows format for the JVM
CYGPATH_COMMAND="$(which cygpath 2>/dev/null)"
if [ -e "$CYGPATH_COMMAND" ]; then
	SAMPLE_CONFIG_DIR=$(cygpath -w "$SAMPLE_CONFIG_DIR")
	HSQLDB_LIB=$(cygpath -w "$HSQLDB_LIB")
	HSQLDB_RC=$(cygpath -w "$HSQLDB_RC")
	HSQLDB_DIR=$(cygpath -u "$HSQLDB_DIR")
	CYGWIN_BASE=$(cygpath -w / | sed -e "s#:\\\\#:/#g")
fi

# ---------------------------------------------------------------------------------
# FUNCTIONS
# ---------------------------------------------------------------------------------

#
# Drop all data from the HSQLDB instance.
#
data_drop()
{
  hsqldb_sqlQuery "DROP TABLE $HSQLDB_TABLE;"
}

#
# Create the SQL table into the HSQLDB database. All field are read from the first
# line of the CSV file passed in the first parameter. Once the table is created,
# data should be correctly imported.
#
data_import()
{
  # $1 : CSV fullpath
  # $2 : CSV separator
  HSQLDB_TABLEDEF=""
  IFSO=$IFS
  IFS=$2
  for field in `head -n 1 $1`; do
    if [ -n "$HSQLDB_TABLEDEF" ]; then
      HSQLDB_TABLEDEF="$HSQLDB_TABLEDEF, $field VARCHAR"
    else
      HSQLDB_TABLEDEF="$field VARCHAR"
    fi
  done
  IFS=$IOFS
	mkdir -p $HSQLDB_DIR
  if [ -n "$HSQLDB_TABLEDEF" ]; then
    hsqldb_sqlQuery "CREATE TEXT TABLE $HSQLDB_TABLE ($HSQLDB_TABLEDEF); SHUTDOWN;"
    if [ $? -eq 0 ]; then
	  echo "Table $HSQLDB_TABLE created"
      nblines=`cat $1 | wc -l`
      let nblines--
			
      tail -n $nblines $1 > $HSQLDB_DIR/`basename $1`
      sep=`hsqldb_checkCsvSeparator $2`
		encoding=`file $1  2> /dev/null | cut -d' ' -f2`
		if [ "x$encoding" == "xUTF-8" ] ; then
			hsqldb_sqlQuery "SET TABLE $HSQLDB_TABLE SOURCE \"`basename $1`;fs=$sep;encoding=UTF-8\"; SHUTDOWN;" > /dev/null 2>&1
		else
			hsqldb_sqlQuery "SET TABLE $HSQLDB_TABLE SOURCE \"`basename $1`;fs=$sep\"; SHUTDOWN;" > /dev/null 2>&1
		fi
	  if [ $? -eq 0 ]; then
		  echo "$nblines lines added to table $HSQLDB_TABLE"
		  return
	  fi
    fi
  fi
  exit 1
}

#
# Show data from the HSQLDB instance.
#
data_show()
{
  hsqldb_sqlQuery "SELECT * FROM $HSQLDB_TABLE;"
}

#
# Return the CSV separator used by hsqldb.
#
hsqldb_checkCsvSeparator()
{
  # $1 : A separator
  case "$1" in
    ";")
      echo -n "\semi"
      ;;
    "\"")
      echo -n "\quote"
      ;;
    " ")
      echo -n "\space"
      ;;
    "'")
      echo -n "\apos"
      ;;
    *)
      echo -n $1
      ;;
  esac
}

#
# Remove all data on disk that have been created by an hsqldb server. No checks
# on a running process are performed, so be carefull.
#
hsqldb_cleanData()
{
  oldpwd=`pwd`
  cd $HSQLDB_DIR > /dev/null 2>&1
  if [ $? -eq 0 ]; then
    rm -f *
    cd ..
    rmdir `basename "$HSQLDB_DIR"`
  fi
  cd $oldpwd
}

#
# Perform a SQL query on the running HSQLDB instance. No checks on a running
# process are performed, so be carefull.
#
hsqldb_sqlQuery()
{
  # $1 : sql query

	# check if we have java executable
	get_java

	# is this Cygwin? if so, convert path to Windows format for the JVM
	CYGPATH_COMMAND="$(which cygpath 2>/dev/null)"
	if [ -e "$CYGPATH_COMMAND" ]; then
		[ ! -f "$HSQLDB_RC.bak" ] && cp "$HSQLDB_RC" "$HSQLDB_RC.bak"
		cat "$HSQLDB_RC"  | sed -e "s#:/tmp/#:$CYGWIN_BASE/tmp/#g" > "$HSQLDB_RC.new"
		mv "$HSQLDB_RC.new" "$HSQLDB_RC"
	fi
	
	"${JAVA_COMMAND}" -jar "$HSQLDB_LIB" --rcFile "$HSQLDB_RC" --sql "$1" lscdb
}

# Determine where the java exectuable is
function get_java() {
    # Do we have a JAVA_HOME environment variable?
    if [ "z" != "z${JAVA_HOME}" ]; then
        JAVA_COMMAND="${JAVA_HOME}/bin/java"
        return
    fi  

    # Try java command on PATH as an alternative
    JAVA_COMMAND="$(which java 2>/dev/null)"
    if [ -e "$JAVA_COMMAND" ]; then return; fi

    # Nothing seems approprite, warn and exit
    log "No java executable found on PATH or in JAVA_HOME! Aborting."
    log "Define JAVA_HOME or adjust your PATH variable to include java."
    exit 2
}

run_java_command()
{
	# check if we have java executable
	get_java

	for jar in "$SAMPLE_HOME"/../../lib/slf4j-api*.jar; do
		LDAPCP="$LDAPCP:$jar"
	done

	for jar in "$SAMPLE_HOME"/../../lib/*.jar; do
		LDAPCP="$LDAPCP:$jar"
	done

	for jar in "$SAMPLE_HOME"/../lib/*.jar; do
		LDAPCP="$LDAPCP:$jar"
	done

	# is this Cygwin? if so, convert path to Windows format for the JVM
	CYGPATH_COMMAND="$(which cygpath 2>/dev/null)"
	if [ -e "$CYGPATH_COMMAND" ]; then
		LDAPCP=$(cygpath -p -w "$LDAPCP")
	fi

	"${JAVA_COMMAND}" -cp "$LDAPCP" $1
}

start_ldap() {
	echo "Starting LDAP server on ldap://localhost:33389/ ..."
	run_java_command "org.lsc.opendj.LdapServer -a -f $SAMPLE_CONFIG_DIR"
}

stop_ldap() {
	run_java_command "org.lsc.opendj.LdapServer -o -f $SAMPLE_CONFIG_DIR"
}

run() {
	CLASSPATH=""
	for jar in "$SAMPLE_HOME"/../lib/*.jar; do
		CLASSPATH="$CLASSPATH:$jar"
	done
	
	CONFIGPATH=$SAMPLE_HOME/etc;

	# is this Cygwin? if so, convert path to Windows format for the JVM
	CYGPATH_COMMAND="$(which cygpath 2>/dev/null)"
	if [ -e "$CYGPATH_COMMAND" ]; then
		CLASSPATH=$(cygpath -p -w "$CLASSPATH")
		CONFIGPATH=$(cygpath -w "$CONFIGPATH")
		
		[ ! -f "$CONFIGPATH/lsc.xml.bak" ] && cp "$CONFIGPATH/lsc.xml" "$CONFIGPATH/lsc.xml.bak"
		cat "$CONFIGPATH/lsc.xml"  | sed -e "s#jdbc:hsqldb:file:/tmp/lsc/hsqldb/lsc#jdbc:hsqldb:file:$CYGWIN_BASE/tmp/lsc/hsqldb/lsc#g" > "$CONFIGPATH/lsc.xml.new"
		mv "$CONFIGPATH/lsc.xml.new" "$CONFIGPATH/lsc.xml"
	fi

	export CLASSPATH
	export CONFIGPATH

	command="\"$SAMPLE_HOME/../../bin/lsc\" --config \"$CONFIGPATH\" --synchronize all --clean all"
	echo "Running $command"
	"$SAMPLE_HOME/../../bin/lsc" --config "$CONFIGPATH" --synchronize all --clean all
}

# ---------------------------------------------------------------------------------
# MAIN
# ---------------------------------------------------------------------------------

# -- Check script parameters --

ACTIONS=""

case "$1" in
  "--drop")
    ACTIONS="$ACTIONS dropdata"
    shift
    ;;
  "--import")
    shift
    CSV_FILE="$1"
    shift
    if [ "$1" != "" ]; then
      CSV_SEPARATOR="$1"
      shift
    fi
    ACTIONS="$ACTIONS cleandata importdata"
    ;;
  "--show")
    ACTIONS="$ACTIONS showdata"
    shift
    ;;
  "--start-ldap-server")
    ACTIONS="$ACTIONS startldap"
	shift
	;;
  "--stop-ldap-server")
    ACTIONS="$ACTIONS stopldap"
!	shift
	;;
  "--run")
	run
	shift
	;;
  "--clean")
	ACTIONS="$ACTIONS cleandata cleanldapdata"
	shift
	;;
  "--help"|*)
    echo ""
    echo " $0 <option>"
    echo ""
    echo " One option from available options is:"
    echo "   --import <file> [<separator>]    Import data from CSV file"
    echo "   --show    Show data from database"
    echo "   --drop    Drop the table $HSQLDB_TABLE from database"
    echo "   --start-ldap-server    Launch an embedded OpenDJ LDAP server"
    echo "   --stop-ldap-server     Halt an embedded OpenDJ LDAP server"
    echo "   --run     Run the synchronization task"
	echo "   --clean   Remove all files from disk"
    echo "   --help    Print informations"
    echo ""
    exit 1
    ;;
esac

# -- Run actions --

for action in $ACTIONS; do
  case "$action" in
    "dropdata")
      data_drop
      ;;
    "importdata")
      if [ ! -e "$CSV_FILE" ]; then
        echo " Error - file '$1' not found"
        exit 1
      fi
      data_import "$CSV_FILE" "$CSV_SEPARATOR"
      ;;
    "showdata")
      data_show
      ;;
    "startldap")
      start_ldap
      ;;
    "stopldap")
      stop_ldap
      ;;
    "cleandata")
      hsqldb_cleanData
      ;;
    "cleanldapdata")
      rm -rf /tmp/opendj-test
      ;;
  esac
done

