#!/bin/bash
####################################################################
# Prey Core Basic Functions - by Tomas Pollak (bootlog.org)
# URL: http://preyproject.com
# License: GPLv3
####################################################################

log(){
	eval echo -e '"$1"' "$log_output"
}

####################################################################
# xml parsing functions
####################################################################

get_key(){
	echo "$1" | sed -e 's/.*\/\(.*\)>/\1/' -e 'y/-/_/' # we also replace -'s to _'s
}

get_value(){
	echo "$1" | sed 's/.*>\([^<].*\)<.*/\1/'
}

# expects attr name, returns value
# example: get_attribute 'name' $line
get_attribute(){
	echo "$2" | sed "s/.*$1=\"\([^\"]*\)\".*/\1/"
	# echo "$2" | sed -e "s/.*$1=\([a-z_\"']*\).*/\1/" -e "s/[^a-z_]//g"
}

####################################################################
# setting configs
####################################################################

# key, value
set_config(){
	if [ -n "$2" ]; then
		# filter $() or backticks to prevent unwanted command execution
		local clean_val=$(echo "$2" | sed "s/[\`|\$(].*[\`|)]//g")
		eval "${1}=\"$clean_val\""
	fi
}

# module, key, value
set_module_config(){
	set_config "${1}__${2}" "$3"
}

save_config_value(){
	local key="$1"
	local val="$2"

	if [ "$val" == "true" ]; then
		local val='y'
	elif [ "$val" == 'false' ]; then
		local val='n'
	fi

	# use % as delimiter instead of / so URLs get set correctly
	sed -i.backup -e "s%^$key=.*%$key='$val'%" "$config_file" 2> /dev/null
	local rs=$?
	if [ -f "$config_file" ]; then
		rm -f "$config_file.backup" 2> /dev/null
	else
		mv "$config_file.backup" "$config_file" 2> /dev/null
	fi

	return $rs
}

####################################################################
# local var storage
####################################################################

# fetches a var and then assigns it as $value
# expects the name of hash and then the name of var
get_var(){
	HASH="$1[*]"
	local ${!HASH}
	eval 'echo ${'"${2}"'}'
}

store_var(){
	eval $1[\${#$1[*]}]="$2"
}

store_key_value(){
	store_var "$1" "$2=\"$3\""
}

# if you need to fetch a specific trace or file
get_trace(){
	get_var traces ${1}__$2
}

get_file(){
	get_var files ${1}__$2
}

####################################################################
# list generators for report
####################################################################

separator='########################################################'

generate_query_string(){
	local default_prefix="\n -F "
	[ -n "$2" ] && prefix="$2" || prefix="$default_prefix"
	for t in $(eval echo \${$1[@]}); do
		local start=`echo ${t%%=*}`
		local index=`echo $(( ${#start} + 1 ))`
		if [ "$prefix" != "$default_prefix" ]; then
			local trace_field=`echo "$start" | sed "s/^\([^_].*\)__\(.*\)/[\1][\2]/"`
		else
			local trace_field=`echo "$start" | sed 's/^\([^_].*\)__\(.*\)/\1[\2]/'`
		fi
		echo -n "${prefix}${trace_field}=${t:index}"
	done
}

generate_list(){
	for t in $(eval echo \${$1[@]}); do
		local current_node=$(echo $t | sed 's/__.*//')
		[ "$current_node" != "$previous_node" ] && echo -e "$separator\n# $current_node\n$separator\n"
		# removes module name and replaces _'s with whitespaces
		echo -e "$t\n" | sed -e 's/^\([^_].*\)__/ :: /' -e 's/%20/ /g' -e 's/_/ /'
		local previous_node=$current_node
	done
}

####################################################################
# trace & file functions
####################################################################

add_trace(){
	log " ++ Adding trace for $current_module: $1"
	store_key_value 'traces' "${current_module}__$1" "$(urlencode "$2")"
}

remove_traces(){
	unset -v traces
	log " -- Dropping all traces!"
}

add_file(){
	if [ -f "$2" ]; then
		log " ++ Adding file for $current_module: $1 -> $2"
		store_key_value 'files' "${current_module}__$1" "$2"
	else
		log " ${red}!!${color_end} Couldn't find file at $2!"
	fi
}

list_files(){
	# log " -- ${#files[*]} files gathered!"
	for f in "${files[@]}"; do
		if [ "$post_method" == 'http' ]; then
			# echo -e "-F $f" | sed -e 's/=/=@/'
			echo "-F $f" | sed 's/^\([^_].*\)__\([^=].*\)=\(.*\)/\1[\2]=@\3/'
		else # just list the file paths
			echo $f | sed 's/^.*=\(.*\)/\1/'
		fi
	done
}

remove_files(){
	for f in "${files[@]}"; do
		file=`echo $f | sed 's/^.*=\(.*\)/\1/'`
		rm -f "$file"
		log " -- Removed $file"
	done
	unset -v files
}

####################################################################
# delay functions, mac and linux
####################################################################

# echoes random number between 1 and $1 (first argument passed)
get_random_number(){
	echo $[ ( $RANDOM % $1 ) + 1 ]
}

# returns 1 if cron entry is set to one our intervals
one_hour_interval(){
	echo "$current_delay" | grep "/" > /dev/null || echo 1
}

# returns cron delay depending on device's state:
# when missing, every X minutes, otherwise every hour on minute X
get_delay_for(){
	[ -n "$device_missing" ] && echo '*/'$1' * * * *' || echo "$1 * * * *"
}

get_current_delay(){
	# crontab -l | grep prey | sed "s/^..\([0-9]*\).*/\1/"
	crontab -l 2> /dev/null | grep prey | head -1 | sed 's/ \/.*//'
}

update_execution_delay(){
	local full_path=$(full_path "$base_path")
	(crontab -l 2> /dev/null | grep -v prey; echo "${1}" "${full_path}/prey.sh > /var/log/prey.log 2>&1") | crontab -
}

# if device is missing we'll make sure the current delay matches the one
# on the server. otherwise we'll make sure it is set to the regular interval.
check_and_update_delay(){
	local current_delay=$(get_current_delay)
	if [ -n "$device_missing" ]; then
		local new_delay=$(get_delay_for "$1")
		if [ "$current_delay" != "$new_delay" ]; then
			log " -- Setting frequency to $1!"
			update_execution_delay "$new_delay"
		fi
	elif [[ -z "$current_delay" || -z "$(one_hour_interval)" ]]; then
		log " -- Setting delay to regular interval."
		[ "$os" == "windows" ] && local new_delay=$(get_delay_for 60) || local new_delay="$(get_delay_for $(get_random_number 59))"
		update_execution_delay "$new_delay"
	fi
}

ensure_trigger_loaded(){
	[ -z "$(is_trigger_loaded)" ] && reload_trigger
}

running_from_cron(){
	# tty -s
	# [ $? -eq 1 ] && echo 1
	[ "$TERM" == "dumb" ] && echo 1
}

####################################################################
# http functions
####################################################################

send_request(){
	local curl_arguments="$2"
	local headers_file="$tmpbase/prey-curl-headers.txt"
	response_body=$(getter $curl_arguments -L $1 --dump-header "$headers_file")
	if [[ $? == 0 && -f "$headers_file" ]]; then
		response_headers=$(cat "$headers_file" && rm -f "$headers_file" 2> /dev/null)
		get_status_code
	else
		log " !! Unable to get response from server!"
		# error_exit "Exiting..."
	fi
}

get_status_code(){
	# if we get a redirect response, we should capture the last http status code
	response_status=$(echo -e "$response_headers" | grep 'HTTP/' | tail -1 | cut -d" " -f2)
}

# we may eventually use a specific header for Prey
get_header_value(){
	echo "$response_headers" | grep "$1" | tail -1 | sed 's/.*: \([a-z\/-]*\).*/\1/'
}

# fetches last response if online actions were enabled for the device
get_last_response(){
	response_body=$(cat "$last_response")
}

####################################################################
# check mode functions
####################################################################

verify_installation(){
	log " -- Checking if cron daemon is running..."
	if [ -n `is_process_running 'cron'` ]; then
		log " -- Cron daemon found."
	else
		log " !! Cron daemon not found! Try running it with 'sudo /etc/init.d/cron start'."
	fi
	log " -- Checking for crontab entry..."
	local result=`crontab -l | grep 'prey.sh' | wc -l 2> /dev/null`
	if [ "$result" -gt 0 ]; then
		log " -- Found!"
	else
		log " !! Not found!\n -> It seems Prey's crontab entry was removed after installation. Please set again the running interval."
	fi
}

verify_smtp_settings(){

	if [ "$mail_to" != "mailbox@domain.com" ]; then
		log " -- Target mailbox set."
	else
		log " !! Target mailbox not set!"
	fi

	if [ "$smtp_username" != "username@gmail.com" ]; then
		log " -- SMTP username set."
	else
		log " !! SMTP username not set!"
	fi

	if [ "$(decrypt $smtp_password)" != "" ]; then
		log " -- Password seems to be fine as well."
	else
		log " !! Password not set! (Remember it should be base64 encrypted)."
	fi

}

####################################################################
# self setup functions
####################################################################

self_setup(){

	log ' -- Gathering PC info...'
	get_pc_info

	log ' -- Sending request to Control Panel...'

	[ "$os" == "linux" ] && local os_name=$distro_name || local os_name=$os
	local params="device[title]=$pc_name&device[device_type]=$pc_type&device[os_version]=$pc_os_version&device[os]=$(capitalize $os_name)"

	send_request "$check_url/devices.xml" "--connect-timeout 10 -u $api_key:x -d "$params""

	if [ $response_status == "201" ]; then

		log ' -- Device succesfully added! Applying configuration...'
		device_key=`echo "$response_body" | grep "<key>" | sed 's/.*<key>\(.*\)<\/key>.*/\1/'`

		save_config_value device_key "$device_key"

		if [ $? == 1 ]; then
			log " -- There was a problem saving the configuration. You probably don't have permissions to modify $base_path/config."
		else
			log " -- All set. Assigned key is $device_key."
		fi

	else

		log " -- Couldn't add this device to your account. Make sure you have available slots!\n"
		exit 1

	fi

}

####################################################################
# reverse ssh tunnel
####################################################################

open_reverse_tunnel(){

	local local_tunnel_port="$1"

	if [ -z "$remote_tunnel_port" ]; then
		log " -- Unknown remote port to connect to. Please try again later."
		return 1
	fi

	if [ ! -f "$tmpbase/prey-tunnel-${local_tunnel_port}.pid" ]; then
		log " -- Opening reverse tunnel from ${local_tunnel_port} to ${remote_tunnel_port} as ${remote_tunnel_user} on ${remote_tunnel_host}..."
		reverse_tunnel_command
		if [ -f "prey-tunnel.pid" ]; then
			mv "prey-tunnel.pid" "$tmpbase/prey-tunnel-${local_tunnel_port}.pid"
			log " -- Tunnel open and ready to serve!"
			update_device_info_with "device[active_tunnel]=${remote_tunnel_host}:${remote_tunnel_port}"
			return 0
		else
			log " -- Couldn't open reverse tunnel!"
			return 1
		fi
	else
		echo " -- Reverse tunnel already on!"
		return 0
	fi
}

close_reverse_tunnel(){
	local tunnel_pidfile="$tmpbase/prey-tunnel-${1}.pid"
	if [ -f "${tunnel_pidfile}" ]; then
		local tunnel_pid=`cat "$tunnel_pidfile"`
		log " -- Closing reverse tunnel on port ${1} with PID ${tunnel_pid}..."
		kill -9 $tunnel_pid &> /dev/null
		if [ $? == 1 ]; then
			log " -- Seems like the reverse tunnel was already closed!"
		fi
		rm -f "$tunnel_pidfile"
	fi
}
