From c07e521309561ed56166c84e0702e56831293857 Mon Sep 17 00:00:00 2001
From: "andre.peters" <andre.peters@servercow.de>
Date: Fri, 12 Jan 2018 08:30:54 +0100
Subject: [PATCH] [Web] Fixes number validation in forms, fixes #882

---
 data/web/js/api.js                   |   9 +-
 helper-scripts/backup_and_restore.sh | 224 +++++++++++++++++++++++++++
 helper-scripts/nextcloud.sh          |   8 +-
 3 files changed, 233 insertions(+), 8 deletions(-)
 create mode 100755 helper-scripts/backup_and_restore.sh

diff --git a/data/web/js/api.js b/data/web/js/api.js
index f0a0e0b6..2308a595 100644
--- a/data/web/js/api.js
+++ b/data/web/js/api.js
@@ -91,12 +91,12 @@ $(document).ready(function() {
           }
         }
         if ($(this).attr("max")) {
-          if ($(this).val() > $(this).attr("max")) {
+          if (Number($(this).val()) > Number($(this).attr("max"))) {
             invalid = true;
             $(this).addClass('inputMissingAttr');
           } else {
             if ($(this).attr("min")) {
-              if ($(this).val() < $(this).attr("min")) {
+              if (Number($(this).val()) < Number($(this).attr("min"))) {
                 invalid = true;
                 $(this).addClass('inputMissingAttr');
               } else {
@@ -175,12 +175,13 @@ $(document).ready(function() {
           }
         }
         if ($(this).attr("max")) {
-          if ($(this).val() > $(this).attr("max")) {
+          if (Number($(this).val()) > Number($(this).attr("max"))) {
+            alert($(this).attr("max"))
             invalid = true;
             $(this).addClass('inputMissingAttr');
           } else {
             if ($(this).attr("min")) {
-              if ($(this).val() < $(this).attr("min")) {
+              if (Number($(this).val()) < Number($(this).attr("min"))) {
                 invalid = true;
                 $(this).addClass('inputMissingAttr');
               } else {
diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh
new file mode 100755
index 00000000..83bf3c9f
--- /dev/null
+++ b/helper-scripts/backup_and_restore.sh
@@ -0,0 +1,224 @@
+#!/bin/bash
+
+if [[ ! ${1} =~ (backup|restore) ]]; then
+  echo "First parameter needs to be 'backup' or 'restore'"
+  exit 1
+fi
+
+if [[ ${1} == "backup" && ! ${2} =~ (vmail|redis|rspamd|postfix|mysql|all) ]]; then
+  echo "Second parameter needs to be 'vmail', 'redis', 'rspamd', 'postfix', 'mysql' or 'all'"
+  exit 1
+fi
+
+if [[ -z ${BACKUP_LOCATION} ]]; then
+  while [[ -z ${BACKUP_LOCATION} ]]; do
+    read -ep "Backup location (absolute path, starting with /): " BACKUP_LOCATION
+  done
+fi
+
+if [[ ! ${BACKUP_LOCATION} =~ ^/ ]]; then
+  echo "Backup directory needs to be given as absolute path (starting with /)."
+  exit 1
+fi
+
+if [[ -f ${BACKUP_LOCATION} ]]; then
+  echo "${BACKUP_LOCATION} is a file!"
+  exit 1
+fi
+
+if [[ ! -d ${BACKUP_LOCATION} ]]; then
+  echo "${BACKUP_LOCATION} is not a directory"
+  read -p "Create it now? [y|N] " CREATE_BACKUP_LOCATION
+  if [[ ! ${CREATE_BACKUP_LOCATION,,} =~ ^(yes|y)$ ]]; then
+    exit 1
+  else
+    mkdir ${BACKUP_LOCATION}
+    chmod 755 ${BACKUP_LOCATION}
+  fi
+else
+  if [[ ${1} == "backup" ]] && [[ -z $(echo $(stat -Lc %a ${BACKUP_LOCATION}) | grep -oE '[0-9][0-9][5-7]') ]]; then
+    echo "${BACKUP_LOCATION} is not write-able for others, that's required for a backup."
+    exit 1
+  fi
+fi
+BACKUP_LOCATION=$(echo ${BACKUP_LOCATION} | sed 's#/$##')
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
+echo "Using ${BACKUP_LOCATION} as backup/restore location."
+echo
+source ${SCRIPT_DIR}/../mailcow.conf
+
+function backup() {
+  DATE=$(date +"%Y-%m-%d-%H-%M-%S")
+  mkdir -p "${BACKUP_LOCATION}/mailcow-${DATE}"
+  chmod 755 "${BACKUP_LOCATION}/mailcow-${DATE}"
+  while (( "$#" )); do
+    case "$1" in
+    vmail|all)
+      docker run --rm \
+        -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
+        -v $(docker volume ls -qf name=vmail):/vmail \
+        debian:stretch-slim /bin/tar -cvpzf /backup/backup_vmail.tar.gz /vmail
+      ;;&
+    redis|all)
+      docker exec -it $(docker ps -qf name=redis) redis-cli save
+      docker run --rm \
+        -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
+        -v $(docker volume ls -qf name=redis):/redis \
+        debian:stretch-slim /bin/tar -cvpzf /backup/backup_redis.tar.gz /redis
+      ;;&
+    rspamd|all)
+      docker run --rm \
+        -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
+        -v $(docker volume ls -qf name=rspamd):/rspamd \
+        debian:stretch-slim /bin/tar -cvpzf /backup/backup_rspamd.tar.gz /rspamd
+      ;;&
+    postfix|all)
+      docker run -it --rm \
+        -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
+        -v $(docker volume ls -qf name=postfix):/postfix \
+        debian:stretch-slim /bin/tar -cvpzf /backup/backup_postfix.tar.gz /postfix
+      ;;&
+    mysql|all)
+      SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
+      docker run -it --rm \
+        --network $(docker network ls -qf name=mailcow) \
+        -v $(docker volume ls -qf name=mysql):/var/lib/mysql/ \
+        --entrypoint= \
+        -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup \
+        ${SQLIMAGE} /bin/sh -c "mysqldump -hmysql -uroot -p${DBROOT} --all-databases | gzip > /backup/backup_mysql.gz"
+      ;;
+    esac
+    shift
+  done
+}
+
+function restore() {
+  docker stop $(docker ps -qf name=watchdog-mailcow)
+  RESTORE_LOCATION="${1}"
+  shift
+  while (( "$#" )); do
+    case "$1" in
+    vmail)
+      docker stop $(docker ps -qf name=dovecot-mailcow)
+      docker run -it --rm \
+        -v ${RESTORE_LOCATION}:/backup \
+        -v $(docker volume ls -qf name=vmail):/vmail \
+        debian:stretch-slim /bin/tar -xvzf /backup/backup_vmail.tar.gz
+      docker start $(docker ps -aqf name=dovecot-mailcow)
+      echo
+      echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
+      echo
+      echo "docker exec $(docker ps -qf name=dovecot-mailcow) doveadm force-resync -A '*'"
+      echo
+      read -p "Force a resync now? [y|N] " FORCE_RESYNC
+      if [[ ${FORCE_RESYNC,,} =~ ^(yes|y)$ ]]; then
+        docker exec $(docker ps -qf name=dovecot-mailcow) doveadm force-resync -A '*'
+      else
+        echo "OK, skipped."
+      fi
+      ;;
+    redis)
+      docker stop $(docker ps -qf name=redis-mailcow)
+      docker run -it --rm \
+        -v ${RESTORE_LOCATION}:/backup \
+        -v $(docker volume ls -qf name=redis):/redis \
+        debian:stretch-slim /bin/tar -xvzf /backup/backup_redis.tar.gz
+      docker start $(docker ps -aqf name=redis-mailcow)
+      ;;
+    rspamd)
+      docker stop $(docker ps -qf name=rspamd-mailcow)
+      docker run -it --rm \
+        -v ${RESTORE_LOCATION}:/backup \
+        -v $(docker volume ls -qf name=rspamd):/rspamd \
+        debian:stretch-slim /bin/tar -xvzf /backup/backup_rspamd.tar.gz
+      docker start $(docker ps -aqf name=rspamd-mailcow)
+      ;;
+    postfix)
+      docker stop $(docker ps -qf name=postfix-mailcow)
+      docker run -it --rm \
+        -v ${RESTORE_LOCATION}:/backup \
+        -v $(docker volume ls -qf name=postfix):/postfix \
+        debian:stretch-slim /bin/tar -xvzf /backup/backup_postfix.tar.gz
+      docker start $(docker ps -aqf name=postfix-mailcow)
+      ;;
+    mysql)
+      SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
+      docker stop $(docker ps -qf name=mysql-mailcow)
+      docker run \
+        -it --rm \
+        -v $(docker volume ls -qf name=mysql):/var/lib/mysql/ \
+        --entrypoint= \
+        -u mysql \
+        -v ${RESTORE_LOCATION}:/backup \
+        ${SQLIMAGE} /bin/sh -c "mysqld --skip-grant-tables & \
+        until mysqladmin ping; do sleep 3; done && \
+        echo Restoring... && \
+        gunzip < backup/backup_mysql.gz | mysql -uroot && \
+        mysql -uroot -e SHUTDOWN;"
+      docker start $(docker ps -aqf name=mysql-mailcow)
+      ;;
+    esac
+    shift
+  done
+  docker start $(docker ps -aqf name=watchdog-mailcow)
+}
+
+if [[ ${1} == "backup" ]]; then
+  backup ${@,,}
+elif [[ ${1} == "restore" ]]; then
+  i=1
+  declare -A FOLDER_SELECTION
+  if [[ $(find ${BACKUP_LOCATION}/mailcow-* -maxdepth 1 -type d 2> /dev/null| wc -l) -lt 1 ]]; then
+    echo "Selected backup location has no subfolders"
+    exit 1
+  fi
+  for folder in $(ls -d ${BACKUP_LOCATION}/mailcow-*/); do
+    echo "[ ${i} ] - ${folder}"
+    FOLDER_SELECTION[${i}]="${folder}"
+    ((i++))
+  done
+  echo
+  input_sel=0
+  while [[ ${input_sel} -lt 1 ||  ${input_sel} -gt ${i} ]]; do
+    read -p "Select a restore point: " input_sel
+  done
+  i=1
+  echo
+  declare -A FILE_SELECTION
+  RESTORE_POINT="${FOLDER_SELECTION[${input_sel}]}"
+  if [[ -z $(find "${FOLDER_SELECTION[${input_sel}]}" -maxdepth 1 -type f -regex ".*\(redis\|rspamd\|mysql\|vmail\|postfix\).*") ]]; then
+    echo "No datasets found"
+    exit 1
+  fi
+  for file in $(ls -f "${FOLDER_SELECTION[${input_sel}]}"); do
+    if [[ ${file} =~ vmail ]]; then
+      echo "[ ${i} ] - Mail directory (/var/vmail)"
+      FILE_SELECTION[${i}]="vmail"
+      ((i++))
+    elif [[ ${file} =~ redis ]]; then
+      echo "[ ${i} ] - Redis DB"
+      FILE_SELECTION[${i}]="redis"
+      ((i++))
+    elif [[ ${file} =~ rspamd ]]; then
+      echo "[ ${i} ] - Rspamd data"
+      FILE_SELECTION[${i}]="rspamd"
+      ((i++))
+    elif [[ ${file} =~ postfix ]]; then
+      echo "[ ${i} ] - Postfix data"
+      FILE_SELECTION[${i}]="postfix"
+      ((i++))
+    elif [[ ${file} =~ mysql ]]; then
+      echo "[ ${i} ] - SQL DB"
+      FILE_SELECTION[${i}]="mysql"
+      ((i++))
+    fi
+  done
+  echo
+  input_sel=0
+  while [[ ${input_sel} -lt 1 ||  ${input_sel} -gt ${i} ]]; do
+    read -p "Select a dataset to restore: " input_sel
+  done
+  echo "Restoring ${FILE_SELECTION[${input_sel}]} from ${RESTORE_POINT}..."
+  restore "${RESTORE_POINT}" ${FILE_SELECTION[${input_sel}]}
+fi
diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh
index 781e5133..d466cd5a 100755
--- a/helper-scripts/nextcloud.sh
+++ b/helper-scripts/nextcloud.sh
@@ -16,9 +16,9 @@ done
 
 [[ ${NC_PURGE} == "y" ]] && [[ ${NC_INSTALL} == "y" ]] && { echo "Cannot use -p and -i at the same time"; }
 
-source ./mailcow.conf
+  SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
-if [[ ${NC_PURGE} == "y" ]]; then
+  source ${SCRIPT_DIR}/../mailcow.conf
 
 	docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \
 	  "$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'nc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)"
@@ -31,7 +31,7 @@ if [[ ${NC_PURGE} == "y" ]]; then
 	[[ -f ./data/conf/nginx/site.nextcloud.custom ]] && mv ./data/conf/nginx/site.nextcloud.custom ./data/conf/nginx/site.nextcloud.custom-$(date +%s).bak
 	[[ -f ./data/conf/nginx/nextcloud.conf ]] && mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak
 
-	docker-compose restart nginx-mailcow
+  docker restart $(docker ps -aqf name=nginx-mailcow)
 
 elif [[ ${NC_INSTALL} == "y" ]]; then
 
@@ -101,7 +101,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
 		cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/
 	fi
 
-	docker-compose restart nginx-mailcow
+  docker restart $(docker ps -aqf name=nginx-mailcow)
 
 	echo "Login as admin with password: ${ADMIN_NC_PASS}"