#!/bin/bash
# Title: git-sync.sh
# Version: 2.2
# Author: Frédéric CHEVALIER <fcheval@txbiomedgenetics.org>
# Created in: 2014-09-03
# Modified in: 2016-01-07
# Licence: GPL v3



#======#
# Aims #
#======#

aim="Synchronize files from a git server on this terminal."



#==========#
# Versions #
#==========#

# v2.2 - 2016-01-07: process of list file improved (tmp list not needed anymore) / Timestamp added to the downloaded file to avoid interference with several script instance running at the same time
# v2.1 - 2015-03-12: evaluation of the variables created from the list in order to use environment variables (ie, $HOME) / directoy checked instead of created otherwise may lead to unwanted path if error in list file / usage message updated
# v2.0 - 2015-02-27: error function improved / info function added / file installed and directory created if not already present / modification of file permission can be specified in the file list and apply to the file
# v1.3 - 2014-12-04: warning and error functions improved
# v1.2 - 2014-09-24: temp file bug corrected / check if local file exist
# v1.1 - 2014-09-14: test_dep bug corrected
# v1.0 - 2014-09-14: git dependency check replaced by wget check (bug) / test_dep function improved / remove comments in list file / check for http path / remove spaces in path / checksum loop corrected
# v0.1 - 2014-09-23: dry-run option added / no check certifcate option added for wget
# v0.0 - 2014-09-03: creation



#===========#
# Functions #
#===========#

# Dependency test
function test_dep {
    which $1 &> /dev/null
    if [[ $? != 0 ]]
    then
        error "Package $1 is needed. Exiting..." 1
    fi
}

# Usage message
function usage {
    echo -e "
    \e[32m${0##*/}\e[00m -l|--list file -d|--dry-run -h|--help

Aim: $aim

Options:
    -l, --list      Text file containing both remote and local path of
                    a given file for synchronization.
                        - The list file must have a unique path pair
                            per line and each path must be semi-colon
                            separated (remote_path ; local_path).
                            The \e[33mremote path\e[0m must be \e[33mhttp\e[0m a path.
                        - Modification of file permission can be specified
                            in a third column. The syntax is the chmod
                            syntax (ie, remote_path ; local_path ; uog+rwx).
                        - Comments beginning by # are allowed anywhere
                            in the list file.

    -d, --dry-run   Run the synchronization but print a message instead of updating the files. 

    -h, --help      This message
"
}


# Error message
function error {
    if [[ -t 1 ]]
    then
        echo -e "\e[31mError:\e[00m $1"
    else
        echo -e "Error: $1"
    fi
    
    exit $2
}


# Warning message
function warning {
    if [[ -t 1 ]]
    then
        echo -e "\e[33mWarning:\e[00m $1"
    else
        echo -e "Warning: $1"
    fi
}


# Info message
function info {
    if [[ -t 1 ]]
    then
        echo -e "\e[32mInfo:\e[00m $1"
    else
        echo -e "Info: $1"
    fi
}



#==============#
# Dependencies #
#==============#

test_dep wget



#==========================#
# Declaration of variables #
#==========================#

# Options
while [[ $# -gt 0 ]]
do
    case $1 in
        -l|--list    ) list="$2" ; shift 2 ;;
        -d|--dry-run ) dry=1 ; shift ;;
        -h|--help    ) usage ; exit 0 ;;
        *            ) error "Invalid option: $1\n$(usage)\n" 1 ;;
    esac
done


# Check the existence of obligatory options
if [[ -z "$list" ]]
then
    error "The option -l is required. Exiting...\n$(usage)\n" 1
fi


# Timestamp
myts=$(date +%N)



#============#
# Processing #
#============#

# Check file list
if [[ $(awk -F ";" "END {print NF}" "$list") -gt 3 ]]
then
    error "The list file "$list" is malformated. Each path pair must be on unique line and each path must be semi-colon (';') separated." 1
fi


# Synchronization
while read line
do

    # Remove comments from line
    line=$(echo "$line" | sed "/^\s*#/d;s/\s*#.*$//")
    
    # Skip if empty line
    if [[ -z "$line" ]]
    then 
        continue
    fi

    # Get paths
    eval mysrc=$(echo "$line" | cut -d ";" -f 1 | sed -e "s/^ *//" -e "s/ *$//")
    eval mydst=$(echo "$line" | cut -d ";" -f 2 | sed -e "s/^ *//" -e "s/ *$//")
    eval mymod=$(echo "$line" | cut -d ";" -f 3 | sed -e "s/^ *//" -e "s/ *$//")

    mydst_tmp="/tmp/${mydst##*/}-$myts"


    # Check if mydst variable is empty or contain directory
    if [[ -z "$mydst" ]]
    then
        warning "The destination name is absent. Skipping..."
        continue
    elif [[ $(echo "$mydst" | grep "/") && ! -d "${mydst%/*}" ]]
    then
        warning "${mydst##*/}: ${mydst%/*} directory does not exist. Skipping..."
        continue
    fi

    # Check if remote path is http
    if [[ ! $(echo "$mysrc" | grep -i "^http") ]]
    then
        warning "${mydst##*/}: Remote path is not http address. Skipping..."
        continue
    fi

    # Download file
    wget --no-check-certificate -O "$mydst_tmp" "$mysrc" &> /dev/null

    # Check for errors
    if [[ $? != 0 ]]
    then
        warning "${mydst##*/}: An error occured during the downloading. Skipping..."
        continue
    # Check if destination file exists
    elif [[ ! -e "$mydst" ]]
    then
        if [[ $dry == 1 ]]
        then
            info "${mydst##*/}: file available but not installed."
        else
            mv "$mydst_tmp" "$mydst"
            info "${mydst##*/} installed."
        fi
    else
        # Compare downloaded file and destination file (update file if need)
        mydst_tmp_md=$(md5sum "$mydst_tmp" | cut -d " " -f 1)
        mydst_md=$(md5sum "$mydst" | cut -d " " -f 1)
        if [[ "$mydst_tmp_md" != "$mydst_md" ]]
        then
            if [[ $dry == 1 ]]
            then
                info "${mydst##*/}: new version available."
            else
                mv "$mydst_tmp" "$mydst"
                info "${mydst##*/} updated."
            fi
        else
            rm "$mydst_tmp"
        fi
    fi

    if [[ -z $dry && -n "$mymod" && -n $(echo "$mymod" | sed -n "/[u,o,g,][+,-][r,w,x,s,t,S,T]/p") ]]
    then
        chmod "$mymod" "$mydst"
    elif [[ -z $dry && -n "$mymod" && -z $(echo "$mymod" | sed -n "/[u,o,g,][+,-][r,w,x,s,t,S,T]/p") ]]
    then
        warning "${mydst##*/}: Impossible to change permission, \"$mymod\" does not follow the syntax of the chmod command (see help)."
        continue
    fi

done < "$list"


exit 0