vtenext/vteUpdater.sh
2021-04-28 20:10:26 +02:00

2872 lines
71 KiB
Bash

#! /bin/bash
#######################################
# SPDX-FileCopyrightText: 2009-2020 Vtenext S.r.l. <info@vtenext.com>
# SPDX-License-Identifier: AGPL-3.0-only
#######################################
VERSION="1.36"
#
# Changelog
#
# Version 1.36
# . Fix wrong group detection
#
# Version 1.35
# . Read user and group from config.inc file
#
# Version 1.34
# . Ignore tickets lang file
#
# Version 1.33
# . Fix rollback with new vteversion file
#
# Version 1.32
# . Exclude tabdata files from diff
#
# Version 1.31
# . Support for vteversion file
#
# Version 1.30
# . Fix for old md5sum versions
#
# Version 1.29
# . Support for user supplied file hashes
# . Maintenance mode set before backup
#
# Version 1.28
# . Support for community edition
#
# Version 1.27
# . Support for automatic updates
#
# Version 1.26
# . Alert for old PHP version
# . Added option to only delete files
#
# Version 1.25
# . Fix accesskey parameter
#
# Version 1.24
# . Ask to copy post patches if refusing rollback
#
# Version 1.23
# . Rollback tries first to drop the old database
#
# Version 1.22
# . Mysql backup also save triggers and events
#
# Version 1.21
# . Mysql backup also save stored procedures
#
# Version 1.20
# . Read safe update ranges from server
# . Support for old files removal
#
# Version 1.19
# . Added safe update ranges
#
# Version 1.18
# . Support for new maintenance mode
#
# Version 1.17
# . Minor bugfix
#
# Version 1.16
# . Support for vteversion file
#
# Version 1.14 and 1.15
# . Support for update notes
#
# Version 1.13
# . Added patches cmd options
#
# Version 1.12
# . Added custom tags option
#
# Version 1.11
# . Fix for old tar command
#
# Version 1.10 and 1.9
# . Fix file comparison bug
#
# Version 1.8
# . Fix backup bug
#
# Version 1.7
# . Added support for mysqli database
#
# Version 1.6
# . Fixed VTE Url
#
# Version 1.5
# . Adjusted minumum revision
#
# Version 1.4
# . Fix parsing of username
#
# Version 1.3
# . Fix detection of errors after update
#
# Version 1.2
# . Fix for install folder
# . Fix timeout issue for some updates
#
# Version 1.1
# . Self update ability
# . Some minor bugfixes
#
# Version 1.0
# . First beta release
#
# Documentation : modules/SDK/doc/vteUpdater.pdf
#
# CONSTANTS
DEBUG_LEVEL_DEBUG=4
DEBUG_LEVEL_INFO=3
DEBUG_LEVEL_WARNING=2
DEBUG_LEVEL_ERROR=1
RETCODE_OK=0
RETCODE_FAIL=1
RETCODE_BREAK=2
RETCODE_OKROLLBACK=3
CRMVILLAGE_VTE="https://partner.vtecrm.net/"
UPDATE_SERVER="https://autoupdate.vtecrm.net/"
UPDATE_SERVER_CE="https://autoupdatece.vtecrm.net/"
# CONFIG VARIABLES
[ -t 1 ] && USECOLORS=1 || USECOLORS=0
DEBUG=$DEBUG_LEVEL_INFO
LOGDEBUG=$DEBUG_LEVEL_DEBUG
VTEDIR=./
WORKDIR="$VTEDIR"vte_updater/
PACKAGESDIR="$WORKDIR"packages/
BACKUPDIR="$WORKDIR"backup/
TEMPDIR="$WORKDIR"temp/
# these patterns are used to find files that shouldn't be checked
EXCLUDE_CHECK=(
"tabdata.php"
"parent_tabdata.php"
".*ckeditor/lang/.*"
".*jscalendar/lang/.*"
".*plupload/i18n/.*"
".*dataTables/i18n/.*"
# ignore some languages, since there were problems in old releases
"modules/HelpDesk/language/*.lang.php"
".*de_de.lang.php"
".*de_de.lang.js"
".*lang_de_de.js"
".*pt_br.lang.php"
".*pt_br.lang.js"
".*lang_pt_br.js"
".*nl_nl.lang.php"
".*nl_nl.lang.js"
".*lang_nl_nl.js"
)
# GLOBAL VARIABLES
STATUS=""
VTEREVISION=0
ISCOMMUNITY=0
REVRANGES=""
VTEURL=""
VTEBRANCH=""
LOGFILE=""
DIFFLOG=""
UPDATELOG=""
NOTESLOG=""
RECFILE=""
DESTREVISION=0
HTTPD_USER="www-data"
HTTPD_GROUP="www-data"
BACKUPFILE=""
BACKUPDELFILE=""
BACKUPDBFILE=""
PACKAGEFILE=""
SRCFILE=""
SRCFILE_DIR=""
DELFILE=""
DELFILE_DIR=""
PACKAGE_DIR=""
SELFUPDATE="vteSelfUpdate.php"
EXTRATAGS=()
EXTRATAGSFILE=""
PREPATCHDIR=""
POSTPATCHDIR=""
FILEHASHES=""
ROLLBACKOK=0
USERNAME=""
ACCESSKEY=""
HASHEDKEY=""
# command line flags
WWWUSERS=""
SKIPFILECHECK=0
SKIPFILEREMOVE=0
SKIPDBACKUP=0
SKIPVTEBACKUP=0
SKIPCRON=0
ONLYBACKUP=0
ONLYROLLBACK=0
ONLYDELETE=0
ONLYFILECHECK=0
SKIPUPGRADE=0
FORCEUPGRADE=0
ONLYUPGRADE=0
USELOCALPKG=""
USELOCALSRCPKG=""
USELOCALDELPKG=""
ENABLEVTE=0
BATCH=0
# compatibility_opts
COMP_TAR_VCS=1
# FUNCTIONS
write_log () {
local now=$(date +"%Y-%m-%d %H:%M:%S")
if [ -n "$LOGFILE" -a -w "$LOGFILE" ]; then
echo "[$now] $1" >> "$LOGFILE"
fi
}
write_rec () {
local now=$(date +"%Y-%m-%d %H:%M:%S")
if [ -n "$RECFILE" -a -w "$RECFILE" ]; then
echo "[$now] $1" >> "$RECFILE"
fi
}
print_text () {
echo $1
}
debug () {
[ $DEBUG -ge $DEBUG_LEVEL_DEBUG ] && print_text "${COLOR_WHITE}[DEBUG]${COLOR_NORMAL} $1"
[ $LOGDEBUG -ge $DEBUG_LEVEL_DEBUG ] && write_log "[DEBUG] $1"
}
info () {
[ $DEBUG -ge $DEBUG_LEVEL_INFO ] && print_text "${COLOR_CYAN}[INFO]${COLOR_NORMAL} $1"
[ $LOGDEBUG -ge $DEBUG_LEVEL_INFO ] && write_log "[INFO] $1"
}
warning () {
[ $DEBUG -ge $DEBUG_LEVEL_WARNING ] && print_text "${COLOR_YELLOW}[WARNING]${COLOR_NORMAL} $1"
[ $LOGDEBUG -ge $DEBUG_LEVEL_WARNING ] && write_log "[WARNING] $1"
}
error () {
[ $DEBUG -ge $DEBUG_LEVEL_ERROR ] && print_text "${COLOR_RED}[ERROR]${COLOR_NORMAL} $1"
[ $LOGDEBUG -ge $DEBUG_LEVEL_ERROR ] && write_log "[ERROR] $1"
}
init_colors () {
# first set all colors to 0
COLOR_BOLD=""
COLOR_UNDERLINE=""
COLOR_STANDOUT=""
COLOR_NORMAL=""
COLOR_BLACK=""
COLOR_RED=""
COLOR_GREEN=""
COLOR_YELLOW=""
COLOR_BLUE=""
COLOR_MAGENTA=""
COLOR_CYAN=""
COLOR_WHITE=""
if [ $USECOLORS -eq 0 ]; then
return
fi
# then check if we are using a real terminal and get the color codes
command -v "tput" > /dev/null 2>&1 && [ -t 1 ]; {
local NCOL=$(tput colors)
if [ -n "$NCOL" -a $NCOL -ge 8 ]; then
COLOR_BOLD="$(tput bold)"
COLOR_UNDERLINE="$(tput smul)"
COLOR_STANDOUT="$(tput smso)"
COLOR_NORMAL="$(tput sgr0)"
COLOR_BLACK="$(tput setaf 0)"
COLOR_RED="$(tput setaf 1)"
COLOR_GREEN="$(tput setaf 2)"
COLOR_YELLOW="$(tput setaf 3)"
COLOR_BLUE="$(tput setaf 4)"
COLOR_MAGENTA="$(tput setaf 5)"
COLOR_CYAN="$(tput setaf 6)"
COLOR_WHITE="$(tput setaf 7)"
fi
}
}
version_lte() {
[ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]
}
version_lt() {
[ "$1" = "$2" ] && return 1 || version_lte $1 $2
}
# reads a password
ask_pwd () {
# $1 = text
local PWD=""
echo -n "$1: "
while IFS= read -r -s -n 1 char; do
if [[ $char == $'\0' ]]; then
echo ""
break
elif [[ $char == $'\177' ]]; then
if [ -n "$PWD" ]; then
echo -e -n '\b \b'
PWD="${PWD%?}"
fi
else
echo -n "*"
PWD="${PWD}${char}"
fi
done
write_rec "$1: *****"
write_log "$1: *****"
_RET="$PWD"
}
ask () {
# $1 = question
# $2 = possible answers
# $3 = convert to lowercase (1/0, default =1)
local question="$1 "
local lcconvert=1
if [ -n "$3" -a "$3" = "0" ]; then
lcconvert=0
fi
if [ -n "$2" ]; then
question="$question""($2) "
fi
question="$question"": "
echo ""
echo -n "$question"
ans=""
while [ -z "$ans" ]; do
# read the answer
read ans
if [ -z "$ans" ]; then
echo -n "Please answer $2 : "
fi
done
# transform to lowercase
if [ $lcconvert -eq 1 ]; then
ans=$(echo "$ans" | tr '[:upper:]' '[:lower:]')
fi
write_rec "$question $ans"
write_log "$question $ans"
_RET=$ans
}
is_valid_user () {
# $1 = username to test
# return 0=invalid
_RET=0
id -u "$1" 1>/dev/null 2>&1 && _RET=1
}
is_valid_group () {
# $1 = groupname to test
# return 0=invalid
_RET=0
getent group "$1" 1>/dev/null 2>&1 && _RET=1
}
# retrieves the user and group of the webserver
# this is a very rough way to do things
get_httpd_user () {
local USR=""
local GRP=""
if [ -n "$WWWUSERS" ]; then
USR=$(echo -n $WWWUSERS | cut -f 1 -d ":")
GRP=$(echo -n $WWWUSERS | cut -f 2 -d ":")
is_valid_user "$USR"
if [ $_RET -eq 0 ]; then
error "The chosen user doesn't exist on this system"
exit $RETCODE_FAIL
fi
is_valid_group "$GRP"
if [ $_RET -eq 0 ]; then
error "The chosen group doesn't exist on this system"
exit $RETCODE_FAIL
fi
HTTPD_USER=$USR
HTTPD_GROUP=$GRP
debug "Using $HTTPD_USER:$HTTPD_GROUP as user and group for files"
return
fi
debug "Detecting web server user and group..."
# test for config.inc.php
USR=$(php -r 'require("config.inc.php"); if (is_array($new_folder_storage_owner)) echo $new_folder_storage_owner["user"];')
GRP=$(php -r 'require("config.inc.php"); if (is_array($new_folder_storage_owner)) echo $new_folder_storage_owner["group"];')
# test for apache
if [ -z "$USR" -a -r "/etc/apache2/envvars" ]; then
USR=$(bash -c 'source /etc/apache2/envvars && echo $APACHE_RUN_USER')
fi
if [ -z "$GRP" -a -r "/etc/apache2/envvars" ]; then
GRP=$(bash -c 'source /etc/apache2/envvars && echo $APACHE_RUN_GROUP')
fi
# test for apache on CentOS
if [ -z "$USR" -a -r "/etc/httpd/conf/httpd.conf" ]; then
USR=$(grep "^User " /etc/httpd/conf/httpd.conf | sed 's/User\s*//')
fi
if [ -z "$GRP" -a -r "/etc/httpd/conf/httpd.conf" ]; then
GRP=$(grep "^Group " /etc/httpd/conf/httpd.conf | sed 's/Group\s*//')
fi
# TODO: other systems, other webservers...
# fallback test on file
if [ -z "$USR" ]; then
USR=$(stat -c '%U' "$VTEDIR"config.inc.php)
fi
if [ -z "$GRP" ]; then
GRP=$(stat -c '%G' "$VTEDIR"config.inc.php)
fi
if [ -n "$USR" -a -n "$GRP" ]; then
is_valid_user "$USR"
if [ $_RET -eq 1 ] ; then HTTPD_USER=$USR; fi
is_valid_group "$GRP"
if [ $_RET -eq 1 ] ; then HTTPD_GROUP=$GRP; fi
fi
if [ $BATCH -eq 0 ]; then
ask "The user and group for the web server have been detected as $HTTPD_USER:$HTTPD_GROUP. Is this correct?" "Y/N"
if [ "$_RET" = 'n' ]; then
while true; do
ask "Please type the user" ""
USR=$_RET
is_valid_user "$USR"
if [ $_RET -eq 1 ]; then
break
else
warning "The chosen user doesn't exist on this system"
fi
done
while true; do
ask "Please type the group" ""
GRP=$_RET
is_valid_group "$GRP"
if [ $_RET -eq 1 ]; then
break
else
warning "The chosen group doesn't exist on this system"
fi
done
HTTPD_USER=$USR
HTTPD_GROUP=$GRP
fi
fi
debug "Using $HTTPD_USER:$HTTPD_GROUP as user and group for files"
}
check_command () {
debug "Checking $1..."
command -v "$1" > /dev/null 2>&1 || { error "Command $1 not found."; exit $RETCODE_FAIL; }
}
check_tar_opts () {
debug "Checking tar options..."
# check exclude-vcs options, which is not available in old tar
tar --exclude-vcs --version 1>/dev/null 2>/dev/null || COMP_TAR_VCS=0
}
check_commands_comp () {
check_tar_opts
}
check_vtedir () {
debug "Checking VTE directory..."
if [ -d "$1" -a -w "$1" ]; then
# do some write tests
if [ -f "$1"config.inc.php -a -f "$1"/index.php -a -f "$1"/modules/Update/Update.php ]; then
if [ -w "$1"config.inc.php -a -w "$1"/index.php -a -w "$1"/modules ]; then
:
else
error "Some files are not writable, please check permissions first"
exit $RETCODE_FAIL;
fi
if [ -s "$1"config.inc.php -a ! -d "$1"/install -a ! -f "$1"/install.php ]; then
:
else
error "VTE is not correctly installed. Please complete the installation process first, or check if the install/ folder is still around."
exit $RETCODE_FAIL;
fi
else
error "Directory $1 doesn't seem a valid VTE directory"
exit $RETCODE_FAIL;
fi
else
error "VTE Directory $1 is not writable"
exit $RETCODE_FAIL;
fi
# check if it's a community edition
if [ -f "$1"vteversion.php -a -r "$1"vteversion.php ]; then
VERSIONFILE="vteversion.php"
else
VERSIONFILE="vtigerversion.php"
fi
grep -q -i -F "VTENEXTCE" "$1""$VERSIONFILE" && {
ISCOMMUNITY=1
}
}
create_workdir () {
debug "Creating working directory..."
mkdir -p "$1" >/dev/null 2>&1
if [ ! -w "$WORKDIR" ]; then
error "Unable to create working directory $1"
exit $RETCODE_FAIL
fi
touch "$1"testfile
if [ ! -r "$1"testfile ]; then
error "Working directory $1 is not writable"
exit $RETCODE_FAIL
fi
rm -f "$1"testfile >/dev/null 2>&1
mkdir -p "$PACKAGESDIR"
# create .htaccess
echo "Deny from all" > "$WORKDIR"".htaccess"
# create index.php
echo '<?php header("Location: ../"); ' > "$WORKDIR""index.php"
}
extract_php_global_var () {
# $1 = php file
# $2 = variable name
if [ ! -r "$1" ]; then
error "PHP file $1 not found"
exit $RETCODE_FAIL
fi
# escape variable name (so i can search for arrays also)
local varname=$(echo -n "$2" | sed 's/[][]/\\\0/g')
local ROW=$(grep -m 1 -o -E "^[[:space:]]*\\\$$varname[[:space:]]*=[[:space:]]*['\"]?.*['\"]?[[:space:]]*;" "$1")
local VALUE=$(echo -n "$ROW" | sed -r "s/.*\\\$$varname[[:space:]]*=[[:space:]]*['\"]?//" | sed -r "s/['\"]?[[:space:]]*;\$//" )
_RET=$VALUE
}
extract_php_version_var () {
# $1 = vtedir
# $2 = variable name
if [ -f "$1"vteversion.php -a -r "$1"vteversion.php ]; then
extract_php_global_var "$1"vteversion.php "$2"
elif [ -f "$1"vtigerversion.php -a -r "$1"vtigerversion.php ]; then
extract_php_global_var "$1"vtigerversion.php "$2"
else
error "Unable to find vteversion file";
exit $RETCODE_FAIL
fi
}
get_vte_revision () {
extract_php_version_var "$1" enterprise_current_build
VTEREVISION=$_RET
if [ -z "$VTEREVISION" ]; then
error "Unable to retrieve VTE revision"
exit $RETCODE_FAIL
elif [ $VTEREVISION -lt 840 ]; then
error "VTE is too old"
exit $RETCODE_FAIL
fi
# extract the branch
extract_php_version_var "$1" enterprise_current_version
VTEBRANCH=$_RET
if [ "$VTEBRANCH" = "4.5" ]; then
error "VTE 4.5 is not supported"
exit $RETCODE_FAIL
fi
# extract also vte url
extract_php_global_var "$1"config.inc.php site_URL
VTEURL="$_RET"
# replace the update server if community
if [ $ISCOMMUNITY -eq 1 ]; then
UPDATE_SERVER=$UPDATE_SERVER_CE
fi
}
init_subfolder () {
mkdir -p "$WORKDIR""$VTEREVISION"
if [ -w "$WORKDIR""$VTEREVISION" ]; then
WORKDIR="$WORKDIR""$VTEREVISION"/
BACKUPDIR="$WORKDIR"backup/
TEMPDIR="$WORKDIR"temp/
else
error "Unable to create folder ${WORKDIR}${VTEREVISION}"
exit $RETCODE_FAIL
fi
mkdir -p "$BACKUPDIR"
mkdir -p "$TEMPDIR"
}
init_log () {
local now=$(date +"%Y%m%d-%H%M%S")
local LOGNAME="$now""_update_"$VTEREVISION".log"
local RECNAME="$now""_rec_"$VTEREVISION".rec"
LOGFILE="$WORKDIR"$LOGNAME
RECFILE="$WORKDIR"$RECNAME
touch "$LOGFILE"
if [ ! -w "$LOGFILE" ]; then
error "Unable to log to file $LOGFILE"
exit $RETCODE_FAIL
fi
debug "Logging to $LOGFILE"
touch "$RECFILE"
# log for differences, truncate
DIFFLOG="$WORKDIR""differences.log"
echo -n "" > "$DIFFLOG"
}
unpack_src () {
# $1 = source file
if [ $SKIPFILECHECK -eq 1 ]; then
return
fi
debug "Unpacking source files..."
SRCFILE_DIR="$WORKDIR"src/
mkdir -p "$SRCFILE_DIR"
if [ ! -w "$SRCFILE_DIR" ]; then
error "Folder $SRCFILE_DIR is not writable"
exit $RETCODE_FAIL
fi
# do cleanup
rm -rf "$SRCFILE_DIR"*
# unpack
tar -xf "$1" -C "$SRCFILE_DIR" --strip=1
rm -f "$SRCFILE_DIR""vteUpdater.sh" 2>/dev/null
fix_permissions "$SRCFILE_DIR"
}
unpack_update () {
# $1 = update file
debug "Unpacking update files..."
PACKAGE_DIR="$WORKDIR"files"$DESTREVISION"/
mkdir -p "$PACKAGE_DIR"
if [ ! -w "$PACKAGE_DIR" ]; then
error "Folder $PACKAGE_DIR is not writable"
exit $RETCODE_FAIL
fi
# do cleanup
rm -rf "$PACKAGE_DIR"*
# unpack
tar -xf "$1" -C "$PACKAGE_DIR" --strip=1
rm -f "$PACKAGE_DIR""vteUpdater.sh" 2>/dev/null
rm -rf "$PACKAGE_DIR"install "$PACKAGE_DIR"install.php 2>/dev/null
fix_permissions "$PACKAGE_DIR"
}
unpack_del () {
# $1 = file
if [ $SKIPFILEREMOVE -eq 1 -o ! -s "$DELFILE" ]; then
# do nothing
return
fi
debug "Unpacking deleted files list ..."
DELFILE_DIR="$WORKDIR"del/
mkdir -p "$DELFILE_DIR"
if [ ! -w "$DELFILE_DIR" ]; then
error "Folder $DELFILE_DIR is not writable"
exit $RETCODE_FAIL
fi
# do cleanup
rm -rf "$DELFILE_DIR"*
# unpack
tar -xf "$1" -C "$DELFILE_DIR" --strip=1
fix_permissions "$DELFILE_DIR"
}
check_files_md5 () {
# $1 = file 1
# $2 = file 2
# return 1 = equal, 0 = differs
# md5 che
local hash1=$(md5sum "$1" | cut -f 1 -d " ")
local hash2=$(md5sum "$2" | cut -f 1 -d " ")
if [ "$hash1" = "$hash2" ]; then
_RET=1
else
_RET=0
fi
}
check_files_lines () {
# $1 = file 1
# $2 = file 2
# return 1 = equal, 0 = differs
debug "Checking file $1 line by line..."
_RET=1
# NEW FASTER CODE
# now trim extra spaces, remove blank lines and make sure file ends with newline
sed -e 's/^\s*\|\s*$//g' -e '/^$/d' -e '$a\' "$1" > "$TEMPDIR"diff_file_1
sed -e 's/^\s*\|\s*$//g' -e '/^$/d' -e '$a\' "$2" > "$TEMPDIR"diff_file_2
cmp -s "$TEMPDIR"diff_file_1 "$TEMPDIR"diff_file_2 || _RET=0
# OLD CODE: WORKS, BUT IT'S EXTREMELY SLOW
# while true; do
# # go on until we find a non empty line
# while read -r -u 3 line1; do
# line1=$(echo -n "$line1" | sed 's/^[\r\n\t ]*\|[\r\n\t ]*$//g')
# if [ -n "$line1" ]; then break; fi
# done
# while read -r -u 4 line2; do
# line2=$(echo -n "$line2" | sed 's/^[\r\n\t ]*\|[\r\n\t ]*$//g')
# if [ -n "$line2" ]; then break; fi
# done
#
# # end of files
# if [ -z "$line1" -a -z "$line2" ]; then break; fi
#
# # here I have 2 non empty lines
# if [ "$line1" = "$line2" ]; then
# # empty or difference of spaces
# continue
# else
# _RET=0
# break
# fi
# done 3<"$1" 4<"$2"
}
check_mycrmv_tags () {
# $1 = file to check
debug "Checking file $1 for custom tags..."
local LINE=$(grep -l -i -F 'mycrmv@' "$1")
_RET=1
if [ -n "$LINE" ]; then
_RET=0
return
fi
if [ ${#EXTRATAGS[@]} -gt 0 -a -n "$EXTRATAGSFILE" ]; then
debug "Checking file $1 for additional tags..."
LINE=$(grep -l -i -F -f "$EXTRATAGSFILE" "$1")
if [ -n "$LINE" ]; then
_RET=0
fi
fi
}
check_single_file () {
# $1 = file in VTE folder that will be overwritten
# $2 = file in Source folder
# return 1 = equal, 0 = differs
check_files_md5 "$1" "$2"
if [ "$_RET" = "1" ]; then
_RET=1
return
fi
check_mycrmv_tags "$1"
if [ "$_RET" = "0" ]; then
_RET=0
return
fi
check_files_lines "$1" "$2"
if [ "$_RET" = "1" ]; then
_RET=1
return
fi
_RET=0
}
check_package () {
# $1 = package file
debug "Checking package $1..."
_RET=1
local BASENAME=$(basename "$1")
local MODULE=${BASENAME%.zip}
local SRCPACK="$SRCFILE_DIR""$1"
local VTEFILE=""
local SRCFILE=""
local NEWSRCFILE=""
local SKIP=0
if [ -f "$SRCPACK" ]; then
# unpack source
PACKLIST=$(zipinfo -1 "$SRCPACK")
unzip -q -o "$SRCPACK" -d "$SRCFILE_DIR" -x '*manifest.xml'
if [ $? -gt 0 ]; then
error "Error during extraction of $1"
exit $RETCODE_FAIL
fi
# now check every file extracted
for zf in $PACKLIST; do
if [ "$zf" = 'manifest.xml' ]; then continue; fi
SRCFILE="$SRCFILE_DIR""$zf"
# check for special folders
NEWSRCFILE=""
if [[ "$zf" =~ ^cron/ ]]; then
zf="cron/modules/$MODULE/"${zf#cron/}
NEWSRCFILE="$SRCFILE_DIR""$zf"
elif [[ "$zf" =~ ^templates/ ]]; then
zf="Smarty/templates/modules/$MODULE/"${zf#templates/}
NEWSRCFILE="$SRCFILE_DIR""$zf"
fi
# and move the file to the right place
if [ -n "$NEWSRCFILE" -a -f "$SRCFILE" ]; then
mkdir -p $(dirname "$NEWSRCFILE")
mv -f "$SRCFILE" "$NEWSRCFILE"
fi
VTEFILE="$VTEDIR""$zf"
SRCFILE="$SRCFILE_DIR""$zf"
if [[ "$zf" =~ ^[^/\\]+zip$ ]]; then
# sub package
check_package "$zf"
continue
fi
SKIP=0
for exre in ${EXCLUDE_CHECK[@]}; do
if [[ "$zf" =~ $exre ]]; then
SKIP=1
break
fi
done
if [ $SKIP -eq 1 ]; then
debug "Skipping file $VTEFILE"
continue
fi
if [ -f "$VTEFILE" ]; then
check_single_file "$VTEFILE" "$SRCFILE"
if [ "$_RET" = "1" ]; then
:
# equal
elif [ "$_RET" = "0" ]; then
echo "$VTEFILE" >> "$DIFFLOG"
fi
fi
done
fi
}
check_update_files () {
if [ $SKIPFILECHECK -eq 1 ]; then
warning "File check skipped as specified on command line"
return
fi
info "Starting file comparison..."
local LIST=$(find "$PACKAGE_DIR" -type f)
local LASTDIR=$(basename "$PACKAGE_DIR")
local VTEFILE=''
local VTEFILERE=''
local SRCFILE=''
local PACKS=''
local SKIP=0
local MD5LIST=''
local MD5FOUND=''
if [ -n "$FILEHASHES" ]; then
if [ ! -r "$FILEHASHES" ]; then
error "The provided file $FILEHASHES is not readable"
exit $RETCODE_FAIL
fi
# check if the ignore-missing option is supported
md5sum --help | grep -q -F -- "--ignore-missing" && {
MD5LIST=$(LC_ALL=C md5sum --ignore-missing -c "$FILEHASHES" 2>/dev/null)
} || {
MD5LIST=$(LC_ALL=C md5sum -c "$FILEHASHES" 2>/dev/null)
}
fi
# prepare the file with the extra tags
if [ ${#EXTRATAGS[@]} -gt 0 ]; then
EXTRATAGSFILE=$(mktemp)
printf '%s\n' "${EXTRATAGS[@]}" > "$EXTRATAGSFILE"
local EXTRAPLIST=$(printf ", %s" "${EXTRATAGS[@]}")
EXTRAPLIST=${EXTRAPLIST:1}
info "Additional tags to be searched: $EXTRAPLIST"
else
EXTRATAGSFILE=""
fi
for f in $LIST; do
# strip first part
f=$(echo -n "$f" | sed "s#.*$LASTDIR/##")
if [[ "$f" =~ ^packages/ ]]; then
PACKS=$PACKS" $f"
continue
fi
VTEFILE="$VTEDIR""$f"
SRCFILE="$SRCFILE_DIR""$f"
# check if I should exclude it from the check
SKIP=0
for exre in ${EXCLUDE_CHECK[@]}; do
if [[ "$f" =~ $exre ]]; then
SKIP=1
break
fi
done
if [ $SKIP -eq 1 ]; then
debug "Skipping file $VTEFILE"
continue
fi
if [ -f "$VTEFILE" ]; then
# check for passed md5 list
if [ -n "$MD5LIST" ]; then
VTEFILERE=$(echo -n "$VTEFILE" | sed 's|[.]|\\\0|g')
MD5FOUND=$(echo "$MD5LIST" | grep "^${VTEFILERE}: ")
if [ -n "$MD5FOUND" ]; then
# ok, was one in the md5 file
echo "$MD5FOUND" | grep -q ' OK$' && {
# the hash is good, ignore file
:
} || {
# the hash is different, show it
echo "$VTEFILE" >> "$DIFFLOG"
}
# and don't do any other check!
continue
fi
fi
if [ ! -r "$SRCFILE" ]; then
debug "File $f is not in source files, probably it's a new file, checking only tags"
check_mycrmv_tags "$VTEFILE"
if [ "$_RET" = "0" ]; then
echo "$VTEFILE" >> "$DIFFLOG"
fi
continue
fi
check_single_file "$VTEFILE" "$SRCFILE"
if [ "$_RET" = "1" ]; then
:
# equal
elif [ "$_RET" = "0" ]; then
echo "$VTEFILE" >> "$DIFFLOG"
fi
fi
done
# now check packages
for p in $PACKS; do
check_package $p
done
# remove the temp file
if [ -f "$EXTRATAGSFILE" ]; then
rm -rf "$EXTRATAGSFILE"
fi
# check for file differences
if [ -s "$DIFFLOG" ]; then
warning "Some differences were found in VTE. These are the modified files. Check the log in $DIFFLOG"
cat "$DIFFLOG"
if [ $ONLYFILECHECK -eq 0 -a $BATCH -eq 0 ]; then
ask "Do you want to continue (if you answer YES you may loose some customizations)" "Y/N"
if [ "$_RET" = 'n' ]; then
info "Update interrupted"
exit $RETCODE_OK
fi
fi
else
info "No differences found"
fi
if [ $ONLYFILECHECK -eq 1 ]; then
exit $RETCODE_OK
fi
}
create_vte_backup () {
if [ $SKIPVTEBACKUP -eq 1 ]; then
warning "VTE files backup skipped as specified on command line"
return
fi
info "Creating backup of VTE files..."
if [ ! -w "$BACKUPDIR" ]; then
error "Backup directory $BACKUPDIR is not writable"
restore_cron_and_vte
exit $RETCODE_FAIL
fi
local now=$(date +"%Y%m%d-%H%M%S")
local BAKNAME="$now""_files.tgz"
BACKUPFILE="$BACKUPDIR""$BAKNAME"
if [ $COMP_TAR_VCS -eq 1 ]; then
tar --exclude-vcs --exclude "vte_updater" --exclude "storage" --exclude "vteUpdater.sh" -czf "$BACKUPFILE" "$VTEDIR"
else
tar --exclude ".git" --exclude ".svn" --exclude "vte_updater" --exclude "storage" --exclude "vteUpdater.sh" -czf "$BACKUPFILE" "$VTEDIR"
fi
if [ $? -gt 0 ]; then
error "There was an error during backup."
restore_cron_and_vte
exit $RETCODE_FAIL
fi
if [ ! -s "$BACKUPFILE" ]; then
error "There was an error during backup."
restore_cron_and_vte
exit $RETCODE_FAIL
fi
debug "Backup of files created in $BACKUPFILE"
}
get_db_config () {
# global vars because i need them later
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_server.]"
DBHOST=$_RET
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_port.]"
DBPORT=$_RET
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_username.]"
DBUSER=$_RET
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_password.]"
DBPWD=$_RET
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_type.]"
DBTYPE=$_RET
extract_php_global_var "${VTEDIR}config.inc.php" "dbconfig[.db_name.]"
DBNAME=$_RET
# clean variables
DBPORT=$(echo -n "$DBPORT" | tr -d ":")
if [ -z "$DBPORT" ]; then
DBPORT=3306
fi
}
create_db_backup () {
if [ $SKIPDBACKUP -eq 1 ]; then
warning "Database backup skipped as specified on command line"
return
fi
if [ $ONLYBACKUP -eq 0 -a $BATCH -eq 0 ]; then
ask "Do you want to backup the database?" "Y/N"
if [ "$_RET" = 'n' ]; then
ask "Without a backup you won't be able to restore the VTE in case of errors, answer 'Y' only if you already have a backup. Are you sure?" "Y/N"
if [ "$_RET" = 'y' ]; then
warning "Database backup skipped by user"
return
fi
fi
fi
info "Creating backup of database..."
get_db_config
if [ "$DBTYPE" != "mysql" -a "$DBTYPE" != "mysqli" ]; then
error "The database type ($DBTYPE) is not supported for automatic backup. Please do the backup manually, then skip this step."
clean_vte_backup
restore_cron_and_vte
exit $RETCODE_FAIL
fi
check_command mysql
check_command mysqldump
local now=$(date +"%Y%m%d-%H%M%S")
local BAKNAME="$now""_db.sql.gz"
BACKUPDBFILE="$BACKUPDIR""$BAKNAME"
mysqldump --default-character-set=utf8 --single-transaction --quick --routines --triggers --events --add-drop-table \
-h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" "$DBNAME" | gzip > "$BACKUPDBFILE"
if [ ${PIPESTATUS[0]} -gt 0 -o ${PIPESTATUS[1]} -gt 0 ]; then
error "Error during database backup"
clean_vte_backup
clean_db_backup
restore_cron_and_vte
exit $RETCODE_FAIL
fi
debug "Backup of database created in $BACKUPDBFILE"
}
clean_vte_backup () {
debug "Removing backup of VTE files..."
if [ -r "$BACKUPFILE" ]; then
rm -f "$BACKUPFILE"
fi
}
clean_db_backup () {
debug "Removing backup of database..."
if [ -r "$BACKUPDBFILE" ]; then
rm -f "$BACKUPDBFILE"
fi
}
# WARNING!! Use this function only for testing
clean_all_backups () {
debug "Removing all existing backups..."
rm -rf "$BACKUPDIR"*
}
set_file_ownership () {
# $1 = dir or file
debug "Setting ownership to $HTTPD_USER:$HTTPD_GROUP for $1..."
chown -fR "$HTTPD_USER":"$HTTPD_GROUP" "$1" || warning "Unable to set ownership of $1"
}
fix_permissions () {
# $1 = dir or file
set_file_ownership "$1"
}
# detects if the file lines end with CRLF or LF
# return 1 if it's CRLF
is_dos_eol () {
# $1 = filename
_RET=0
[[ $(head -1 "$1") == *$'\r' ]] && _RET=1
}
is_vte_disabled () {
_RET=0
# check new method
if [ -f "$VTEDIR"maintenance.php ]; then
grep -q -E '^\$vte_maintenance\s*=\s*(true|1);' "$VTEDIR"maintenance.php && _RET=1
else
grep -q -F "VTEUPDATE_DISABLED" "$VTEDIR"config.inc.php && _RET=1
fi
}
disable_vte () {
debug "Disabling VTE..."
# check if already disabled
is_vte_disabled
if [ $_RET -eq 1 ]; then
debug "VTE is already disabled."
return
fi
# new method
if [ -w "$VTEDIR"maintenance.php ]; then
sed -i 's/^\$vte_maintenance.*$/$vte_maintenance = true;/i' "$VTEDIR"maintenance.php
else
# preserve EOL style
local EOL='\n'
is_dos_eol "$VTEDIR"config.inc.php
if [ $_RET -eq 1 ]; then
EOL='\r\n'
fi
sed -i 's/^<?php/\0'$EOL'\/\* VTEUPDATE_DISABLED \*\/ global $vte_self_update; if (PHP_SAPI != "cli" || !$vte_self_update) die("VTE is in maintenance mode");/i' "$VTEDIR"config.inc.php
fi
}
enable_vte () {
debug "Enabling VTE..."
# new method
if [ -w "$VTEDIR"maintenance.php ]; then
sed -i 's/^\$vte_maintenance.*$/$vte_maintenance = false;/i' "$VTEDIR"maintenance.php
fi
# and just to be sure, enable it in the old way
sed -i '/VTEUPDATE_DISABLED/d' "$VTEDIR"config.inc.php
}
enable_cron () {
if [ ! -w "/etc/crontab" ]; then
warning "Crontab file is not writable. Please enable cron processes manually"
fi
sed -i "s/### VTEUPDATE ### //g" /etc/crontab
debug "Cron processes enabled"
}
disable_cron () {
if [ ! -w "/etc/crontab" ]; then
warning "Crontab file is not writable. Please make sure to stop all the active cron processes manually"
if [ $BATCH -eq 1 ]; then
return
fi
ask "Continue with the update?" "Y/N"
if [ "$_RET" = "n" ]; then
info "Update cancelled by user"
exit $RETCODE_OK
else
return
fi
fi
# get vte full path and escape it for regexp
local fullvtepath=$(readlink -n -f "$VTEDIR" | sed 's#\/#\\/#g')
# suspend the cron
sed -i "s/^[^#].*${fullvtepath}.*\(cron\|import\|erpconnector\).*/### VTEUPDATE ### \0/ig" /etc/crontab
debug "Cron processes disabled"
if [ $SKIPCRON -eq 1 ]; then
return
fi
# wait for end
local counter=120
local cronline=""
info "Waiting 2 minutes for cron processes to finish..."
while [ $counter -gt 0 ]; do
cronline=$(ps aux | grep "$fullvtepath" | grep -v -F grep)
if [ -z "$cronline" ]; then
break;
fi
echo -n "."
counter=$(($counter - 1))
sleep 1
done
echo ""
if [ -n "$cronline" ]; then
warning "Some cron processes are taking a long time to terminate. You should terminate them manually"
if [ $BATCH -eq 0 ]; then
ask "Continue with the update?" "Y/N"
if [ "$_RET" = "n" ]; then
info "Update cancelled by user"
exit $RETCODE_OK
fi
fi
fi
}
create_selfupdate () {
# create the file with the old version
echo '<?php $vteversion_preupdate = "'"$VTEREVISION"'"; ' > "vteversion_old.php"
# create the update file
cat << 'ENDPHP' > "$SELFUPDATE"
<?php
// check if calling from command line
if (PHP_SAPI != 'cli' || empty($_SERVER['argv']) || $_SERVER['argc'] < 3) die('ERROR: Not Permitted');
$vte_self_update = true;
require('config.inc.php');
// enable die on error
$dbconfig['db_dieOnError'] = true;
require('vteversion_old.php');
require_once('include/utils/utils.php');
require_once('modules/Update/Update.php');
$pstart = intval($_SERVER['argv'][1]);
$pend = intval($_SERVER['argv'][2]);
if ($pstart <= 0 || $pend <= 0 || $pstart >= $pend || $vteversion_preupdate != $pstart || $enterprise_current_build != $pend) {
echo "ERROR: Wrong version ($pstart -> $pend, VTE: $vteversion_preupdate)\n";
exit(1);
}
global $currentModule, $current_user, $current_language;
global $log, $adb, $table_prefix;
if (empty($table_prefix)) $table_prefix = 'vtiger';
$current_language = 'it_it';
$currentModule = 'Update';
$current_user = CRMEntity::getInstance('Users');
$current_user->id = 1;
$_REQUEST['module'] = 'Update';
if (property_exists('Update', 'logPrefix')) {
Update::$logPrefix = 'VTEUPDATER##';
}
$focus = new $currentModule('server','srv_user','srv_pwd',$pstart);
$focus->to_version = $pend;
// raise PHP limits
ini_set('memory_limit','1024M');
set_time_limit(0);
// launch update
$r = $focus->update_changes();
if ($r === false) {
echo "ERROR: update failed\n";
exit(1);
}
// remove open sessions
if (Vtiger_Utils::CheckTable("{$table_prefix}_userauthtoken")) {
$adb->query("truncate table {$table_prefix}_userauthtoken");
}
if (Vtiger_Utils::CheckTable("{$table_prefix}_ws_userauthtoken")) {
$adb->query("truncate table {$table_prefix}_ws_userauthtoken");
}
// remove cache files
exec("rm -f smartoptimizer/cache/*gz", $output, $ret);
exec("rm -f Smarty/templates_c/*php", $output, $ret);
echo "\nUPDATE FINISHED\n";
ENDPHP
}
# apply a fix for the update class wich lower the max execution time if it was already 0
fix_update_class () {
debug "Fixing Update.php file..."
local SRCFILE="$VTEDIR"modules/Update/Update.php
local DESTFILE="$TEMPDIR"Update.php.bak
# clean and make a backup copy
rm -f "$DESTFILE" 2>/dev/null
cp -f "$SRCFILE" "$DESTFILE"
if [ ! -r "$DESTFILE" ]; then
warning "Unable to read Update.php file, the update can continue anyway"
return
fi
# patch the file
sed -i 's/^\s*set_time_limit([0-9]\+);\s*$/$met = ini_get("max_execution_time"); if ($met > 0 \&\& $met < 600) set_time_limit(600);/g' "$SRCFILE"
# check if there was any change
_RET=1
cmp -s "$SRCFILE" "$DESTFILE" || _RET=0
if [ $_RET -eq 1 ]; then
# equals, remove the bak
debug "Update.php is already patched"
rm -f "$DESTFILE" 2>/dev/null
fi
}
# revert the previous fix
clean_fix_update_class () {
debug "Restoring Update.php file..."
local SRCFILE="$VTEDIR"modules/Update/Update.php
local DESTFILE="$TEMPDIR"Update.php.bak
if [ -r "$DESTFILE" ]; then
cp -f "$DESTFILE" "$SRCFILE" && rm -f "$DESTFILE" 2>/dev/null
fi
}
remove_files () {
if [ $SKIPFILEREMOVE -eq 1 -o ! -s "$DELFILE" ]; then
debug "Skipped files removal phase"
fi
info "Removing obsolete files..."
local now=$(date +"%Y%m%d-%H%M%S")
local BAKNAME="$now""_removed.tgz"
BACKUPDELFILE="$BACKUPDIR""$BAKNAME"
local REMFILE
local HASH
local VTEHASH
local NDELFILES=0
local NDELDIRS=0
if [ -s "$DELFILE_DIR""files.txt" ]; then
debug "Creating backup of removed files..."
cut -f 1 -d $'\t' "$DELFILE_DIR""files.txt" | tar --ignore-failed-read --exclude "vte_updater" --exclude "storage" --exclude "vteUpdater.sh" -czf "$BACKUPDELFILE" -T- 2>/dev/null
while read -r row; do
REMFILE="$VTEDIR"${row%%$'\t'*}
HASH=${row##*$'\t'}
if [ -f "$REMFILE" ]; then
VTEHASH=$(sha1sum "$REMFILE" | cut -f 1 -d " ");
if [ -n "$VTEHASH" -a "$VTEHASH" = "$HASH" ]; then
rm -f "$REMFILE" && {
debug "File $REMFILE removed"
NDELFILES=$((NDELFILES+1))
}
else
warning "The file $REMFILE might have some custom code, left untouched"
fi
fi
done < "$DELFILE_DIR""files.txt"
fi
if [ -s "$DELFILE_DIR""directories.txt" ]; then
while read -r row; do
if [ -d "$row" ]; then
if rmdir "$row" 2>/dev/null; then
debug "Directory $row removed"
NDELDIRS=$((NDELDIRS+1))
else
warning "Directory $row is not empty, left untouched"
fi
fi
done < "$DELFILE_DIR""directories.txt"
fi
info "Removed $NDELFILES files and $NDELDIRS directories"
}
do_update () {
info "Starting update..."
if [ ! -d "$PACKAGE_DIR" ]; then
error "$PACKAGE_DIR directory not found"
restore_cron_and_vte
exit $RETCODE_FAIL
fi
create_selfupdate
if [ ! -s "$SELFUPDATE" ]; then
error "Update script $SELFUPDATE was not created succesfully"
restore_cron_and_vte
exit $RETCODE_FAIL
fi
# check versions
if [ $VTEREVISION -ge $DESTREVISION ]; then
error "The destination revision is lower or equal than the installed one"
restore_cron_and_vte
exit $RETCODE_FAIL
fi
# after this point, vte will be corrupted if interrupted
# copy files
cp -rf "$PACKAGE_DIR"* "$VTEDIR"
if [ -n "$PREPATCHDIR" ]; then
cp -rf "$PREPATCHDIR"/* "$VTEDIR" && {
info "Files from $PREPATCHDIR copied."
} || {
warning "Error while copying files from $PREPATCHDIR. Continuing with the update..."
}
fi
# fis permissions
fix_permissions "$VTEDIR"
fix_update_class
info "Files copied. Executing schema update..."
local now=$(date +"%Y%m%d-%H%M%S")
local BAKNAME="$now""_vteupdate.log"
local NOTESNAME="$now""_update_notes.log"
UPDATELOG="$WORKDIR""$BAKNAME"
NOTESLOG="$WORKDIR""$NOTESNAME"
# execute update
php -f "$VTEDIR""$SELFUPDATE" "$VTEREVISION" "$DESTREVISION" > "$UPDATELOG" 2>&1
_RET=$?
debug "PHP Update script terminated with status $_RET"
if [ $_RET -gt 0 ]; then
clean_fix_update_class
error "There were errors during the update, please check log file $UPDATELOG"
if [ $BATCH -eq 1 ]; then
cat "$UPDATELOG"
else
ask "Do you want do display the log here?" "Y/N"
if [ "$_RET" = "y" ]; then
cat "$UPDATELOG"
fi
fi
do_rollback
return
fi
# check logfile
grep -q -E -i "warning|error|exception|fatal" "$UPDATELOG" && {
clean_fix_update_class
error "There were errors during the update, please check log file $UPDATELOG"
if [ $BATCH -eq 1 ]; then
cat "$UPDATELOG"
else
ask "Do you want do display the log here?" "Y/N"
if [ "$_RET" = "y" ]; then
cat "$UPDATELOG"
fi
fi
do_rollback
return
}
# check for the signature of update end
grep -q "^UPDATE FINISHED\$" "$UPDATELOG" || {
clean_fix_update_class
error "There were errors during the update, please check log file $UPDATELOG"
if [ $BATCH -eq 1 ]; then
cat "$UPDATELOG"
else
ask "Do you want do display the log here?" "Y/N"
if [ "$_RET" = "y" ]; then
cat "$UPDATELOG"
fi
fi
do_rollback
return
}
clean_fix_update_class
remove_files
# check for update notes
grep -F "VTEUPDATER##" "$UPDATELOG" > "$NOTESLOG"
if [ -s "$NOTESLOG" ]; then
# clean it up!
sed -i "s/^.*VTEUPDATER##//g" "$NOTESLOG"
info "Update notes:";
cat "$NOTESLOG"
fi
if [ -n "$POSTPATCHDIR" ]; then
cp -rf "$POSTPATCHDIR"/* "$VTEDIR" && {
info "Files from $POSTPATCHDIR copied."
} || {
warning "Error while copying files from $POSTPATCHDIR. Please copy them manually."
}
fi
# change permission again
fix_permissions "$VTEDIR"
info "Update successful!"
}
do_rollback () {
if [ $BATCH -eq 0 ]; then
ask "Do you want to restore the VTE to its original status?" "Y/N"
if [ "$_RET" = 'n' ]; then
warning "Rollback skipped by user"
# ask to apply post patches
if [ -n "$POSTPATCHDIR" ]; then
ask "Do you want to apply post-patches anyway?" "Y/N"
if [ "$_RET" = 'y' ]; then
cp -rf "$POSTPATCHDIR"/* "$VTEDIR" && {
info "Files from $POSTPATCHDIR copied."
} || {
warning "Error while copying files from $POSTPATCHDIR. Please copy them manually."
}
fix_permissions "$VTEDIR"
fi
fi
return
fi
fi
check_command mysql
if [ ! -r "$BACKUPFILE" ]; then
error "Backup file $BACKUPFILE not found. This is very bad!"
warning "VTE left in inconsistent state"
exit $RETCODE_FAIL
fi
info "Starting rollback..."
# restore files
if [ $COMP_TAR_VCS -eq 1 ]; then
tar --exclude-vcs --exclude "vte_updater/*" --exclude "storage/*" --exclude "vteUpdater.sh" -xzf "$BACKUPFILE" -C "$VTEDIR"
else
tar --exclude ".git" --exclude ".svn" --exclude "vte_updater/*" --exclude "storage/*" --exclude "vteUpdater.sh" -xzf "$BACKUPFILE" -C "$VTEDIR"
fi
if [ $? -gt 0 ]; then
error "Error extracting $1"
warning "VTE left in inconsistent state"
exit $RETCODE_FAIL
fi
# check if double version files, keep the old one
if [ -f "$VTEDIR""vtigerversion.php" -a -f "$VTEDIR""vteversion.php" ]; then
rm "$VTEDIR""vteversion.php"
fi
debug "Backup of files restored"
# restore db
if [ ! -r "$BACKUPDBFILE" ]; then
warning "Backup database file $BACKUPDBFILE not found. If you made a manual backup, please restore it now."
warning "VTE left in inconsistent state"
exit $RETCODE_FAIL
fi
# check if I can drop and then re-create, otherwise new tables might cause problems
local CANDROP=0
local TESTDBNAME="vteupd_test_"$(date +"%Y%m%d")
# get old db charset
local DBCHARSET=$(mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -s -N -e "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = '$DBNAME'")
if [ -z "$DBCHARSET" ]; then
DBCHARSET="utf8"
fi
# silently remove test db
mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -e "DROP DATABASE IF EXISTS $TESTDBNAME;"
# try to create
mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -e "CREATE DATABASE $TESTDBNAME CHARACTER SET $DBCHARSET;" && \
mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -e "DROP DATABASE $TESTDBNAME;" && \
CANDROP=1
# do the real drop+create
if [ $CANDROP -eq 1 ]; then
mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -e "DROP DATABASE $DBNAME"
if [ $? -gt 0 ]; then
warning "Unable to drop database, the rollback will continue, but there might be problems with the next update."
else
mysql -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" -e "CREATE DATABASE $DBNAME CHARACTER SET $DBCHARSET;"
if [ $? -gt 0 ]; then
error "Unable to create the database, please restore it manually."
exit $RETCODE_FAIL
fi
debug "Database recreated correctly"
fi
else
warning "The current mysql user cannot recreate the database. The rollback will continue, but there might be problems with the next update."
fi
# ok, restore it!
zcat "$BACKUPDBFILE" | mysql --default-character-set=utf8 -h "$DBHOST" -P "$DBPORT" -u "$DBUSER" --password="$DBPWD" "$DBNAME"
if [ $? -gt 0 ]; then
error "Error restoring database"
warning "VTE left in inconsistent state"
exit $RETCODE_FAIL
fi
debug "Backup of database restored"
info "Rollback completed"
ROLLBACKOK=1
}
vtews_call () {
# $1 = wsname
# $2...$n = parameters in form $2 = name, $3 = value, $3 = name2, $4 = value ....
# return 0 on error, the json encoded result otherwise
# prepare data
local WSNAME="$1"
local data=""
shift
while [ $# -gt 0 ]; do
local PNAME="$1"
shift
local PVAL="$1"
shift
[ -n "$data" ] && data="$data""&"
data="$data""${PNAME}="$(php -r 'echo urlencode($argv[1]);' "$PVAL")
done
debug "Calling webservice on ${CRMVILLAGE_VTE}: $WSNAME"
local OUTPUT=$(wget --quiet --timeout 20 "${CRMVILLAGE_VTE}webservice.php?operation=$WSNAME" --post-data="$data" -O - )
local success=$(php -r 'if ($d = json_decode($argv[1], true)) { echo ($d["success"] ? 1 : 0); } else echo 0; ' "$OUTPUT")
if [ $success -eq 1 ]; then
local result=$(php -r 'if ($d = json_decode($argv[1], true)) echo json_encode($d["result"]);' "$OUTPUT")
_RET="$result"
else
local message=$(php -r 'if ($d = json_decode($argv[1], true)) { echo $d["error"]["message"]; } else echo "Invalid response"; ' "$OUTPUT")
warning "Error during request: $message"
_RET=0
fi
}
update_ws_call () {
# $1 = wsname
# $2...$n = parameters in form $2 = name, $3 = value, $3 = name2, $4 = value ....
# return
# prepare data
local WSNAME="$1"
local data=""
shift
while [ $# -gt 0 ]; do
local PNAME="$1"
shift
local PVAL="$1"
shift
[ -n "$data" ] && data="$data""&"
data="$data""${PNAME}="$(php -r 'echo urlencode($argv[1]);' "$PVAL")
done
debug "Calling webservice on ${UPDATE_SERVER}: $WSNAME"
local OUTPUT=$(wget --quiet --timeout 20 "${UPDATE_SERVER}ws.php?wsname=$WSNAME" --post-data="$data" -O - )
local success=$(php -r 'if ($d = json_decode($argv[1], true)) { echo ($d["success"] ? 1 : 0); } else echo 0; ' "$OUTPUT")
if [ $success -eq 1 ]; then
local result=$(php -r 'if ($d = json_decode($argv[1], true)) echo json_encode($d["result"]);' "$OUTPUT")
_RET="$result"
else
local message=$(php -r 'if ($d = json_decode($argv[1], true)) { echo $d["error"]; } else echo "Invalid response"; ' "$OUTPUT")
warning "Error during request: $message"
debug "Server responded with $OUTPUT"
_RET=0
fi
}
get_user_accesskey () {
if [ -n "$ACCESSKEY" -a -n "$USERNAME" ]; then
debug "Using provided username $USERNAME and accesskey"
HASHEDKEY=$(echo -n "$ACCESSKEY" | md5sum | cut -f 1 -d " ")
return
fi
if [ $BATCH -eq 1 ]; then
error "In batch mode you have to provide username and accesskey from the commandline"
exit $RETCODE_FAIL
fi
local COUNTER=1
while true; do
ask "Please type your username" "" "0"
USERNAME=$_RET
ask_pwd "Password"
local PASSWORD=$_RET
# validate on server
info "Validating credentials..."
vtews_call "login_pwd" "username" "${USERNAME}" "password" "${PASSWORD}"
if [ -z "$_RET" ]; then
error "Error validating credentials"
exit $RETCODE_FAIL
elif [ "$_RET" = "0" ]; then
if [ $COUNTER -ge 3 ]; then
error "Invalid credentials. Too many attempts, exiting."
exit $RETCODE_FAIL
fi
COUNTER=$(($COUNTER+1))
error "Invalid credentials, try again"
continue
fi
ACCESSKEY=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d[0];' "$_RET")
break
done
if [ -z "$ACCESSKEY" ]; then
error "Invalid accesskey"
exit $RETCODE_FAIL
fi
HASHEDKEY=$(echo -n "$ACCESSKEY" | md5sum | cut -f 1 -d " ")
debug "Accesskey succesfully retrieved"
}
get_safe_dest_revision () {
local VTEREV=$1
# read ranges from server
update_ws_call "get_safe_update_ranges"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
fi
REVRANGES=$(php -r '$r = array(); if ($d = json_decode($argv[1], true)) { array_walk_recursive($d, function($a) use (&$r) { $r[] = $a; }); echo implode(" ", $r); }' "$_RET")
read -r -a REVRANGES <<< "$REVRANGES"
local REVCOUNT=${#REVRANGES[@]}
local REVMAXIDX=$(( $REVCOUNT / 3 - 1 ))
_RET=""
for i in $(seq 0 $REVMAXIDX); do
if [ $VTEREV -ge ${REVRANGES[$i*3]} -a $VTEREV -le ${REVRANGES[$i*3+1]} ]; then
_RET=${REVRANGES[$i*3+2]}
break
fi
done
}
is_safe_revision () {
local CHECKREV=$1
local REVCOUNT=${#REVRANGES[@]}
local REVMAXIDX=$(( $REVCOUNT / 3 - 1 ))
_RET=0
for i in $(seq 0 $REVMAXIDX); do
if [ $CHECKREV -eq ${REVRANGES[$i*3+2]} ]; then
_RET=1
break
fi
done
}
get_dest_revision () {
get_safe_dest_revision $VTEREVISION
local SAFEREV=$_RET
# check if specified from command line
if [ -n "$DESTREVISION" -a $DESTREVISION -gt 0 ]; then
if [ $DESTREVISION -le $VTEREVISION ]; then
error "The destination revision is lower or equal than the installad version"
exit $RETCODE_FAIL
fi
if [ -n "$SAFEREV" -a "$SAFEREV" != "$DESTREVISION" ]; then
is_safe_revision $DESTREVISION
if [ "$_RET" = "0" ]; then
if [ $BATCH -eq 1 ]; then
warning "You are updating to an unsupported revision (the safest one would be $SAFEREV). Continuing anyway since we are in batch mode."
_RET='y'
else
ask "You are updating to an unsupported revision (the safest one would be $SAFEREV). Do you want to proceed anyway?" "Y/N"
fi
else
if [ $BATCH -eq 1 ]; then
warning "You are updating to an unstable revision and the update can fail. Continuing anyway since we are in batch mode."
_RET='y'
else
ask "Updating to the chosen revision is risky and the update can fail (the safest one would be $SAFEREV). Do you want to proceed anyway?" "Y/N"
fi
fi
if [ $_RET = 'n' ]; then
info "Update cancelled by user"
exit $RETCODE_OK
fi
fi
# php version check (hardcoded for now, might be dynamic in the future)
if [ $DESTREVISION -ge 1819 ]; then
local PHPVERSION=$(php -r "echo phpversion();")
local MINPHPVERSION="7.0"
version_lt "$PHPVERSION" "$MINPHPVERSION" && {
error "The destination revision supports only PHP >= $MINPHPVERSION. Please update the system before updating."
exit $RETCODE_FAIL
}
fi
return
fi
info "Checking for latest available version..."
update_ws_call "get_latest_revision"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
fi
DESTREVISION=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d;' "$_RET")
debug "Latest version available: $DESTREVISION"
if [ $DESTREVISION -le $VTEREVISION ]; then
info "No new versions available"
exit $RETCODE_OK
else
if [ -z "$SAFEREV" ]; then
if [ $BATCH -eq 1 ]; then
warning "Updating to revision $DESTREVISION, not yet released and not officially supported."
else
ask "You can update to revision $DESTREVISION, which is not yet released and not officially supported. Do you want to proceed?" "Y/N"
fi
else
if [ $SAFEREV -lt $DESTREVISION ]; then
debug "An even newer version is available, but the safest revision ($SAFEREV) is being used"
fi
DESTREVISION=$SAFEREV
if [ $BATCH -eq 0 ]; then
ask "You can update to revision $DESTREVISION. Do you want to proceed?" "Y/N"
fi
fi
if [ $_RET = 'n' ]; then
info "Update cancelled by user"
exit $RETCODE_OK
fi
fi
# php version check (hardcoded for now, might be dynamic in the future)
if [ $DESTREVISION -ge 1819 ]; then
local PHPVERSION=$(php -r "echo phpversion();")
local MINPHPVERSION="7.0"
version_lt "$PHPVERSION" "$MINPHPVERSION" && {
error "The destination revision supports only PHP >= $MINPHPVERSION. Please update the system before updating."
exit $RETCODE_FAIL
}
fi
}
fetch_package () {
# check if we have the packages from command line
if [ -s "$PACKAGEFILE" -a \( -s "$SRCFILE" -o $SKIPFILECHECK -eq 1 \) ]; then
debug "Using update and source file from command line"
return
fi
# first check if package is already here
local UPDFILELOC="$PACKAGESDIR""vte${VTEREVISION}-${DESTREVISION}.tgz"
local SRCFILELOC="$PACKAGESDIR""vte${VTEREVISION}-${DESTREVISION}src.tgz"
local DELFILELOC="$PACKAGESDIR""vte${VTEREVISION}-${DESTREVISION}del.tgz"
if [ -s "$UPDFILELOC" -a -s "$SRCFILELOC" ]; then
PACKAGEFILE="$UPDFILELOC"
SRCFILE="$SRCFILELOC"
info "Found needed packages in local cache, using them."
debug "Using packages $PACKAGEFILE and $SRCFILE"
if [ -s "$DELFILELOC" ]; then
DELFILE="$DELFILELOC"
debug "Using also deleted files list $DELFILE"
fi
return
fi
if [ $BATCH -eq 1 ]; then
error "In batch mode you should manually provide the update packages or be sure they are in the local cache"
exit $RETCODE_FAIL
fi
# check if the package is ready
update_ws_call "is_package_available" "start_revision" "$VTEREVISION" "dest_revision" "$DESTREVISION"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
fi
local AVAIL=$(php -r 'if ($d = json_decode($argv[1], true)) echo ($d ? 1 : 0); else echo 0;' "$_RET")
if [ $AVAIL -eq 0 ]; then
# no package available, request it and exit
# for community, no requests are possible
if [ $ISCOMMUNITY -eq 1 ]; then
info "The update package is not available, try again later"
exit $RETCODE_OK
fi
info "The update package is not available at the moment, but you can submit a request and you'll be informed as soon as it will be ready"
while true; do
ask "To continue, type the email address you want to be notified"
local EMAIL="$_RET"
if [[ "$EMAIL" =~ [a-zA-Z0-9_.+%-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,5} ]]; then
break
else
error "The address provided is invalid"
fi
done
get_user_accesskey
info "Sending request..."
update_ws_call "request_package" \
"username" "$USERNAME" "hashedkey" "$HASHEDKEY" \
"start_revision" "$VTEREVISION" "dest_revision" "$DESTREVISION" \
"email" "$EMAIL" "vte_url" "$VTEURL"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
else
info "Thank you! The update package will be generated shortly. You'll receive an email when it will be ready"
fi
exit $RETCODE_OK
else
# download package
if [ $ISCOMMUNITY -eq 1 ]; then
info "Downloading package..."
update_ws_call "request_download" \
"start_revision" "$VTEREVISION" "dest_revision" "$DESTREVISION" \
"vte_url" "$VTEURL"
else
get_user_accesskey
info "Downloading package..."
update_ws_call "request_download" \
"username" "$USERNAME" "hashedkey" "$HASHEDKEY" \
"start_revision" "$VTEREVISION" "dest_revision" "$DESTREVISION" \
"vte_url" "$VTEURL"
fi
if [ -z "$_RET" -o "$_RET" = "0" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
fi
local REMOTESRC=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d["src"];' "$_RET")
local REMOTEUPD=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d["upd"];' "$_RET")
local REMOTEDEL=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d["del"];' "$_RET")
debug "Remote packages are $REMOTEUPD and $REMOTESRC"
if [ -n "$REMOTEDEL" ]; then
debug "Remote deleted files list is $REMOTEDEL"
fi
if [ -z "$REMOTESRC" -o -z "$REMOTEUPD" ]; then
error "Unable to communicate with the update server"
exit $RETCODE_FAIL
fi
#local OUTFILESRC="$PACKAGESDIR"$(basename "${REMOTESRC/_[a-zA-Z0-9]*./.}" )
#local OUTFILEUPD="$PACKAGESDIR"$(basename "${REMOTEUPD/_[a-zA-Z0-9]*./.}" )
# TODO use curl if wget is not available
debug "Packages download started..."
if [[ "$REMOTESRC" =~ ^http ]]; then
# absolute url
wget -nv --progress=bar:force "$REMOTESRC" -O "$SRCFILELOC"
else
wget -nv --progress=bar:force "$UPDATE_SERVER""$REMOTESRC" -O "$SRCFILELOC"
fi
if [ $? -gt 0 ]; then
rm -f "$SRCFILELOC" 2>/dev/null
error "Error during download"
exit $RETCODE_FAIL
fi
if [[ "$REMOTEUPD" =~ ^http ]]; then
wget -nv --progress=bar:force "$REMOTEUPD" -O "$UPDFILELOC"
else
wget -nv --progress=bar:force "$UPDATE_SERVER""$REMOTEUPD" -O "$UPDFILELOC"
fi
if [ $? -gt 0 ]; then
rm -f "$UPDFILELOC" "$SRCFILELOC" 2>/dev/null
error "Error during download"
exit $RETCODE_FAIL
fi
if [ -n "$REMOTEDEL" ]; then
if [[ "$REMOTEDEL" =~ ^http ]]; then
wget -nv --progress=bar:force "$REMOTEDEL" -O "$DELFILELOC"
else
wget -nv --progress=bar:force "$UPDATE_SERVER""$REMOTEDEL" -O "$DELFILELOC"
fi
if [ $? -gt 0 ]; then
rm -f "$DELFILELOC" 2>/dev/null
warning "Unable to download deleted files list. This step will be skipped."
fi
fi
if [ -s "$UPDFILELOC" -a -s "$SRCFILELOC" ]; then
PACKAGEFILE="$UPDFILELOC"
SRCFILE="$SRCFILELOC"
if [ -s "$DELFILELOC" ]; then
DELFILE="$DELFILELOC"
fi
info "Packages downloaded succesfully"
else
error "Packages were not downloaded correctly"
exit $RETCODE_FAIL
fi
fi
}
check_only_backup () {
if [ $ONLYBACKUP -eq 1 ]; then
create_vte_backup
create_db_backup
info "Backup completed. Backup files are $BACKUPFILE and $BACKUPDBFILE"
exit $RETCODE_OK
fi
}
check_only_enable () {
if [ $ENABLEVTE -eq 1 ]; then
info "Enabling VTE..."
enable_vte
exit $RETCODE_OK
elif [ $ENABLEVTE -eq 2 ]; then
info "Disabling VTE..."
disable_vte
exit $RETCODE_OK
fi
}
check_only_rollback () {
if [ -n "$ONLYROLLBACK" -a "$ONLYROLLBACK" -gt 0 ]; then
if [ $ONLYROLLBACK -ge $VTEREVISION ]; then
error "You can't rollback to a revision greater than the actual VTE"
exit $RETCODE_FAIL
fi
# look for update files
local SEARCHDIR="$VTEDIR"vte_updater/"$ONLYROLLBACK"/backup/
if [ ! -d "$SEARCHDIR" ]; then
error "No backup found for the specified revision"
exit $RETCODE_FAIL
fi
# look for files
local datepart
local filebak
local dbbak
for f in "$SEARCHDIR"*_files.tgz; do
filebak="$f"
datepart=${f%%-*}
# look for db file
for dbf in "${datepart}-"*_db.sql.gz; do
dbbak="$dbf"
break
done
if [ -s "$filebak" -a -s "$dbbak" ]; then
break
fi
done
if [ ! -s "$filebak" -o ! -s "$dbbak" ]; then
error "No backup found for the specified revision"
exit $RETCODE_FAIL
fi
BACKUPFILE="$filebak"
BACKUPDBFILE="$dbbak"
debug "Using backup files $BACKUPFILE and $BACKUPDBFILE for rollback"
# extract date and time
local bakdate=$(basename "$BACKUPFILE")
bakdate=${bakdate:0:4}"-"${bakdate:4:2}"-"${bakdate:6:2}
local baktime=$(basename "$BACKUPFILE")
baktime=${baktime:9:2}":"${baktime:11:2}
local bakdbtime=$(basename "$BACKUPDBFILE")
bakdbtime=${bakdbtime:9:2}":"${bakdbtime:11:2}
info "Found backup in date $bakdate (Time of backup: Files $baktime, Database $bakdbtime)"
get_db_config
if [ "$DBTYPE" != "mysql" -a "$DBTYPE" != "mysqli" ]; then
error "The database type ($DBTYPE) is not supported for automatic rollback."
exit $RETCODE_FAIL
fi
do_rollback
enable_vte
exit $RETCODE_OK
fi
}
check_only_delete () {
if [ -n "$ONLYDELETE" -a "$ONLYDELETE" -gt 0 ]; then
if [ $BATCH -eq 1 ]; then
error "Option --only-delete cannot be used in batch mode"
exit $RETCODE_FAIL
fi
if [ -z "$DELFILE" ]; then
local STARTREV
while true; do
ask "From which revision was this VTE updated?"
if [ "$_RET" -ge 1000 -a "$_RET" -lt "$VTEREVISION" ]; then
STARTREV="$_RET"
break
else
warning "Invalid revision specified"
fi
done
# switch revisions
local OLDREVISION="$VTEREVISION"
DESTREVISION="$VTEREVISION"
VTEREVISION="$STARTREV"
fetch_package
VTEREVISION="$OLDREVISION"
DESTREVISION=""
fi
if [ ! -f "$DELFILE" ]; then
error "Unable to retrieve a valid deleted files package"
exit $RETCODE_FAIL
fi
unpack_del $DELFILE
remove_files
exit $RETCODE_OK
fi
}
check_use_package () {
if [ -n "$USELOCALPKG" ]; then
if [ ! -r "$USELOCALPKG" -o ! -s "$USELOCALPKG" ]; then
error "The specified update file $USELOCALPKG was not found"
exit $RETCODE_FAIL
fi
if [ $SKIPFILECHECK -eq 0 -a -z "$USELOCALSRCPKG" ]; then
error "You must also provide the source package with the --src-package option"
exit $RETCODE_FAIL
elif [ $SKIPFILECHECK -eq 0 -a ! -s "$USELOCALSRCPKG" ]; then
error "The specified source file $USELOCALSRCPKG was not found"
exit $RETCODE_FAIL
fi
# now check for the internal revision
local TMPFILE="$TEMPDIR"tmp_vteversion.php
tar --wildcards -Oxf "$USELOCALPKG" "*/vt*version.php" > "$TMPFILE"
if [ $? -gt 0 -o ! -s "$TMPFILE" ]; then
error "The provided package is not a valid update package"
rm -f "$TMPFILE"
exit $RETCODE_FAIL
fi
extract_php_global_var "$TMPFILE" "enterprise_current_build"
if [ -z "$_RET" ]; then
error "The provided package is not a valid update package"
rm -f "$TMPFILE"
exit $RETCODE_FAIL
fi
DESTREVISION=$_RET
rm -f "$TMPFILE"
PACKAGEFILE="$USELOCALPKG"
SRCFILE="$USELOCALSRCPKG"
debug "Using provided package and source files $USELOCALPKG, $USELOCALSRCPKG to update VTE to revision $DESTREVISION"
elif [ -n "$USELOCALSRCPKG" ]; then
warning "Option --src-package without --upd-package has no effect"
fi
if [ -n "$USELOCALDELPKG" ]; then
if [ ! -r "$USELOCALDELPKG" -o ! -s "$USELOCALDELPKG" ]; then
error "The specified deleted files list $USELOCALDELPKG was not found"
exit $RETCODE_FAIL
fi
DELFILE="$USELOCALDELPKG"
fi
}
self_update () {
if [ $SKIPUPGRADE -eq 1 ]; then
info "Self update skipped as specified on command line"
return
fi
if [ $FORCEUPGRADE -eq 1 -a $BATCH -eq 1 ]; then
error "You cannot specify --force-upgrade and --batch at the same time"
exit $RETCODE_FAIL
fi
if [ $BATCH -eq 1 ]; then
info "Self update skipped in batch mode"
return
fi
local LASTUPDATEFILE="$WORKDIR""last_update_check"
if [ $FORCEUPGRADE -eq 0 -a -s "$LASTUPDATEFILE" ]; then
# check the content
local NOW=$(date +%Y%m%d)
local LASTCHECK=$(cat "$LASTUPDATEFILE")
if [ -n "$NOW" -a -n "$LASTCHECK" ]; then
# do the comparison
if [ "$NOW" = "$LASTCHECK" ]; then
debug "Last check for updates was today, skipped"
return
fi
fi
fi
# check for new versions
info "Checking for upgrades of this script..."
update_ws_call "get_latest_script_version"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
warning "Unable to communicate with the update server, self update skipped"
return
fi
# save last update time to file
date +%Y%m%d > "$LASTUPDATEFILE"
local NEWVERSION=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d;' "$_RET")
local COMP=$(php -r 'echo version_compare($argv[1], $argv[2]);' "$VERSION" "$NEWVERSION")
if [ "$COMP" = "-1" ]; then
info "Upgrading script..."
update_ws_call "request_script_download"
if [ -z "$_RET" -o "$_RET" = "0" ]; then
warning "Unable to communicate with the update server, self update skipped"
return
fi
local URL=$(php -r 'if ($d = json_decode($argv[1], true)) echo $d["url"];' "$_RET")
debug "Remote script is $URL"
if [ -z "$URL" ]; then
warning "Unable to communicate with the update server, self update skipped"
return
fi
debug "Downloading script..."
local SCRIPTTMP="$WORKDIR""vteUpdater-${NEWVERSION}_temp.tgz"
wget -nv -q "$UPDATE_SERVER""$URL" -O "$SCRIPTTMP"
if [ $? -gt 0 -o ! -s "$SCRIPTTMP" ]; then
rm -f "$SCRIPTTMP" 2>/dev/null
warning "Error during download, self update skipped"
return
fi
debug "Upgrade downloaded successfully"
# now decompress it
local NEWSCRIPT="${WORKDIR}vteUpdater.sh"
rm -f "$NEWSCRIPT" 2>/dev/null
tar -xzf "$SCRIPTTMP" -C "$WORKDIR"
if [ ! -s "$NEWSCRIPT" ]; then
warning "The downloaded upgrade is invalid, skipped"
return
fi
# adjust permissions
local MYSELF=$(basename $0)
local FILEMODE=$(stat -c "%a" $MYSELF)
if ! chmod $FILEMODE "$NEWSCRIPT" ; then
warning "Unable to set correct permission to the new script, self update skipped"
return
fi
# create script to replace myself
cat > updUpdater.sh << UPDEND
#!/bin/bash
# do a backup copy
BAKCOPY="$WORKDIR""vte_updater-$VERSION.sh.bak"
cp -f "vteUpdater.sh" "\$BAKCOPY" 2>/dev/null
# overwrite
if mv -f "$NEWSCRIPT" "vteUpdater.sh"; then
echo
echo "[INFO] The update script has been upgraded to version $NEWVERSION. Please launch it again now."
rm -f -- \$0
else
echo "[WARNING] The self update has failed, please run again the update script"
echo "[WARNING] A backup copy of this script is in \$BAKCOPY"
rm -f -- \$0
fi
UPDEND
# execute it
{
debug "Installing new script version..."
bash ./updUpdater.sh
exit 0
}
else
debug "Server version is $NEWVERSION, no need to update"
fi
}
check_only_self_update () {
if [ $ONLYUPGRADE -eq 1 -a $BATCH -eq 0 ]; then
exit $RETCODE_OK
fi
}
trap_handler () {
echo
if [ "$STATUS" = "UPDATING" ]; then
warning "CTRL+C pressed during update"
if [ $BATCH -eq 0 ]; then
ask "Are you really sure to interrupt the update process? This can leave the VTE in a inconsistent state." "Y/N"
if [ "$_RET" = "n" ]; then
info "Resuming update..."
return
fi
fi
warning "Update interrupted by user"
else
warning "Script interrupted by user. No changes made to VTE"
fi
# delete partial backups
if [ "$STATUS" = "CREATINGBACKUP" ]; then
rm -f "$BACKUPFILE"
rm -f "$BACKUPDBFILE"
# vte & crons can still be stopped, enable them!
restore_cron_and_vte
fi
# exit
exit $RETCODE_BREAK
}
restore_cron_and_vte () {
is_vte_disabled
if [ $_RET -eq 1 ]; then
enable_vte
enable_cron
fi
}
print_usage () {
cat << USAGE
Usage: vteUpdater.sh [OPTIONS...]
Options
-d N, --dest-revision=N Update to revision N (if not specified, query the update
server for the latest available version)
-r N, --rollback=N Try to rollback VTE to revision N (only if a backup was previously created)
--disable-vte Only set the VTE in maintenance mode
--enable-vte Only remove the maintenance mode
--upd-package=PKG Use the file PKG as the update package
--src-package=PKG Use the file PKG as the source package used during the file check phase.
Unless you specify the --skip-file-check option, you have to also provide
this file when using --upd-package.
--del-package=PKG Use PKG for the deleted files list (optional)
--only-backup Only create the backup of database and files (storage folder is excluded)
--only-delete Only delete obsolete files (detect current version, or use --del-package option)
--only-file-check Check only for modified files
--only-upgrade Like --force-upgrade, but exit immediately after
--skip-file-check Skip the check made on files that will be overridden. WARNING: you may loose
any customization in the VTE.
--skip-file-remove Don't remove obsolete files at the end of the update
--skip-db-backup Skip the backup of the database. WARNING: you may not be able
to restore VTE in case of errors. Use at your own risk!
--skip-vte-backup Skip the backup of VTE files. WARNING: you may not be able to restore
VTE in case of errors. Use at your own risk!
--skip-upgrade Do not check for upgrades when starting
--skip-cron Do not wait the end of VTE cronjobs
--force-upgrade Check for upgrades when starting (by default the check is done only once a day)
-t T --tags=T Additional tags to search during the file comparision. Can be specified multiple times.
--pre-patches=DIR Copy the files in DIR in the VTE directory BEFORE executing the schema update
No checks will be done for owerwritten files
--post-patches=DIR Copy the files in DIR in the VTE directory AFTER the schema update
No checks will be done for owerwritten files
--www-user=USR:GRP Set the user and group for the VTE files to USR and GRP
--file-hashes=FILE FILE contains md5 hashes to use to check for files differences.
For these files, only the hash check is performed
-k X, --accesskey=X Use X as the accesskey for the requests (--username must be used also)
-u X, --username=X Use X as the username for the requests (--accesskey must be also specified)
-b --batch Enable batch mode: no user input required
(skip auto upgrade, assume yes on other questions)
-v N, --verbosity=N Set the verbositiy level to N (1=errors only, 4=debug, default=3)
--version Show version number
-h, --help Show this help screen
USAGE
local cmd=$(command -v base64 2>/dev/null)
if [ -z "$cmd" ]; then
echo -e "\n This script does not have Super Shark Powers.\n"
else
echo -e "\n This script has Super Shark Powers.\n"
fi
exit $RETCODE_OK
}
show_shark () {
local cmd=$(command -v base64 2>/dev/null)
if [ -z "$cmd" ]; then
echo -e "\n This script does not have Super Shark Powers.\n"
else
# gzipped and base64 encoded ASCII shark
cat << SHARK | base64 --decode | gunzip
H4sICAhCqFMAA3NxdWFsb19hc2NpaS50eHQApZRNbsQgDIX3nIKdJfzyLlF1OZuq6jV6/92AIcNP
TCp1vErI+55tDAnxzaBF+DcP4w+RYuXbkH8aGF+kAFwXiuxd8OKbB8XxYEJEFw2IZR39/CJU42lA
VUYkNnikdy0ojM6fNaFaaH7GqKGLEpa8+O72wAVZCna03cShoJcc1mHvUjWsiCNnf7FSMEOc02ha
slaHpbo1kc5jyyH5LAG3lIFXMorcVtgWa2/LBsxDXOWrmlv5jshH153kRHEt/y7NGTPFejLtw2V/
JvNOUX+11aAW/iGdK0QCT8TUctjghrDfCs6Z2D0xeV3Je1gukqLe4Z/vz4+vR6gdZa2J8kDDE9hO
EaUfBQAA
SHARK
fi
}
parse_cmdline () {
while getopts ":hbv:u:d:k:r:t:-:" arg; do
case $arg in
# special case for long options
-)
# split the value from the name
local LONGOPT="$OPTARG"
if [[ "$OPTARG" =~ = ]] ; then
LONGOPT=${OPTARG%%=*}
OPTARG=${OPTARG##*=}
else
OPTARG=""
fi
case "$LONGOPT" in
enable-vte)
ENABLEVTE=1
;;
disable-vte)
ENABLEVTE=2
;;
only-backup)
ONLYBACKUP=1
;;
only-delete)
ONLYDELETE=1
;;
upd-package)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
USELOCALPKG="$OPTARG"
;;
src-package)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
USELOCALSRCPKG="$OPTARG"
;;
del-package)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
USELOCALDELPKG="$OPTARG"
;;
rollback)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
ONLYROLLBACK="$OPTARG"
;;
username)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
USERNAME="$OPTARG"
;;
accesskey)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
ACCESSKEY="$OPTARG"
;;
tags)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
EXTRATAGS+=("$OPTARG")
;;
pre-patches)
if [ ! -d "$OPTARG" ]; then
echo "The argument for $LONGOPT is not a valid directory"
print_usage
fi
PREPATCHDIR="$OPTARG"
;;
post-patches)
if [ ! -d "$OPTARG" ]; then
echo "The argument for $LONGOPT is not a valid directory"
print_usage
fi
POSTPATCHDIR="$OPTARG"
;;
file-hashes)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
FILEHASHES="$OPTARG"
;;
www-user)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
WWWUSERS="$OPTARG"
;;
skip-file-check)
SKIPFILECHECK=1
;;
only-file-check)
ONLYFILECHECK=1
;;
skip-file-remove)
SKIPFILEREMOVE=1
;;
skip-db-backup)
SKIPDBACKUP=1
;;
skip-vte-backup)
SKIPVTEBACKUP=1
;;
skip-upgrade)
SKIPUPGRADE=1
;;
force-upgrade)
FORCEUPGRADE=1
;;
only-upgrade)
FORCEUPGRADE=1
ONLYUPGRADE=1
;;
skip-cron)
SKIPCRON=1
;;
dest-revision)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
DESTREVISION=$OPTARG
;;
batch)
BATCH=1
;;
verbosity)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$LONGOPT"
print_usage
fi
DEBUG=$OPTARG
;;
shark)
show_shark
exit $RETCODE_OK
;;
version)
echo "VTE Automatic Updater -- version $VERSION"
exit $RETCODE_OK
;;
help)
print_usage
;;
*)
echo "Invalid argument: --$LONGOPT"
print_usage
;;
esac
;;
# other options
d)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
DESTREVISION=$OPTARG
;;
r)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
ONLYROLLBACK="$OPTARG"
;;
u)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
USERNAME="$OPTARG"
;;
t)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
EXTRATAGS+=("$OPTARG")
;;
k)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
ACCESSKEY="$OPTARG"
;;
b)
BATCH=1
;;
h)
print_usage
;;
v)
if [ -z "$OPTARG" ]; then
echo "Missing argument for option --$arg"
print_usage
fi
DEBUG=$OPTARG
;;
?)
echo "Invalid argument: -$OPTARG"
print_usage
;;
esac
done
}
# ------------------------ START ------------------------
# command line parsing
parse_cmdline $@
# color init and header
init_colors
print_text "${COLOR_BOLD}${COLOR_WHITE}VTE Automatic Updater -- version $VERSION ${COLOR_NORMAL}"
trap trap_handler SIGINT
# INITIAL CHECKS
STATUS="INITIALIZING"
check_command stat
check_command touch
check_command head
check_command tr
check_command cut
check_command date
check_command grep
check_command sed
check_command sort
check_command tar
check_command unzip
check_command zipinfo
check_command gzip
check_command zcat
check_command find
check_command md5sum
check_command sha1sum
check_command php
check_command wget
# check options for various commands
check_commands_comp
# ...
check_vtedir "$VTEDIR"
create_workdir "$WORKDIR"
self_update
check_only_self_update
get_vte_revision "$VTEDIR"
if [ $ISCOMMUNITY -eq 1 ]; then
info "Found VTE Community Edition at revision $VTEREVISION"
else
info "Found VTE at revision $VTEREVISION"
fi
init_subfolder
init_log
write_log "Command invoked with arguments: $@"
check_only_backup
check_only_enable
check_only_rollback
check_use_package
check_only_delete
STATUS="FETCHPACKAGES"
get_dest_revision
fetch_package
info "Updating VTE to revision $DESTREVISION"
get_httpd_user
STATUS="UNPACKING"
unpack_src $SRCFILE
unpack_update $PACKAGEFILE
unpack_del $DELFILE
check_update_files
STATUS="CREATINGBACKUP"
disable_cron
disable_vte
create_vte_backup
create_db_backup
# NO-RETURN POINT
# So far nothing has been changed (apart from disabilng crons and vte)
STATUS="UPDATING"
do_update
enable_vte
enable_cron
STATUS=""
if [ $ROLLBACKOK -eq 1 ]; then
exit $RETCODE_OKROLLBACK
else
exit $RETCODE_OK
fi