#!/bin/bash -


if ! id -u | grep -q '^0$'; then
	echo "Please run \"${0##*/}\" as root, or use \"sudo ${0##*/}\""
	echo "Unable to use \"$(whoami)\" users to achieve the expected goals"
	exit 1
fi

readonly _command="${0##*/}"
_preferences_path='/var/mobile/Library/Preferences'
_package="$(dpkg -S "${0}")"
_package="${_package%%: *}"
_backup_path='/var/mobile/Media/fh'

_new_backup_path=$(plutil -key 'backup_path' "${_preferences_path}/${_package}.plist")
case "${_new_backup_path}" in
	*/*)
		_backup_path="${_new_backup_path}"
		unset _new_backup_path
		;;
esac
mkdir -p "${_backup_path}"

_print_help() {
	echo "Usage: ${_command} [OPTIONS...]
	-h		Print this help
	-c <NAME> 	Create backup named NAME
	-r <NAME> 	Restore backup named NAME
	-d <NAME> 	Delete backup named NAME
	-l		List the backups
	-C		Clean font caches
	-f		Override permission
	-p		Show user-defined variables
	—skip-check	Skip compatibility check
	—respring	Repsring in end
Example:
	${_command} -c <NAME>
	${_command} -r <NAME>
	${_command} -r <NAME> —skip-check
	${_command} -r <NAME> —respring
	${_command} -d <NAME>
	${_command} -l
	${_command} -C
	${_command} -C —respring
	${_command} -f
	${_command} -p"
}

_new_tmp(){
	aaaa="/tmp/$(uuid)"
	case "${1}" in
		f)
			touch "${aaaa}"
			;;
		d)
			mkdir -p "${aaaa}"
			;;
	esac
	echo "${aaaa}"
	unset aaaa
}

_clean_caches(){
	rm -rf '/var/mobile/Library/Caches/com.apple.keyboards/'
	rm -rf '/var/mobile/Library/Caches/com.apple.UIStatusBar/'
	rm -rf "/var/mobile/Library/Caches/TelephonyUI"*
	find "/var/mobile/Containers/Data/Application" -maxdepth 4 -type d -iname "TelephonyUI*" -exec rm -rf {} \;
}

_fix_permissions(){
	chown -R root:wheel /System/Library/Fonts/
	chown -R root:wheel /System/Library/Fonts/*
	find '/System/Library/Fonts' -type d -exec chmod -R 0755 {} \;
	find '/System/Library/Fonts' -type f -exec chmod -R 0644 {} \;
}

_read_plist(){
	while read -t '0.01' _incoming; do
		if [ -z "${_incoming}" ]; then
			break
		elif [ -z "${_is_echo}" ]; then
			#echo 'User specified files will be ignored'
			#echo 'will read the incoming text...'
			_is_echo='1'
		fi
		_plist="${_plist}
${_incoming}"
	done
	unset _incoming _is_echo
	if [ -n "${_plist}" ]; then
		_plist="$(echo "${_plist}" | sed '1d')"
		return
	fi
	if [ -z "${1}" ]; then
		echo 'Error: null name'
		exit 1
		_plist="$(cat "${1}")"
	elif [ -f "${1}" ]; then
		_plist="$(cat "${1}")"
	else
		echo "Error: File not found at path \"${1}\""
		exit 1
	fi
}

_analyze_plist(){
	#Should only be able to read strings and boolean without special symbols
	_read="$(echo "${_plist}" | grep -n "<key>${1}</key>" | sed -n '1p')"
	if [ -z "${_read}" ]; then
		echo "No found \"${1}\""
		exit 1
	fi
	_read="${_read%%:*}"
	_read="$((_read+1))"
	_read="$(echo "${_plist}" | sed -n "${_read}p")"
	case "${_read}" in
		*string*)
			_read="${_read%%</string>*}"
			_read="${_read##*<string>}"
			;;
		*'<true/>')
			_read='1'
			;;
		*'<false/>')
			_read='0'
			;;
	esac
	echo "${_read}"
}

_read_information(){
	case "${1}" in
		*.ftb)
			if [ -f "${1}" ]; then
				_backup_file="${1}"
			else
				echo "No backup named \"${1}\" was found"
				exit 1
			fi
			;;
		*)
			if [ -f "${_backup_path}/${1}.ftb" ]; then
				_backup_file="${_backup_path}/${1}.ftb"
			else
				echo "No backup named \"${1}\" was found"
				exit 1
			fi
			;;
	esac
	if [ -n "${2}" ]; then
		_plist="$(unzip -z "${_backup_file}" | sed '1d' | fh -R)"
		_analyze_plist "${2}"
	else
		unzip -z "${_backup_file}" | sed '1d'
	fi
}

_check_backup_compatibility(){
	if [ ! "$(sw_vers -productName)" = "$(fh -R "${1}" 'ProductName')" ]; then
		echo ''
		echo "\"ProductName\" does not match the current system"
		_refuse_recover=1
	fi
	if [ ! "$(sw_vers -productVersion)" = "$(fh -R "${1}" 'ProductVersion')" ]; then
		echo ''
		echo "\"ProductVersion\" does not match the current system"
		_refuse_recover=1
	fi
	if [ ! "$(sw_vers -buildVersion)" = "$(fh -R "${1}" 'ProductBuildVersion')" ]; then
		echo ''
		echo "\"ProductBuildVersion\" does not match the current system"
		_refuse_recover=1
	fi
	if [ "${_refuse_recover}" = '1' ]; then
		echo ''
		echo ''
		echo "Cannot restore this software package \"${1}\", because \"${1}\" does not match your system."
		echo "If you think it doesn't matter, you can try disabling the compatibility check and continue with the recovery."
		exit 1
	fi
}

_check_backup_exists(){
	if [ -f "${_backup_path}/${1}.ftb" ]; then
		_f='1'
	else
		_f='0'
	fi
}

_create_backup(){
	_cuid=$(uuid)
	cd '/System/Library'
	zip -9 -r -q -v "${_backup_path}/${1}.ftb" "./Fonts" -z << EOF >"/tmp/${_cuid}.tmp" &
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>CreateTime</key>
        <string>$(date +%F-%T)</string>
        <key>ID</key>
        <string>$(echo "${_cuid}")</string>
        <key>Name</key>
        <string>$(echo "${1}")</string>
        <key>ProductBuildVersion</key>
        <string>$(sw_vers -buildVersion)</string>
        <key>ProductName</key>
        <string>$(sw_vers -productName)</string>
        <key>ProductVersion</key>
        <string>$(sw_vers -productVersion)</string>
</dict>
</plist>
.
EOF
	cd
	sleep 0.1
	_aa="$(tree '/System/Library/Fonts' | wc -l)"
	while [ '1' -le '2' ]; do
		if [ -z "$(ps -ef | grep 'zip -9 -r -q -v' | grep $$ | grep -v grep)" ]; then
			unset _file _file_rows
			break
		fi
		_file_rows="$(cat "/tmp/${_cuid}.tmp" | wc -l)"
		_file="$(cat "/tmp/${_cuid}.tmp" | sed -n "${_file_rows}p")"
		_file="${_file##*/}"
		_file="${_file%%.*}"
		_file="${_file%%(*}"
		_file="${_file%%new zip*}"
		_file="${_file%%total*}"
		_file="(${_file_rows}/${_aa}) ${_file}"
		if [ ! "${_file_0}" = "${_file}" ]; then
			echo -ne "\rstatus: "
			a='1'
			while [ "${a}" -le "${#_file_0}" ]; do
				echo -n " "
				a=$((a+1))
			done
			echo -ne "\rstatus: \r"
			echo -ne "status: ${_file}\rstatus: "
		fi
		_file_0="${_file}"
		unset _file _file_rows
	done
	rm -f "/tmp/${_cuid}.tmp"
	echo -ne "\rstatus: "
	a='1'
	while [ "${a}" -le "${#_file_0}" ]; do
		echo -n " "
		a=$((a+1))
	done
	unset _file_0 a
	echo -ne "\rstatus: "
	if [ "${_cuid}" = "$(fh -R "${1}" 'ID')" ]; then
		echo 'Success'
	else
		echo 'Failure'
		echo 'Log: cuid and ID do not match, Maybe the file is damaged?'
		echo "file path: ${_backup_path}/${1}.ftb"
	fi
}

_delete_backup(){
	_log="$(_new_tmp f)"
	if rm "${_backup_path}/${1}.ftb" >>"${_log}"; then
		echo 'Success'
	else
		echo 'Failure'
		echo 'Log:'
		cat "${_log}"
	fi
	rm -f "${_log}"
}

_release_backup(){
	_log="$(_new_tmp f)"
	rm -rf '/System/Library/Fonts_bak'
	mv '/System/Library/Fonts' '/System/Library/Fonts_bak'
	if unzip -q "${1}" -d '/System/Library/' >>"${_log}"; then
		rm -rf '/System/Library/Fonts_bak'
		echo -ne "\rstatus:                     "
		echo -ne "\rstatus: Clean font caches"
		echo -ne "\rstatus: "
		_clean_caches
		echo -ne "\rstatus:                  "
		echo -ne "\rstatus: "
		echo 'Success'
	else
		echo -ne "\rstatus:                     "
		echo -ne "\rstatus: "
		echo 'Failure'
		echo 'Will attempted to revert all changes'
		rm -rf '/System/Library/Fonts'
		mv '/System/Library/Fonts_bak' '/System/Library/Fonts'
		echo 'status: Success'
		echo 'Log:'
		cat "${_log}"
	fi
	"${0}" -f >>/dev/null
	rm -f "${_log}"
}


case "${1}" in
	-h|--help)
		_print_help
		;;
	-l|--list)
		echo "Will list backup on \"${_backup_path}\" dir"
		ls "${_backup_path}" | grep ".*\.ftb" | sed 's#\.ftb##g'
		exit 0
		;;
	-c|--create)
		if [ -z "${2}" ]; then
			_now_time="$(date +%F-%T)"
			echo "Will create backup named \"${_now_time}\""
			_create_backup "${_now_time}"
		else
			echo "Will create backup named \"${2}\""
			_create_backup "${2}"
		fi
		;;
	-C|--clean)
		echo 'Will clean font caches'
		_clean_caches
		case "${2}" in
			-r|--respring)
				killall -9 backboardd
				;;
		esac
		echo 'status: Success'
		;;
	-d|--delete)
		echo "Will delete backup named \"${2}\""
		echo -n "status: "
		if [ -n "${2}" ]; then
			_delete_backup "${2}"
		else
			echo 'Error'
			echo 'Log: null name'
			exit 1
		fi
		shift 2
		for b in "${@}"; do
			echo "Will delete backup named \"${b}\""
			echo -n "status: "
			_delete_backup "${b}"
		done
		;;
	-f|--fix)
		echo 'Will try to override file permissions'
		echo -ne 'status: Overwriting'
		echo -ne "\rstatus: "
		if _fix_permissions >>/dev/null; then
			echo -ne "\rstatus:            "
			echo -ne "\rstatus: "
			echo 'Success'
		else
			echo -ne "\rstatus:            "
			echo -ne "\rstatus: "
			echo 'Failure'
		fi
		;;
	-r|--release|--restore)
		case "${2}" in
			*.ftb)
				if [ -n "$(fh -R "${2}" 'Name')" ]; then
					echo "Will restore custom backup \"$(fh -R "${2}" 'Name')\" to system"
				else
					echo 'Will restore custom backup to system'
				fi
				;;
			*)
				if [ -n "${2}" ]; then
					echo "Will revert backup \"$(fh -R "${2}" 'Name')\" to system"
				else
					echo 'status: Error'
					echo 'Log: null name'
					exit 1
				fi
				;;
		esac
		_check_backup_exists "${2}"
		case "${3}" in
			-r|--respring)
				_respring='1'
				;;
			-s|--skip-check)
				echo -ne 'status: Skip compatibility check'
				echo -ne "\rstatus: "
				;;
			*)
				echo -ne 'status: Check compatibility'
				echo -ne "\rstatus: "
				_check_backup_compatibility "${2}"
				;;
		esac
		case "${4}" in
			-r|--respring)
				_respring='1'
				;;
			-s|--skip-check)
				if [ -z "${_respring}" ]; then
					echo -ne 'status: Skip compatibility check'
					echo -ne "\rstatus: "
				fi
				;;
			*)
				if [ -n "${_respring}" ]; then
					echo -ne 'status: Check compatibility'
					echo -ne "\rstatus: "
					_check_backup_compatibility "${2}"
				fi
				;;
		esac
		sleep 1
		echo -ne "status:                         \r"
		echo -ne 'status: Freeing backup files'
		echo -ne "\rstatus: "
		case "${2}" in
			*.ftb)
				_release_backup "${2}"
				;;
			*)
				_release_backup "${_backup_path}/${2}.ftb"
				;;
		esac
		if [ -n "${_respring}" ]; then
			killall -9 backboardd
		fi
		;;
	-R|--read)
		if [ -n "${2}" ]; then
			_read_information "${2}" "${3}"
		else
			_read_plist
			if [ -n "${_plist}" ]; then
				echo "${_plist}"
			fi
		fi
		;;
	-p)
		plutil "${_preferences_path}/${_package}.plist"
		;;
	*)
		_print_help
		exit 1
		;;
esac
