#!/bin/bash #$1 is an action, like install #$2 is app name, like Arduino DIRECTORY="$(readlink -f "$(dirname "$0")")" function error { echo -e "\e[91m$1\e[39m" exit 1 } if [ -z "$1" ];then error "You need to specify an operation, and in most cases, which app to operate on." fi mkdir -p "${DIRECTORY}/data/status" "${DIRECTORY}/data/update-status" #determine if host system is 64 bit arm64 or 32 bit armhf if [ ! -z "$(file "$(readlink -f "/sbin/init")" | grep 64)" ];then arch=64 elif [ ! -z "$(file "$(readlink -f "/sbin/init")" | grep 32)" ];then arch=32 else error "Failed to detect OS CPU architecture! Something is very wrong." fi if [ "$1" == 'install' ];then #INSTALL #for this operation, a program name must be specified. if [ -z "$2" ];then error "For this operation, you must specify which app to operate on." elif [ ! -d "${DIRECTORY}/apps/$2" ];then error "${DIRECTORY}/apps/$2 does not exist!" fi #ensure an install script is not already running if ps -C install &>/dev/null || ps -C install-32 &>/dev/null || ps -C install-64 &>/dev/null;then echo "An install script is already running. Pi-Apps will wait until that one finishes before installing $2." | yad --text-info \ --title="Waiting" --window-icon="${DIRECTORY}/icons/logo.png" --center --width=500 --height=100 \ --button=OK!"${DIRECTORY}/icons/check.png":0 --fontname=12 --timeout=10 --timeout-indicator=top while ps -C install ;do sleep 1; done fi #if already installed then ask for confirmation if [ "$(cat "${DIRECTORY}/data/status/${2}" )" == 'installed' ];then yad --text="$2 is already installed. Are you sure you want to install it again?" \ --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" \ --button=No!"${DIRECTORY}/icons/exit.png":1 --button=Yes!"${DIRECTORY}/icons/check.png":0 || exit 0 fi #analytics if [ "$(cat "${DIRECTORY}/data/settings/Enable analytics")" == 'Yes' ];then #this is used to let Botspot see the daily installs for each app. #Botspot created an individual bitly link for each app. When you install an app, the cooresponding bit.ly link is "clicked" by a headless chromium-browser instance. #Chromium accesses the bit.ly link, then exits. #Allowing analytics does not identify you, or any personal information about you. Botspot can only see the number of "clicks" per day, as well as the top 10 countries of origin. bitlylink="https://bit.ly/pi-apps-install-$(echo "$2" | tr -d ' ' | sed 's/[^a-zA-Z]//g')" chromium-browser --headless "$bitlylink" &>/dev/null & fi #determine which script to run if [ -f "${DIRECTORY}/apps/${2}/install-32" ] && [ $arch == 32 ];then installscript="${DIRECTORY}/apps/${2}/install-32" scriptname='install-32' elif [ -f "${DIRECTORY}/apps/${2}/install-64" ] && [ $arch == 64 ];then installscript="${DIRECTORY}/apps/${2}/install-64" scriptname='install-64' elif [ -f "${DIRECTORY}/apps/${2}/install" ];then installscript="${DIRECTORY}/apps/${2}/install" scriptname='install' else error "It appears $2 does not have an install-${arch} script suitable for your ${arch}-bit OS." fi lxterminal --title="Installing $2 with $scriptname script" -e " cd $HOME echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\" if \"$installscript\" ; then echo 'installed' > \"${DIRECTORY}/data/status/${2}\" echo -en '\n\e[42m\e[30mCommand succeeded.\e[39m\e[49m\nClosing in 10 seconds.' sleep 10 else echo -en '\n\e[41m\e[30mCommand failed!\e[39m\e[49m\nClose this window to exit.' read enter #technically you could press Enter to exit. fi" #wait until script is done before this command exits sleep 2 while ps -C install &>/dev/null || ps -C install-32 &>/dev/null || ps -C install-64 &>/dev/null;do sleep 1; done elif [ "$1" == 'uninstall' ];then #UNINSTALL #for this operation, a program name must be specified. if [ -z "$2" ];then error "For this operation, you must specify which app to operate on." elif [ ! -d "${DIRECTORY}/apps/$2" ];then error "${DIRECTORY}/apps/$2 does not exist!" fi #ensure an uninstall script is not already running if ps -C uninstall ;then echo "An uninstall script is already running. Pi-Apps will wait until that one finishes before starting this one." | yad --text-info \ --title="Waiting" --window-icon="${DIRECTORY}/icons/logo.png" --center --width=500 --height=100 \ --button=OK!"${DIRECTORY}/icons/check.png":0 --fontname=12 --timeout=10 --timeout-indicator=top while ps -C uninstall ;do sleep 1; done fi #if already uninstalled then ask for confirmation if [ "$(cat "${DIRECTORY}/data/status/${2}" )" == 'uninstalled' ];then yad --text="$2 is already uninstalled. Are you sure you want to uninstall it again?" \ --text-align=center --center --title='Quick question' --window-icon="${DIRECTORY}/icons/logo.png" \ --button=No!"${DIRECTORY}/icons/exit.png":1 --button=Yes!"${DIRECTORY}/icons/check.png":0 || exit 0 fi lxterminal --title="Uninstalling $2" -e " cd $HOME echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\" if \"${DIRECTORY}/apps/${2}/uninstall\" ; then echo 'uninstalled' > \"${DIRECTORY}/data/status/${2}\" echo -en '\n\e[42m\e[30mCommand succeeded.\e[39m\e[49m\nClosing in 10 seconds..' sleep 10 else echo 'corrupted' > \"${DIRECTORY}/data/status/${2}\" echo -en '\n\e[41m\e[30mCommand failed!\e[39m\e[49m\nClose this window to exit.' read enter #technically you could press Enter to exit. fi" #wait until script is done before this command exits sleep 2 while ps -C uninstall >/dev/null;do sleep 1; done elif [ "$1" == 'update' ];then #UPDATE #for this operation, a program name must be specified. if [ -z "$2" ];then error "For this operation, you must specify which app to operate on." fi #HIDDEN FEATURE - if $3 equals nofetch, then don't download github repo. Useful for updating multiple apps at maximum speed if [ "$3" == 'nofetch' ] && [ -d "${DIRECTORY}/update" ];then true else rm -rf "${DIRECTORY}/update" && mkdir "${DIRECTORY}/update" && cd "${DIRECTORY}/update" || error "failed to enter the update directory!" git clone "$(cat "${DIRECTORY}/etc/git_url")" || error "failed to clone repository!" fi newhash=$(find "${DIRECTORY}/update/pi-apps/apps/${2}" -type f -print0 | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}') oldhash=$(find "${DIRECTORY}/apps/${2}" -type f -print0 | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}') #detect which installation script exists and get the hash for that one if [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install-32" ];then scriptname='install-32' elif [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install-64" ];then scriptname='install-64' elif [ -f "${DIRECTORY}/update/pi-apps/apps/${2}/install" ];then scriptname='install' else error "No install script found for the $2 app! Please report this to Botspot." fi newinstallhash=$(sha1sum "${DIRECTORY}/update/pi-apps/apps/${2}/${scriptname}" | awk '{print $1}') oldinstallhash=$(sha1sum "${DIRECTORY}/apps/${2}/${scriptname}" | awk '{print $1}') #echo -e "newinstallhash: $newinstallhash\noldinstallhash: $oldinstallhash" #echo -e "newhash: $newhash\noldhash: $oldhash" if [ "$newhash" == "$oldhash" ];then echo "$2 is identical to the online version. Nothing to do!" exit 0 else echo "$2 is not identical to the online version. Reinstalling..." fi #else installback=no #if install was changed if [ ! "$newinstallhash" == "$oldinstallhash" ];then #if installed already if [ "$(cat "${DIRECTORY}/data/status/${2}")" == 'installed' ];then installback=yes #uninstall it using a recursive script instance "${DIRECTORY}/manage" uninstall "$2" fi fi #move old program to trash gio trash "${DIRECTORY}/apps/${2}" 2>/dev/null #failsafe [ -d "${DIRECTORY}/apps/${2}" ] && error "${DIRECTORY}/apps/${2} still exists, despite trying to delete it!" #copy new version to apps/ cp -rf "${DIRECTORY}/update/pi-apps/apps/${2}" "${DIRECTORY}/apps/${2}" if [ "$installback" == 'yes' ] && [ "$(cat "${DIRECTORY}/data/settings/Reinstall after update")" != 'No' ];then echo "$2 was originally installed before updating. Reinstalling the new version now." #install it using a recursive script instance "${DIRECTORY}/manage" install "$2" fi echo -e "\e[92m${2} was updated successfully.\e[39m" elif [ "$1" == 'check-all' ];then #CHECK-ALL #for this operation, a program name cannot be specified. #hidden flag: if $2 is 'installedonly', then only check for updates for those apps that are installed if [ "$2" == 'installedonly' ];then installedonly=1 else installedonly=0 fi rm -rf "${DIRECTORY}/update" && mkdir "${DIRECTORY}/update" && cd "${DIRECTORY}/update" || error "failed to enter the update directory!" git clone "$(cat "${DIRECTORY}/etc/git_url")" || error "failed to clone repository!" #generate entire app list, combine local apps and online apps to one list applist="$(echo -e "$(ls "${DIRECTORY}/update/pi-apps/apps")\n$(ls "${DIRECTORY}/apps")" | sort | uniq)" if [ $installedonly == 1 ];then #installedonly flag enabled. Remove apps that are uninstalled echo "installedonly flag set to 1" 1>&2 PREIFS="$IFS" IFS=$'\n' for app in $applist do if [ ! -f "${DIRECTORY}/data/status/${app}" ] || [ "$(cat "${DIRECTORY}/data/status/${app}")" == 'uninstalled' ];then #if app is uninstalled, then remove it from the list. applist="$(echo "$applist" | grep -vx "$app")" echo "Removing ${app} from list because it is uninstalled." 1>&2 fi done IFS="$PREIFS" fi applist="$(echo "$applist" | tr '\n' '|')" #echo "App list: $applist" 1>&2 updatable='' PREIFS="$IFS" IFS="|" for app in $applist do #echo "app: $app" newhash=$(find "${DIRECTORY}/update/pi-apps/apps/${app}" -type f -print0 2>/dev/null | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}') oldhash=$(find "${DIRECTORY}/apps/${app}" -type f -print0 2>/dev/null | xargs -0 sha1sum | awk '{print $1}' | sha1sum | awk '{print $1}') #echo -e "newhash: $newhash\noldhash: $oldhash" 1>&2 if [ "$newhash" == "$oldhash" ];then echo -e "${app} is identical\e[90m to the online version. Nothing to do!\e[39m" 1>&2 echo 'latest' > "${DIRECTORY}/data/update-status/${app}" else if [ ! -d "${DIRECTORY}/apps/${app}" ];then echo -e "\e[97m${app} does not exist locally.\e[39m Adding to updatable list." 1>&2 echo 'new' > "${DIRECTORY}/data/update-status/${app}" #in this case, add to updatable list updatable="${updatable}|${app}" elif [ ! -d "${DIRECTORY}/update/pi-apps/apps/${app}" ];then echo -e "\e[97m${app} only exists locally.\e[39m Will not add to updatable list." 1>&2 echo 'local' > "${DIRECTORY}/data/update-status/${app}" #in this case, do not add to updatable list else echo -e "\e[97m${app} exists in both locations, but online version is newer\e[39m. Adding to updatable list." 1>&2 echo 'updatable' > "${DIRECTORY}/data/update-status/${app}" #in this case, add to updatable list updatable="${updatable}|${app}" fi fi done IFS="$PREIFS" #remove initial '|' character updatable="${updatable:1}" if [ -z "$updatable" ];then updatable='.' fi echo -e "\e[97mThese apps can be updated:\n${updatable}" elif [ "$1" == 'update-all' ];then #UPDATE-ALL #for this operation, a program name cannot be specified. PREIFS="$IFS" IFS='|' updatable="$("${DIRECTORY}/manage" check-all | tail -1)" echo "Updatable: ${updatable}EOU" for updateapp in $updatable do echo "updating $updateapp" #update it using a recursive script instance echo "${DIRECTORY}/manage update $updateapp" "${DIRECTORY}/manage" update "$updateapp" || exit 1 done IFS="$PREIFS" echo -e '\e[92mOperation completed successfully!\e[39m' else error "Did not understand ${1}. It must be either "\'"install"\'", "\'"uninstall"\'", "\'"update"\'", "\'"check-all"\'", or "\'"update-all"\'"." fi