|
#!/bin/bash |
|
|
|
# Set the mask for the following actions to prevent anyone *else* from reading the following file |
|
umask 0077 |
|
# Create the temp file |
|
tmpfile="/tmp/$(basename "$0")"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')" |
|
# Tell you script to delete the file it just created when the script dies |
|
trap 'if [ $(cat "$tmpfile/log" | wc -l) -gt 1 ]; then cat "$tmpfile/log" | mail -s "KPSync" $(whoami); fi; rm -Rf -- "$tmpfile" >/dev/null' INT TERM HUP EXIT |
|
mkdir -p "$tmpfile" |
|
|
|
log() { |
|
echo "$1" | tee -a "$tmpfile/log" |
|
} |
|
|
|
error() { |
|
log "$1" |
|
exit $2 |
|
} |
|
|
|
log_cp() { |
|
cp -v "$1" "$2" 2>&1 | tee -a "$tmpfile/log" |
|
} |
|
|
|
log_rm() { |
|
rm -fv "$1" 2>&1 | tee -a "$tmpfile/log" |
|
} |
|
|
|
debug_echo() { |
|
if [ -n "$DEBUG" ]; then |
|
echo "$1" |
|
fi |
|
} |
|
|
|
debug_cls() { |
|
if [ -n "$DEBUG_CLEAR" ]; then |
|
clear |
|
fi |
|
} |
|
|
|
help() { |
|
echo "KPSync file1.kdbx file2.kdbx file3.kdbx" |
|
echo "" |
|
echo "KPSync first fixes any file conflicts and then synchronizes between the paths." |
|
echo "" |
|
exit 1 |
|
} |
|
|
|
Resync() { |
|
log "Synchronizing $1 and $2" |
|
chmod u+w "$1" "$2" |
|
if [ -z "$SYNCPW" ]; then |
|
read -s -p "Please enter sync password: [Note: This will be in your ps output] " SYNCREAD ; /usr/bin/cli /usr/lib/keepass2/KPScript.exe -pw:"$SYNCREAD" -c:Sync -File:"$1" "$2" |
|
ERROR=$? |
|
if [ $ERROR -gt 0 ]; then |
|
error "Error while resyncing" $ERROR |
|
fi |
|
else |
|
/usr/bin/cli /usr/lib/keepass2/KPScript.exe -pw-enc:"$SYNCPW" -c:Sync -File:"$1" "$2" | tee -a "$tmpfile/log" |
|
ERROR=$? |
|
if [ $ERROR -gt 0 ]; then |
|
error "Error while resyncing" $ERROR |
|
fi |
|
fi |
|
} |
|
|
|
TweakTime() { |
|
touch -r "$1" -d '+1 second' "$1" |
|
touch -r "$1" -d '-1 second' "$1" |
|
} |
|
|
|
FixConflicts() { |
|
FILE1="$1" |
|
PATH1="$(dirname "$1")" |
|
NAME1="$(basename "$1" .kdbx)" |
|
|
|
if [ `find "$PATH1" -name "${NAME1}*conflict*kdbx" | wc -l` -gt 0 ]; then |
|
debug_echo "Conflicts found in Path" |
|
find "$PATH1" -name "${NAME1}*conflict*kdbx" | while read file |
|
do |
|
log "Resolving conflict between $1 and $file" |
|
Resync "$1" "$file" |
|
log "Removing Conflict File $file" |
|
log_rm "$file" |
|
done |
|
fi |
|
} |
|
|
|
if [ -z "$1" ] || [ ! -f "$1" ]; then |
|
error "Missing file 1" 1 |
|
help |
|
fi |
|
if [ -z "$2" ] || [ ! -f "$2" ]; then |
|
error "Missing file 2" 1 |
|
help |
|
fi |
|
if [ -z "$3" ] || [ ! -f "$3" ]; then |
|
error "Missing file 3" 1 |
|
help |
|
fi |
|
|
|
_SYNCTIME=60 |
|
if [ -n "$SYNCTIME" ]; then |
|
_SYNCTIME="$SYNCTIME" |
|
fi |
|
|
|
while true; do |
|
debug_cls |
|
debug_echo "Starting Cycle at $(date)" |
|
echo "" > "$tmpfile/log" |
|
SYNCPW="$(cat .syncpw 2>/dev/null || cat ~/.config/syncpw 2>/dev/null || cat ~/.syncpw 2>/dev/null || true)" |
|
|
|
if [ $(wc -l "$1" | cut -f1 -d\ ) -lt 1 ]; then NULLFILE1=true; else NULLFILE1=false; fi |
|
if [ $(wc -l "$2" | cut -f1 -d\ ) -lt 1 ]; then NULLFILE2=true; else NULLFILE2=false; fi |
|
if [ $(wc -l "$3" | cut -f1 -d\ ) -lt 1 ]; then NULLFILE3=true; else NULLFILE3=false; fi |
|
|
|
if [ "$NULLFILE1" = "true" ] && [ "$NULLFILE2" = "true" ] && [ "$NULLFILE3" = "true" ]; then |
|
error "All files are truncated. Please urgently recover from backups!" 127 |
|
elif [ "$NULLFILE1" = "true" ]; then |
|
if [ "$NULLFILE2" = "false" ]; then |
|
log "$1 was truncated, recovering from $2" |
|
log_cp "$2" "$1" |
|
else # NULLFILE3 = false |
|
log "$1 was truncated, recovering from $3" |
|
log_cp "$3" "$1" |
|
fi |
|
elif [ "$NULLFILE2" = "true" ]; then |
|
if [ "$NULLFILE1" = "false" ]; then |
|
log "$2 was truncated, recovering from $1" |
|
log_cp "$1" "$2" |
|
else # NULLFILE3 = false |
|
log "$2 was truncated, recovering from $3" |
|
log_cp "$3" "$2" |
|
fi |
|
elif [ "$NULLFILE3" = "true" ]; then |
|
if [ "$NULLFILE1" = "false" ]; then |
|
log "$3 was truncated, recovering from $1" |
|
log_cp "$1" "$3" |
|
else # NULLFILE2 = false |
|
log "$3 was truncated, recovering from $2" |
|
log_cp "$2" "$3" |
|
fi |
|
fi |
|
|
|
debug_echo "Touching files" |
|
|
|
TweakTime "$1" |
|
TweakTime "$2" |
|
TweakTime "$3" |
|
|
|
debug_echo "Checking for conflicts" |
|
|
|
FixConflicts "$1" |
|
FixConflicts "$2" |
|
FixConflicts "$3" |
|
|
|
debug_echo "Syncing Files" |
|
|
|
HASH1="$(md5sum "$1" | cut -f1 -d\ )" |
|
HASH2="$(md5sum "$2" | cut -f1 -d\ )" |
|
HASH3="$(md5sum "$3" | cut -f1 -d\ )" |
|
|
|
debug_echo "$HASH1 $1" |
|
debug_echo "$HASH2 $2" |
|
debug_echo "$HASH3 $3" |
|
|
|
if [ "$HASH1" = "$HASH2" ] && [ "$HASH1" != "$HASH3" ] && [ "$HASH2" != "$HASH3" ]; then |
|
# 1 and 2 match, 3 does not |
|
log "File $3 is out of sync with the peer files. Resync occurring." |
|
|
|
log_cp "$1" "$tmpfile/target.kdbx" |
|
log_cp "$3" "$tmpfile/diff.kdbx" |
|
Resync "$tmpfile/target.kdbx" "$tmpfile/diff.kdbx" |
|
|
|
log "Resync complete. Replacing files with updated version." |
|
|
|
log_cp "$tmpfile/target.kdbx" "$1" |
|
log_cp "$tmpfile/target.kdbx" "$2" |
|
log_cp "$tmpfile/target.kdbx" "$3" |
|
|
|
debug_echo "Removing master and diff files." |
|
|
|
log_rm "$tmpfile/target.kdbx" |
|
log_rm "$tmpfile/diff.kdbx" |
|
elif [ "$HASH1" != "$HASH2" ] && [ "$HASH1" = "$HASH3" ] && [ "$HASH2" != "$HASH3" ]; then |
|
# 1 and 3 match, 2 does not |
|
log "File $2 is out of sync with the peer files. Resync occurring." |
|
|
|
log_cp "$1" "$tmpfile/target.kdbx" |
|
log_cp "$2" "$tmpfile/diff.kdbx" |
|
Resync "$tmpfile/target.kdbx" "$tmpfile/diff.kdbx" |
|
|
|
log "Resync complete. Replacing files with updated version." |
|
|
|
log_cp "$tmpfile/target.kdbx" "$1" |
|
log_cp "$tmpfile/target.kdbx" "$2" |
|
log_cp "$tmpfile/target.kdbx" "$3" |
|
|
|
debug_echo "Removing master and diff files." |
|
|
|
log_rm "$tmpfile/target.kdbx" |
|
log_rm "$tmpfile/diff.kdbx" |
|
elif [ "$HASH1" != "$HASH2" ] && [ "$HASH1" != "$HASH3" ] && [ "$HASH2" = "$HASH3" ]; then |
|
# 2 and 3 match, 1 does not |
|
log "File $1 is out of sync with the peer files. Resync occurring." |
|
|
|
log_cp "$1" "$tmpfile/target.kdbx" |
|
log_cp "$2" "$tmpfile/diff.kdbx" |
|
Resync "$tmpfile/target.kdbx" "$tmpfile/diff.kdbx" |
|
|
|
log "Resync complete. Replacing files with updated version." |
|
|
|
log_cp "$tmpfile/target.kdbx" "$1" |
|
log_cp "$tmpfile/target.kdbx" "$2" |
|
log_cp "$tmpfile/target.kdbx" "$3" |
|
|
|
debug_echo "Removing master and diff files." |
|
|
|
log_rm "$tmpfile/target.kdbx" |
|
log_rm "$tmpfile/diff.kdbx" |
|
elif [ "$HASH1" != "$HASH2" ] && [ "$HASH1" != "$HASH3" ] && [ "$HASH2" != "$HASH3" ]; then |
|
# ALL FILES OUT OF SYNC |
|
log "All files are out of sync. Bringing all versions (1 -> 2, 1&2 -> 3) up to date." |
|
|
|
log_cp "$1" "$tmpfile/target.kdbx" |
|
log_cp "$2" "$tmpfile/diff.kdbx" |
|
Resync "$tmpfile/target.kdbx" "$tmpfile/diff.kdbx" |
|
|
|
log_cp "$3" "$tmpfile/diff.kdbx" |
|
Resync "$tmpfile/target.kdbx" "$tmpfile/diff.kdbx" |
|
|
|
log "Resync complete. Replacing files with updated version." |
|
|
|
log_cp "$tmpfile/target.kdbx" "$1" |
|
log_cp "$tmpfile/target.kdbx" "$2" |
|
log_cp "$tmpfile/target.kdbx" "$3" |
|
|
|
debug_echo "Removing master and diff files." |
|
|
|
log_rm "$tmpfile/target.kdbx" |
|
log_rm "$tmpfile/diff.kdbx" |
|
elif [ "$HASH1" = "$HASH2" ] && [ "$HASH1" = "$HASH3" ] && [ "$HASH2" = "$HASH3" ]; then |
|
debug_echo "All files in sync." |
|
else |
|
error "Unexpected state. Exiting" 100 |
|
fi |
|
|
|
if [ $(cat "$tmpfile/log" | wc -l) -gt 1 ]; then |
|
cat "$tmpfile/log" | mail -s 'KPSync' $(whoami) |
|
fi |
|
|
|
if [ $_SYNCTIME -lt 2 ]; then |
|
exit 0 |
|
fi |
|
sleep $_SYNCTIME |
|
done |