You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
18 KiB
478 lines
18 KiB
#!/bin/bash
|
|
# Copyright Broadcom, Inc. All Rights Reserved.
|
|
# SPDX-License-Identifier: APACHE-2.0
|
|
#
|
|
# Bitnami SuiteCRM library
|
|
|
|
# shellcheck disable=SC1091
|
|
|
|
# Load generic libraries
|
|
. /opt/bitnami/scripts/libphp.sh
|
|
. /opt/bitnami/scripts/libfs.sh
|
|
. /opt/bitnami/scripts/libos.sh
|
|
. /opt/bitnami/scripts/libvalidations.sh
|
|
. /opt/bitnami/scripts/libpersistence.sh
|
|
. /opt/bitnami/scripts/libwebserver.sh
|
|
|
|
# Load database library
|
|
if [[ -f /opt/bitnami/scripts/libmysqlclient.sh ]]; then
|
|
. /opt/bitnami/scripts/libmysqlclient.sh
|
|
elif [[ -f /opt/bitnami/scripts/libmysql.sh ]]; then
|
|
. /opt/bitnami/scripts/libmysql.sh
|
|
elif [[ -f /opt/bitnami/scripts/libmariadb.sh ]]; then
|
|
. /opt/bitnami/scripts/libmariadb.sh
|
|
fi
|
|
|
|
# Rewrite env variables if SuiteCRM 7 is detected
|
|
if [[ ! -d "${SUITECRM_BASE_DIR}/public" ]]; then
|
|
export SUITECRM_CONF_FILE="${SUITECRM_BASE_DIR}/config.php"
|
|
export SUITECRM_SILENT_INSTALL_CONF_FILE="${SUITECRM_BASE_DIR}/config_si.php"
|
|
fi
|
|
|
|
########################
|
|
# Validate settings in SUITECRM_* env vars
|
|
# Globals:
|
|
# SUITECRM_*
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# 0 if the validation succeeded, 1 otherwise
|
|
#########################
|
|
suitecrm_validate() {
|
|
debug "Validating settings in SUITECRM_* environment variables..."
|
|
local error_code=0
|
|
|
|
# Auxiliary functions
|
|
print_validation_error() {
|
|
error "$1"
|
|
error_code=1
|
|
}
|
|
check_yes_no_value() {
|
|
if ! is_yes_no_value "${!1}" && ! is_true_false_value "${!1}"; then
|
|
print_validation_error "The allowed values for ${1} are: yes no"
|
|
fi
|
|
}
|
|
check_multi_value() {
|
|
if [[ " ${2} " != *" ${!1} "* ]]; then
|
|
print_validation_error "The allowed values for ${1} are: ${2}"
|
|
fi
|
|
}
|
|
check_true_false_value() {
|
|
if ! is_true_false_value "${!1}"; then
|
|
print_validation_error "The allowed values for $1 are [true, false]"
|
|
fi
|
|
}
|
|
check_valid_port() {
|
|
local port_var="${1:?missing port variable}"
|
|
local err
|
|
if ! err="$(validate_port "${!port_var}")"; then
|
|
print_validation_error "An invalid port was specified in the environment variable ${port_var}: ${err}."
|
|
fi
|
|
}
|
|
|
|
# Validate user inputs
|
|
! is_empty_value "$SUITECRM_VALIDATE_USER_IP" && check_true_false_value "SUITECRM_VALIDATE_USER_IP"
|
|
|
|
check_yes_no_value "SUITECRM_ENABLE_HTTPS"
|
|
|
|
# Validate credentials
|
|
for empty_env_var in "SUITECRM_DATABASE_PASSWORD" "SUITECRM_PASSWORD"; do
|
|
is_empty_value "${!empty_env_var}" && print_validation_error "The ${empty_env_var} environment variable is empty or not set."
|
|
done
|
|
|
|
# Validate SMTP credentials
|
|
if ! is_empty_value "$SUITECRM_SMTP_HOST"; then
|
|
for empty_env_var in "SUITECRM_SMTP_USER" "SUITECRM_SMTP_PASSWORD"; do
|
|
is_empty_value "${!empty_env_var}" && warn "The ${empty_env_var} environment variable is empty or not set."
|
|
done
|
|
is_empty_value "$SUITECRM_SMTP_PORT_NUMBER" && print_validation_error "The SUITECRM_SMTP_PORT_NUMBER environment variable is empty or not set."
|
|
! is_empty_value "$SUITECRM_SMTP_PORT_NUMBER" && check_valid_port "SUITECRM_SMTP_PORT_NUMBER"
|
|
! is_empty_value "$SUITECRM_SMTP_PROTOCOL" && check_multi_value "SUITECRM_SMTP_PROTOCOL" "ssl tls"
|
|
fi
|
|
|
|
# Check that the web server is properly set up
|
|
web_server_validate || print_validation_error "Web server validation failed"
|
|
|
|
return "$error_code"
|
|
}
|
|
|
|
########################
|
|
# Ensure SuiteCRM is initialized
|
|
# Globals:
|
|
# SUITECRM_*
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# None
|
|
#########################
|
|
suitecrm_initialize() {
|
|
# Check if SuiteCRM has already been initialized and persisted in a previous run
|
|
local db_host db_port db_name db_user db_pass
|
|
local -r app_name="suitecrm"
|
|
if ! is_app_initialized "$app_name"; then
|
|
# Ensure SuiteCRM persisted directories exist (i.e. when a volume has been mounted to /bitnami)
|
|
info "Ensuring SuiteCRM directories exist"
|
|
ensure_dir_exists "$SUITECRM_VOLUME_DIR"
|
|
# Use daemon:daemon ownership for compatibility when running as a non-root user
|
|
am_i_root && configure_permissions_ownership "$SUITECRM_VOLUME_DIR" -d "775" -f "664" -u "$WEB_SERVER_DAEMON_USER" -g "$WEB_SERVER_DAEMON_GROUP"
|
|
info "Trying to connect to the database server"
|
|
db_host="$SUITECRM_DATABASE_HOST"
|
|
db_port="$SUITECRM_DATABASE_PORT_NUMBER"
|
|
db_name="$SUITECRM_DATABASE_NAME"
|
|
db_user="$SUITECRM_DATABASE_USER"
|
|
db_pass="$SUITECRM_DATABASE_PASSWORD"
|
|
suitecrm_wait_for_db_connection "$db_host" "$db_port" "$db_name" "$db_user" "$db_pass"
|
|
|
|
local -r template_dir="${BITNAMI_ROOT_DIR}/scripts/suitecrm/bitnami-templates"
|
|
if ! is_boolean_yes "$SUITECRM_SKIP_BOOTSTRAP"; then
|
|
# If SuiteCRM 7, use legacy install wizard
|
|
if [[ ! -d "${SUITECRM_BASE_DIR}/public" ]]; then
|
|
# Render configuration file for silent install ('config_si.php')
|
|
(
|
|
export url_protocol=http
|
|
is_boolean_yes "$SUITECRM_ENABLE_HTTPS" && url_protocol=https
|
|
render-template "${template_dir}/config_si.php.tpl" > "$SUITECRM_SILENT_INSTALL_CONF_FILE"
|
|
)
|
|
web_server_start
|
|
suitecrm_7_pass_wizard
|
|
# Configure SMTP via application wizard
|
|
if ! is_empty_value "$SUITECRM_SMTP_HOST"; then
|
|
info "Configuring SMTP"
|
|
suitecrm_pass_smtp_wizard
|
|
fi
|
|
web_server_stop
|
|
# Delete configuration file for silent install as it's not needed anymore
|
|
rm "$SUITECRM_SILENT_INSTALL_CONF_FILE"
|
|
else
|
|
suitecrm_pass_wizard
|
|
# Configure SMTP via application wizard
|
|
if ! is_empty_value "$SUITECRM_SMTP_HOST"; then
|
|
web_server_start
|
|
info "Configuring SMTP"
|
|
suitecrm_pass_smtp_wizard
|
|
web_server_stop
|
|
fi
|
|
fi
|
|
|
|
else
|
|
info "An already initialized SuiteCRM database was provided, configuration will be skipped"
|
|
# A very basic 'config.php' will be generated with enough information for the application to be able to connect to the database
|
|
# Afterwards we will make use of the application's 'Rebuild Config File' functionality to create a complete configuration file
|
|
# Then we will be able to use the application's scripts to generate cache files, '.htaccess' and other required files
|
|
info "Generating SuiteCRM configuration file"
|
|
(
|
|
export db_host db_port db_name db_user db_pass
|
|
render-template "${template_dir}/config_db.php.tpl" > "$SUITECRM_CONF_FILE"
|
|
# Ensure the configuration file is writable by the web server
|
|
# Negate the condition to always return true and avoid causing exit code
|
|
! am_i_root || configure_permissions_ownership "$SUITECRM_CONF_FILE" -d "775" -f "664" -u "$WEB_SERVER_DAEMON_USER" -g "root"
|
|
)
|
|
suitecrm_rebuild_files
|
|
fi
|
|
|
|
info "Persisting SuiteCRM installation"
|
|
persist_app "$app_name" "$SUITECRM_DATA_TO_PERSIST"
|
|
else
|
|
info "Restoring persisted SuiteCRM installation"
|
|
restore_persisted_app "$app_name" "$SUITECRM_DATA_TO_PERSIST"
|
|
info "Trying to connect to the database server"
|
|
db_host="$(suitecrm_conf_get "dbconfig" "db_host_name")"
|
|
db_port="$(suitecrm_conf_get "dbconfig" "db_port")"
|
|
db_name="$(suitecrm_conf_get "dbconfig" "db_name")"
|
|
db_user="$(suitecrm_conf_get "dbconfig" "db_user_name")"
|
|
db_pass="$(suitecrm_conf_get "dbconfig" "db_password")"
|
|
suitecrm_wait_for_db_connection "$db_host" "$db_port" "$db_name" "$db_user" "$db_pass"
|
|
fi
|
|
|
|
# Ensure SuiteCRM cron jobs are created when running setup with a root user
|
|
# https://docs.suitecrm.com/blog/scheduler-jobs/
|
|
local -a cron_cmd=(cd "${SUITECRM_BASE_DIR};" "${PHP_BIN_DIR}/php" "-f" "cron.php")
|
|
if am_i_root; then
|
|
generate_cron_conf "suitecrm" "${cron_cmd[*]} > /dev/null 2>&1" --run-as "$WEB_SERVER_DAEMON_USER" --schedule "*/1 * * * *"
|
|
else
|
|
warn "Skipping cron configuration for SuiteCRM because of running as a non-root user"
|
|
fi
|
|
|
|
# Avoid exit code of previous commands to affect the result of this function
|
|
true
|
|
}
|
|
|
|
########################
|
|
# Get an entry from the SuiteCRM configuration file ('config.php')
|
|
# Globals:
|
|
# SUITECRM_*
|
|
# Arguments:
|
|
# $1 - PHP variable name
|
|
# Returns:
|
|
# None
|
|
#########################
|
|
suitecrm_conf_get() {
|
|
local key="${1:?key missing}"
|
|
# Construct a PHP array path for the configuration, so each key is passed as a separate argument
|
|
local path="\$sugar_config"
|
|
for key in "$@"; do
|
|
path="${path}['${key}']"
|
|
done
|
|
debug "Getting ${key} from SuiteCRM configuration"
|
|
php -r "require ('${SUITECRM_CONF_FILE}'); print_r($path);"
|
|
}
|
|
|
|
########################
|
|
# Set an entry into the SuiteCRM configuration file ('config.php')
|
|
# Globals:
|
|
# SUITECRM_*
|
|
# Arguments:
|
|
# $1 - PHP variable name
|
|
# Returns:
|
|
# None
|
|
#########################
|
|
suitecrm_conf_set() {
|
|
local -r key="${1:?key missing}"
|
|
local -r value="${2:?missing value}"
|
|
debug "Setting ${key} to '${value}' in SuiteCRM configuration"
|
|
# Sanitize key (sed does not support fixed string substitutions)
|
|
local sanitized_pattern
|
|
sanitized_pattern="$(sed 's/[]\[^$.*/]/\\&/g' <<< "$key")"
|
|
# Check if the configuration exists in the file
|
|
if grep -q -E "$sanitized_pattern" "$SUITECRM_CONF_FILE"; then
|
|
# It exists, so replace the line
|
|
replace_in_file "$SUITECRM_CONF_FILE" "('${sanitized_pattern}' => ').*(',)" "\1${value}\2"
|
|
else
|
|
# The SuiteCRM configuration file includes all supported keys, but because of its format,
|
|
# we cannot append contents to the end. We can assume thi
|
|
warn "Could not set the SuiteCRM '${key}' configuration. Check that the file has not been modified externally."
|
|
fi
|
|
}
|
|
|
|
########################
|
|
# Wait until the database is accessible with the currently-known credentials
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# $1 - database host
|
|
# $2 - database port
|
|
# $3 - database name
|
|
# $4 - database username
|
|
# $5 - database user password (optional)
|
|
# Returns:
|
|
# true if the database connection succeeded, false otherwise
|
|
#########################
|
|
suitecrm_wait_for_db_connection() {
|
|
local -r db_host="${1:?missing database host}"
|
|
local -r db_port="${2:?missing database port}"
|
|
local -r db_name="${3:?missing database name}"
|
|
local -r db_user="${4:?missing database user}"
|
|
local -r db_pass="${5:-}"
|
|
check_mysql_connection() {
|
|
echo "SELECT 1" | mysql_remote_execute "$db_host" "$db_port" "$db_name" "$db_user" "$db_pass"
|
|
}
|
|
if ! retry_while "check_mysql_connection"; then
|
|
error "Could not connect to the database"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
########################
|
|
# Pass SuiteCRM SMTP wizard
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# true if the wizard succeeded, false otherwise
|
|
#########################
|
|
suitecrm_pass_smtp_wizard() {
|
|
local -r port="${APACHE_HTTP_PORT_NUMBER:-"$APACHE_DEFAULT_HTTP_PORT_NUMBER"}"
|
|
local wizard_url curl_output
|
|
local -a curl_opts curl_data_opts
|
|
local url_protocol=http
|
|
is_boolean_yes "$SUITECRM_ENABLE_HTTPS" && url_protocol=https
|
|
local site_url="${url_protocol}://127.0.0.1:${port}/index.php"
|
|
cookie_file="/tmp/cookie$(generate_random_string -t alphanumeric -c 8)"
|
|
curl_opts=(
|
|
"--location"
|
|
"--silent"
|
|
"--cookie-jar" "$cookie_file"
|
|
"--cookie" "$cookie_file"
|
|
# Used to avoid XSRF warning
|
|
"--referer" "http://localhost"
|
|
)
|
|
# Step 1: Login to SuiteCRM and check that SMTP is not configured yet
|
|
wizard_url="${site_url}?action=Login&module=Users"
|
|
curl_data_opts=(
|
|
"--data-urlencode" "username_password=${SUITECRM_PASSWORD}"
|
|
"--data-urlencode" "user_name=${SUITECRM_USERNAME}"
|
|
"--data-urlencode" "return_action=Login"
|
|
"--data-urlencode" "module=Users"
|
|
"--data-urlencode" "action=Authenticate"
|
|
)
|
|
curl_output="$(curl "${curl_opts[@]}" "${curl_data_opts[@]}" "${wizard_url}")"
|
|
if [[ "$curl_output" != *"an SMTP server must be configured"* ]]; then
|
|
error "An error occurred while trying to login to configure SMTP"
|
|
return 1
|
|
fi
|
|
|
|
# Step 2: Configure SMTP and check the message to warn about SMTP has gone
|
|
wizard_url="${site_url}?module=EmailMan&action=config"
|
|
|
|
# Check if SMTP should go over SSL
|
|
local smtp_protocol=""
|
|
[[ "$SUITECRM_SMTP_PROTOCOL" = "ssl" ]] && smtp_protocol="1"
|
|
[[ "$SUITECRM_SMTP_PROTOCOL" = "tls" ]] && smtp_protocol="2"
|
|
|
|
# Check if SMTP user:pass is configured
|
|
local smtp_auth_req=0
|
|
! is_empty_value "$SUITECRM_SMTP_USER" && smtp_auth_req=1
|
|
|
|
curl_data_opts=(
|
|
"--data-urlencode" "mail_allowusersend=0"
|
|
"--data-urlencode" "mail_sendtype=SMTP"
|
|
"--data-urlencode" "mail_smtpauth_req=${smtp_auth_req}"
|
|
"--data-urlencode" "module=EmailMan"
|
|
"--data-urlencode" "mail_smtppass=${SUITECRM_SMTP_PASSWORD}"
|
|
"--data-urlencode" "mail_smtpport=${SUITECRM_SMTP_PORT_NUMBER}"
|
|
"--data-urlencode" "mail_smtpserver=${SUITECRM_SMTP_HOST}"
|
|
"--data-urlencode" "mail_smtptype=other"
|
|
"--data-urlencode" "mail_smtpuser=${SUITECRM_SMTP_USER}"
|
|
"--data-urlencode" "mail_smtpssl=${smtp_protocol}"
|
|
"--data-urlencode" "notify_fromaddress=${SUITECRM_SMTP_NOTIFY_ADDRESS}"
|
|
"--data-urlencode" "notify_fromname=${SUITECRM_SMTP_NOTIFY_NAME}"
|
|
"--data-urlencode" "action=Save"
|
|
)
|
|
curl_output="$(curl "${curl_opts[@]}" "${curl_data_opts[@]}" "${wizard_url}")"
|
|
if [[ "$curl_output" == *"an SMTP server must be configured"* ]]; then
|
|
error "An error occurred configuring SMTP for SuiteCRM"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
########################
|
|
# Pass SuiteCRM wizard
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# true if the wizard succeeded, false otherwise
|
|
#########################
|
|
suitecrm_pass_wizard() {
|
|
local -a install_args
|
|
local url_protocol=http
|
|
info "Running setup wizard"
|
|
is_boolean_yes "$SUITECRM_ENABLE_HTTPS" && url_protocol=https
|
|
|
|
install_args=(
|
|
"--site_host=${url_protocol}://${SUITECRM_HOST}"
|
|
"--site_username=${SUITECRM_USERNAME}"
|
|
"--site_password=${SUITECRM_PASSWORD}"
|
|
"--db_username=${SUITECRM_DATABASE_USER}"
|
|
"--db_password=${SUITECRM_DATABASE_PASSWORD}"
|
|
"--db_host=${SUITECRM_DATABASE_HOST}"
|
|
"--db_port=${SUITECRM_DATABASE_PORT_NUMBER}"
|
|
"--db_name=${SUITECRM_DATABASE_NAME}"
|
|
"--no-interaction"
|
|
)
|
|
|
|
suitecrm_execute suitecrm:app:install "${install_args[@]}"
|
|
}
|
|
|
|
########################
|
|
# Pass SuiteCRM 7 wizard
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# true if the wizard succeeded, false otherwise
|
|
#########################
|
|
suitecrm_7_pass_wizard() {
|
|
local -r port="${APACHE_HTTP_PORT_NUMBER:-"$APACHE_DEFAULT_HTTP_PORT_NUMBER"}"
|
|
local wizard_url curl_output
|
|
local -a curl_opts curl_data_opts
|
|
local url_protocol=http
|
|
info "Running setup wizard"
|
|
is_boolean_yes "$SUITECRM_ENABLE_HTTPS" && url_protocol=https
|
|
wizard_url="${url_protocol}://127.0.0.1:${port}/install.php?goto=SilentInstall&cli=true"
|
|
curl_opts=("--location" "--silent")
|
|
curl_data_opts=(
|
|
"--data-urlencode" "current_step=8"
|
|
"--data-urlencode" "goto=Next"
|
|
)
|
|
local wizard_exit_code=0
|
|
wizard_error() {
|
|
error "An error occurred while installing SuiteCRM: ${*}"
|
|
wizard_exit_code=1
|
|
}
|
|
if ! debug_execute curl "${curl_opts[@]}" "${wizard_url}"; then
|
|
wizard_error "The wizard could not be accessed"
|
|
fi
|
|
if ! grep -q "Save user settings" "${SUITECRM_BASE_DIR}/install.log"; then
|
|
wizard_error "Installation failed"
|
|
fi
|
|
return "$wizard_exit_code"
|
|
}
|
|
|
|
########################
|
|
# Rebuild SuiteCRM's configuration file
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# true if succeded, false otherwise
|
|
#########################
|
|
suitecrm_rebuild_files() {
|
|
# The below script executes the code from "Repair > Rebuild Config File" to regenerate the configuration file
|
|
# We prefer to run a script rather than via cURL requests because it would require to login, and could cause
|
|
# issues with SUITECRM_SKIP_BOOTSTRAP
|
|
php_execute <<EOF
|
|
chdir('$SUITECRM_BASE_DIR');
|
|
define('sugarEntry', true);
|
|
require_once('include/utils.php');
|
|
|
|
// Based on 'install.php' includes
|
|
require_once('include/SugarLogger/LoggerManager.php');
|
|
require_once('sugar_version.php');
|
|
require_once('suitecrm_version.php');
|
|
require_once('include/TimeDate.php');
|
|
require_once('include/Localization/Localization.php');
|
|
require_once('include/SugarTheme/SugarTheme.php');
|
|
require_once('include/utils/LogicHook.php');
|
|
require_once('data/SugarBean.php');
|
|
// Include files that are loaded by 'entryPoint.php'
|
|
// (Note: We cannot include 'entryPoint.sh' since it is only expected to work via HTTP requests)
|
|
require_once('include/SugarEmailAddress/SugarEmailAddress.php');
|
|
require_once('include/utils/file_utils.php');
|
|
|
|
// Rebuild the configuration file
|
|
// Based on the RebuildConfig action in the admin panel
|
|
\$clean_config = loadCleanConfig();
|
|
rebuildConfigFile(\$clean_config, \$sugar_version);
|
|
|
|
// Rebuild the .htaccess file
|
|
// Based on the UpgradeAccess action in the admin panel
|
|
require_once 'include/upload_file.php';
|
|
UploadStream::register();
|
|
require('modules/Administration/UpgradeAccess.php');
|
|
EOF
|
|
}
|
|
|
|
########################
|
|
# Execute SuiteCRM console command
|
|
# Globals:
|
|
# *
|
|
# Arguments:
|
|
# None
|
|
# Returns:
|
|
# true if the wizard succeeded, false otherwise
|
|
#########################
|
|
suitecrm_execute() {
|
|
local -a cmd=("php" "${SUITECRM_BASE_DIR}/bin/console" "$@")
|
|
# Run as web server user to avoid having to change permissions/ownership afterwards
|
|
am_i_root && cmd=("run_as_user" "$WEB_SERVER_DAEMON_USER" "${cmd[@]}")
|
|
(
|
|
cd "${SUITECRM_BASE_DIR}" || false
|
|
debug_execute "${cmd[@]}"
|
|
)
|
|
}
|