From e24862bdae65f5eb13b918b9ffa82e4d3c835661 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 20 Apr 2017 21:11:49 +0200 Subject: [PATCH 1/9] New reset admin routine --- docs/u_and_e.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/u_and_e.md b/docs/u_and_e.md index 3f52b05e..c0a7a804 100644 --- a/docs/u_and_e.md +++ b/docs/u_and_e.md @@ -75,7 +75,7 @@ Beware that a mailbox user can login to mailcow and override a domain policy fil Make your changes in `data/Dockerfiles/$service` and build the image locally: ``` -docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service +docker build data/Dockerfiles/service -t mailcow/$service ``` Now auto-recreate modified containers: @@ -311,14 +311,11 @@ Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes* ## Reset admin password Reset mailcow admin to `admin:moohoo`: -1\. Drop admin table ``` -source mailcow.conf -docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;" +cd mailcow_path +bash reset_admin.sh ``` -2\. Open mailcow UI to auto-init the db - ## Rspamd ### Learn spam and ham From 01bcc5e6531d683c4e807b6e1ef346914097a525 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 20 Apr 2017 21:11:53 +0200 Subject: [PATCH 2/9] New reset admin routine --- reset_admin.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 reset_admin.sh diff --git a/reset_admin.sh b/reset_admin.sh new file mode 100644 index 00000000..7ce1def8 --- /dev/null +++ b/reset_admin.sh @@ -0,0 +1,36 @@ +#/bin/bash +if [[ ! -f mailcow.conf ]]; then + echo "Cannot find mailcow.conf, make sure this script is run from within the mailcow folder." + exit 1 +fi + +echo -n "Checking MySQL service... " +docker-compose ps -q mysql-mailcow > /dev/null 2>&1 + +if [[ $? -ne 0 ]]; then + echo "failed" + echo "MySQL (mysql-mailcow) is not up and running, exiting..." + exit 1 +fi + +echo "OK" +read -r -p "Are you sure you want to reset the mailcow administrator account? [y/N] " response +response=${response,,} # tolower +if [[ "$response" =~ ^(yes|y)$ ]]; then + echo -e "\nWorking, please wait..." + source mailcow.conf + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin;" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO admin (username, password, superadmin, created, modified, active) VALUES ('admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1);" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "INSERT INTO domain_admins (username, domain, created, active) VALUES ('admin', 'ALL', NOW(), 1);" + docker-compose exec -T mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM tfa WHERE username='admin';" + echo " +Reset credentials: +--- +Username: admin +Password: moohoo +TFA: none +" +else + echo "Operation canceled." +fi From 7676fe2dd2e86767ff847406cdc26a4426192937 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 20 Apr 2017 21:12:59 +0200 Subject: [PATCH 3/9] New index, Fail2ban setup --- docs/first_steps.md | 78 +++++++++++++++++++++++++++++++++++++++++++++ docs/index.md | 5 ++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/first_steps.md b/docs/first_steps.md index ab7876dc..22e4f192 100644 --- a/docs/first_steps.md +++ b/docs/first_steps.md @@ -158,6 +158,84 @@ docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd docker-compose exec postfix-mailcow postfix reload ``` +## Optional: Log to Syslog + +Enable Rsyslog to receive logs on 524/tcp: + +``` +# This setting depends on your Rsyslog version and configuration format. +# For most Debian derivates it will work like this... +$ModLoad imtcp +$TCPServerAddress 127.0.0.1 +$InputTCPServerRun 524 + +# ...while for Ubuntu 16.04 it looks like this: +module(load="imtcp") +input(type="imtcp" address="127.0.0.1" port="524") + +# No matter your Rsyslog version, you should set this option to off +# if you plan to use Fail2ban +$RepeatedMsgReduction off +``` + +Restart rsyslog after enabling the TCP listener. + +Now setup Docker daemon to start with the syslog driver. +This enables the syslog driver for all containers! + +Debian users can change the startup configuration in `/etc/default/docker` while CentOS users find it in `/etc/sysconfig/docker`: +``` +... +DOCKER_OPTS="--log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524" +... +``` + +**Caution:** For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters. + +Just run `systemctl edit docker.service` and add the following content to fix it. + +**Note:** If "systemctl edit" is not available, just copy the content to `/etc/systemd/system/docker.service.d/override.conf`. + +The first empty ExecStart parameter is not a mistake. + +``` +[Service] +EnvironmentFile=/etc/default/docker +ExecStart= +ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS +``` + +Restart the Docker daemon and run `docker-compose down && docker-compose up -d` to recreate the containers. + +### Use Fail2ban + +**This is a subsection of "Log to Syslog", which is required for Fail2ban to work.** + +Open `/etc/fail2ban/filter.d/common.conf` and search for the prefix_line parameter, change it to ".*": + +``` +__prefix_line = .* +``` + +Create `/etc/fail2ban/jail.d/dovecot.conf`... +``` +[dovecot] +enabled = true +filter = dovecot +logpath = /var/log/syslog +chain = FORWARD +``` + +and `jail.d/postfix-sasl.conf`: +``` +[postfix-sasl] +enabled = true +filter = postfix-sasl +logpath = /var/log/syslog +chain = FORWARD +``` + +Restart Fail2ban. ## Install a local MTA diff --git a/docs/index.md b/docs/index.md index f4aea569..4a5943f0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,8 @@ # mailcow: dockerized - 🐮 + 🐋 = 💕 -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68) +[![Servercow](https://www.servercow.de/img/cow_globe_200.svg)](https://www.servercow.de) + +If you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow! ## Screenshots @@ -30,6 +32,7 @@ mailcow dockerized comes with **11 containers** linked in **one bridged network* - mysql-vol-1 - rspamd-vol-1 - postfix-vol-1 +- crypt-vol-1 The integrated **mailcow UI** allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: From cae7ff7816e6a79300665e3117b7594d89d4dffc Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 20 Apr 2017 21:13:41 +0200 Subject: [PATCH 4/9] Initial clamd container --- data/Dockerfiles/clamav/Dockerfile | 16 ++++++-------- data/Dockerfiles/clamav/bootstrap.sh | 32 ++-------------------------- data/Dockerfiles/clamav/dl_files.sh | 32 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 40 deletions(-) create mode 100755 data/Dockerfiles/clamav/dl_files.sh diff --git a/data/Dockerfiles/clamav/Dockerfile b/data/Dockerfiles/clamav/Dockerfile index 5fc44d9a..170b7d8f 100755 --- a/data/Dockerfiles/clamav/Dockerfile +++ b/data/Dockerfiles/clamav/Dockerfile @@ -1,8 +1,8 @@ -FROM debian:latest +FROM debian:stretch-slim MAINTAINER https://m-ko.de Markus Kosmal # Debian Base to use -ENV DEBIAN_VERSION jessie +ENV DEBIAN_VERSION stretch # initial install of av daemon RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \ @@ -13,15 +13,14 @@ RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-fr clamav-daemon \ clamav-freshclam \ libclamunrar7 \ - wget && \ + curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* # initial update of av databases -RUN wget -O /var/lib/clamav/main.cvd http://db.local.clamav.net/main.cvd && \ - wget -O /var/lib/clamav/daily.cvd http://db.local.clamav.net/daily.cvd && \ - wget -O /var/lib/clamav/bytecode.cvd http://db.local.clamav.net/bytecode.cvd && \ - chown clamav:clamav /var/lib/clamav/*.cvd +COPY dl_files.sh /dl_files.sh +RUN chmod +x /dl_files.sh +RUN /dl_files.sh # permission juggling RUN mkdir /var/run/clamav && \ @@ -33,9 +32,6 @@ RUN sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/clamd.conf && \ echo "TCPSocket 3310" >> /etc/clamav/clamd.conf && \ sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/freshclam.conf -# volume provision -VOLUME ["/var/lib/clamav"] - # port provision EXPOSE 3310 diff --git a/data/Dockerfiles/clamav/bootstrap.sh b/data/Dockerfiles/clamav/bootstrap.sh index 635e93ea..bc5d1b32 100755 --- a/data/Dockerfiles/clamav/bootstrap.sh +++ b/data/Dockerfiles/clamav/bootstrap.sh @@ -1,35 +1,7 @@ #!/bin/bash -# bootstrap clam av service and clam av database updater shell script -# presented by mko (Markus Kosmal) -set -m +trap "kill 0" SIGINT -# start clam service itself and the updater in background as daemon freshclam -d & clamd & -# recognize PIDs -pidlist=`jobs -p` - -# initialize latest result var -latest_exit=0 - -# define shutdown helper -function shutdown() { - trap "" SUBS - - for single in $pidlist; do - if ! kill -0 $pidlist 2>/dev/null; then - wait $pidlist - exitcode=$? - fi - done - - kill $pidlist 2>/dev/null -} - -# run shutdown -trap terminate SUBS -wait - -# return received result -exit $latest_exit +sleep inf diff --git a/data/Dockerfiles/clamav/dl_files.sh b/data/Dockerfiles/clamav/dl_files.sh new file mode 100755 index 00000000..09d61241 --- /dev/null +++ b/data/Dockerfiles/clamav/dl_files.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +declare -a DB_MIRRORS=( + "switch.clamav.net" + "clamavdb.heanet.ie" + "clamav.iol.cz" + "clamav.univ-nantes.fr" + "clamav.easynet.fr" + "clamav.begi.net" +) +declare -a DB_MIRRORS=( $(shuf -e "${DB_MIRRORS[@]}") ) + +DB_FILES=( + "bytecode.cvd" + "daily.cvd" + "main.cvd" +) + +for i in "${DB_MIRRORS[@]}"; do + for j in "${DB_FILES[@]}"; do + [[ -f "/var/lib/clamav/${j}" && -s "/var/lib/clamav/${j}" ]] && continue; + if [[ $(curl -o /dev/null --connect-timeout 1 \ + --max-time 1 \ + --silent \ + --head \ + --write-out "%{http_code}\n" "${i}/${j}") == 200 ]]; then + curl "${i}/${j}" -o "/var/lib/clamav/${j}" -# + fi + done +done + +chown clamav:clamav /var/lib/clamav/*.cvd From babad4f13732aa86a4ee082661cb4f1b7de0fae3 Mon Sep 17 00:00:00 2001 From: andryyy Date: Thu, 20 Apr 2017 21:14:20 +0200 Subject: [PATCH 5/9] Anti-Virus local configuration for Rspamd, container not enabled by default --- data/conf/rspamd/local.d/antivirus.conf | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/conf/rspamd/local.d/antivirus.conf diff --git a/data/conf/rspamd/local.d/antivirus.conf b/data/conf/rspamd/local.d/antivirus.conf new file mode 100644 index 00000000..51bc46f9 --- /dev/null +++ b/data/conf/rspamd/local.d/antivirus.conf @@ -0,0 +1,8 @@ +clamav { + attachments_only = false; + max_size = 20000000; + symbol = "CLAM_VIRUS"; + type = "clamav"; + log_clean = true; + servers = "clamd:3310"; +} From 95cbfe366130023acaa42f59617dd6d9e11a53bc Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 21 Apr 2017 10:19:07 +0200 Subject: [PATCH 6/9] Move mail to spam when DKIM fails, ignore when white/blacklist and honor other actions --- data/conf/rspamd/local.d/force_actions.conf | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/conf/rspamd/local.d/force_actions.conf diff --git a/data/conf/rspamd/local.d/force_actions.conf b/data/conf/rspamd/local.d/force_actions.conf new file mode 100644 index 00000000..7b97e437 --- /dev/null +++ b/data/conf/rspamd/local.d/force_actions.conf @@ -0,0 +1,7 @@ +rules { + DKIM_FAIL { + action = "add header"; + expression = "R_DKIM_REJECT & !MAILLIST & !MAILCOW_WHITE & !MAILCOW_BLACK"; + require_action = ["no action", "greylist"]; + } +} From be28877f685d764be8f9ef39331cac44fe5e3213 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 21 Apr 2017 10:19:24 +0200 Subject: [PATCH 7/9] Remove permanent moo symbol --- data/conf/rspamd/lua/rspamd.local.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 7d6aaa20..09cf9d10 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -7,10 +7,6 @@ rspamd_config.MAILCOW_AUTH = { end } -rspamd_config.MAILCOW_MOO = function (task) - return true -end - modify_subject_map = rspamd_config:add_map({ url = 'http://172.22.1.251:8081/tags.php', type = 'map', From 8adcc4fcd3668c48a45372a81cd2b62c92b01dc0 Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 21 Apr 2017 10:19:45 +0200 Subject: [PATCH 8/9] Force add mailcow_black/white --- data/conf/rspamd/dynmaps/settings.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php index 9be1f696..21377e00 100644 --- a/data/conf/rspamd/dynmaps/settings.php +++ b/data/conf/rspamd/dynmaps/settings.php @@ -207,8 +207,11 @@ while ($row = array_shift($rows)) { } ?> apply "default" { - MAILCOW_MOO = -999.0; + MAILCOW_WHITE = -999.0; } + symbols [ + "MAILCOW_WHITE" + ] } apply "default" { - MAILCOW_MOO = 999.0; + MAILCOW_BLACK = 999.0; } + symbols [ + "MAILCOW_BLACK" + ] } -} \ No newline at end of file +} From 790c92237524ba99efb359fa5bc4b48082ca256b Mon Sep 17 00:00:00 2001 From: andryyy Date: Fri, 21 Apr 2017 10:20:31 +0200 Subject: [PATCH 9/9] New db init system --- data/web/inc/functions.inc.php | 153 ++------ data/web/inc/init_db.inc.php | 584 +++++++++++++++++++++++++++++ data/web/inc/prerequisites.inc.php | 17 +- 3 files changed, 621 insertions(+), 133 deletions(-) create mode 100644 data/web/inc/init_db.inc.php diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 465ab2ac..a7f8f54f 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -62,69 +62,6 @@ function hasMailboxObjectAccess($username, $role, $object) { } return false; } -function init_db_schema() { - // This will be much better in future releases... - global $pdo; - try { - $stmt = $pdo->prepare("SELECT NULL FROM `admin`, `imapsync`, `tfa`"); - $stmt->execute(); - } - catch (Exception $e) { - $lines = file('/web/inc/init.sql'); - $data = ''; - foreach ($lines as $line) { - if (substr($line, 0, 2) == '--' || $line == '') { - continue; - } - $data .= $line; - if (substr(trim($line), -1, 1) == ';') { - $pdo->query($data); - $data = ''; - } - } - // Create index if not exists - $stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_folder_id_idx'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("CREATE INDEX sogo_acl_c_folder_id_idx ON sogo_acl(c_folder_id)"); - } - $stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_uid_idx'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("CREATE INDEX sogo_acl_c_uid_idx ON sogo_acl(c_uid)"); - } - $_SESSION['return'] = array( - 'type' => 'success', - 'msg' => 'Database initialization completed.' - ); - } - // Add newly added columns - $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("ALTER TABLE `mailbox` ADD `kind` VARCHAR(100) NOT NULL DEFAULT ''"); - } - $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'multiple_bookings'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("ALTER TABLE `mailbox` ADD `multiple_bookings` tinyint(1) NOT NULL DEFAULT '0'"); - } - $stmt = $pdo->query("SHOW COLUMNS FROM `imapsync` LIKE 'delete1'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("ALTER TABLE `imapsync` ADD `delete1` tinyint(1) NOT NULL DEFAULT '0'"); - } - $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'wants_tagged_subject'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("ALTER TABLE `mailbox` ADD `wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0'"); - } - $stmt = $pdo->query("SHOW COLUMNS FROM `tfa` LIKE 'key_id'"); - $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); - if ($num_results == 0) { - $pdo->query("ALTER TABLE `tfa` ADD `key_id` VARCHAR(255) DEFAULT 'unidentified'"); - } -} function verify_ssha256($hash, $password) { // Remove tag if any $hash = ltrim($hash, '{SSHA256}'); @@ -287,13 +224,11 @@ function edit_admin_account($postarray) { $password_hashed = hash_password($password); try { $stmt = $pdo->prepare("UPDATE `admin` SET - `modified` = :modified, `password` = :password_hashed, `username` = :username1 WHERE `username` = :username2"); $stmt->execute(array( ':password_hashed' => $password_hashed, - ':modified' => date('Y-m-d H:i:s'), ':username1' => $username, ':username2' => $username_now )); @@ -309,12 +244,10 @@ function edit_admin_account($postarray) { else { try { $stmt = $pdo->prepare("UPDATE `admin` SET - `modified` = :modified, `username` = :username1 WHERE `username` = :username2"); $stmt->execute(array( ':username1' => $username, - ':modified' => date('Y-m-d H:i:s'), ':username2' => $username_now )); } @@ -608,10 +541,9 @@ function edit_user_account($postarray) { } $password_hashed = hash_password($password_new); try { - $stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username"); + $stmt = $pdo->prepare("UPDATE `mailbox` SET `password` = :password_hashed WHERE `username` = :username"); $stmt->execute(array( ':password_hashed' => $password_hashed, - ':modified' => date('Y-m-d H:i:s'), ':username' => $username )); } @@ -1633,13 +1565,11 @@ function add_domain_admin($postarray) { } } try { - $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `created`, `modified`, `active`) - VALUES (:username, :password_hashed, '0', :created, :modified, :active)"); + $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`) + VALUES (:username, :password_hashed, '0', :active)"); $stmt->execute(array( ':username' => $username, ':password_hashed' => $password_hashed, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); } @@ -2228,12 +2158,11 @@ function edit_domain_admin($postarray) { } $password_hashed = hash_password($password); try { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active, `password` = :password_hashed WHERE `username` = :username2"); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active, `password` = :password_hashed WHERE `username` = :username2"); $stmt->execute(array( ':password_hashed' => $password_hashed, ':username1' => $username, ':username2' => $username_now, - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); if (isset($postarray['disable_tfa'])) { @@ -2255,11 +2184,10 @@ function edit_domain_admin($postarray) { } else { try { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active WHERE `username` = :username2"); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active WHERE `username` = :username2"); $stmt->execute(array( ':username1' => $username, ':username2' => $username_now, - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); if (isset($postarray['disable_tfa'])) { @@ -2321,10 +2249,9 @@ function edit_domain_admin($postarray) { } $password_hashed = hash_password($password_new); try { - $stmt = $pdo->prepare("UPDATE `admin` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username"); + $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username"); $stmt->execute(array( ':password_hashed' => $password_hashed, - ':modified' => date('Y-m-d H:i:s'), ':username' => $username )); } @@ -2356,7 +2283,7 @@ function get_admin_details() { return false; } try { - $stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin`WHERE `superadmin`='1' AND active='1'"); + $stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'"); $stmt->execute(); $data = $stmt->fetch(PDO::FETCH_ASSOC); } @@ -2601,8 +2528,8 @@ function mailbox_add_domain($postarray) { } try { - $stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `created`, `modified`, `active`, `relay_all_recipients`) - VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :created, :modified, :active, :relay_all_recipients)"); + $stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `active`, `relay_all_recipients`) + VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :active, :relay_all_recipients)"); $stmt->execute(array( ':domain' => $domain, ':description' => $description, @@ -2612,8 +2539,6 @@ function mailbox_add_domain($postarray) { ':quota' => $quota, ':backupmx' => $backupmx, ':active' => $active, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':relay_all_recipients' => $relay_all_recipients )); $_SESSION['return'] = array( @@ -2789,16 +2714,14 @@ function mailbox_add_alias($postarray) { $goto = implode(",", $gotos); try { - $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`) - VALUES (:address, :goto, :domain, :created, :modified, :active)"); + $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`) + VALUES (:address, :goto, :domain, :active)"); if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) { $stmt->execute(array( ':address' => '@'.$domain, ':goto' => $goto, ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); } @@ -2807,8 +2730,6 @@ function mailbox_add_alias($postarray) { ':address' => $address, ':goto' => $goto, ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); } @@ -2909,13 +2830,11 @@ function mailbox_add_alias_domain($postarray) { } try { - $stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `created`, `modified`, `active`) - VALUES (:alias_domain, :target_domain, :created, :modified, :active)"); + $stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `active`) + VALUES (:alias_domain, :target_domain, :active)"); $stmt->execute(array( ':alias_domain' => $alias_domain, ':target_domain' => $target_domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); $_SESSION['return'] = array( @@ -3118,8 +3037,8 @@ function mailbox_add_mailbox($postarray) { } try { - $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`) - VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :created, :modified, :active)"); + $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`) + VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :active)"); $stmt->execute(array( ':username' => $username, ':password_hashed' => $password_hashed, @@ -3128,8 +3047,6 @@ function mailbox_add_mailbox($postarray) { ':quota_b' => $quota_b, ':local_part' => $local_part, ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); @@ -3137,14 +3054,12 @@ function mailbox_add_mailbox($postarray) { VALUES (:username, '0', '0')"); $stmt->execute(array(':username' => $username)); - $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`) - VALUES (:username1, :username2, :domain, :created, :modified, :active)"); + $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`) + VALUES (:username1, :username2, :domain, :active)"); $stmt->execute(array( ':username1' => $username, ':username2' => $username, ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); @@ -3274,15 +3189,13 @@ function mailbox_add_resource($postarray) { } try { - $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`, `multiple_bookings`, `kind`) - VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :created, :modified, :active, :multiple_bookings, :kind)"); + $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`) + VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :active, :multiple_bookings, :kind)"); $stmt->execute(array( ':name' => $name, ':description' => $description, ':local_part' => $local_part, ':domain' => $domain, - ':created' => date('Y-m-d H:i:s'), - ':modified' => date('Y-m-d H:i:s'), ':active' => $active, ':kind' => $kind, ':multiple_bookings' => $multiple_bookings @@ -3373,12 +3286,10 @@ function mailbox_edit_alias_domain($postarray) { try { $stmt = $pdo->prepare("UPDATE `alias_domain` SET `alias_domain` = :alias_domain, - `active` = :active, - `modified` = :modified + `active` = :active WHERE `alias_domain` = :alias_domain_now"); $stmt->execute(array( ':alias_domain' => $alias_domain, - ':modified' => date('Y-m-d H:i:s'), ':alias_domain_now' => $alias_domain_now, ':active' => $active )); @@ -3454,14 +3365,12 @@ function mailbox_edit_alias($postarray) { try { $stmt = $pdo->prepare("UPDATE `alias` SET `goto` = :goto, - `active`= :active, - `modified` = :modified + `active`= :active WHERE `address` = :address"); $stmt->execute(array( ':goto' => $goto, ':active' => $active, - ':address' => $address, - ':modified' => date('Y-m-d H:i:s'), + ':address' => $address )); $_SESSION['return'] = array( 'type' => 'success', @@ -3506,11 +3415,9 @@ function mailbox_edit_domain($postarray) { isset($postarray['active']) ? $active = '1' : $active = '0'; try { $stmt = $pdo->prepare("UPDATE `domain` SET - `modified`= :modified, `description` = :description WHERE `domain` = :domain"); $stmt->execute(array( - ':modified' => date('Y-m-d H:i:s'), ':description' => $description, ':domain' => $domain )); @@ -3614,7 +3521,6 @@ function mailbox_edit_domain($postarray) { } try { $stmt = $pdo->prepare("UPDATE `domain` SET - `modified`= :modified, `relay_all_recipients` = :relay_all_recipients, `backupmx` = :backupmx, `active` = :active, @@ -3632,7 +3538,6 @@ function mailbox_edit_domain($postarray) { ':maxquota' => $maxquota, ':mailboxes' => $mailboxes, ':aliases' => $aliases, - ':modified' => date('Y-m-d H:i:s'), ':description' => $description, ':domain' => $domain )); @@ -3844,23 +3749,19 @@ function mailbox_edit_mailbox($postarray) { $password_hashed = hash_password($password); try { $stmt = $pdo->prepare("UPDATE `alias` SET - `modified` = :modified, `active` = :active WHERE `address` = :address"); $stmt->execute(array( ':address' => $username, - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); $stmt = $pdo->prepare("UPDATE `mailbox` SET - `modified` = :modified, `active` = :active, `password` = :password_hashed, `name`= :name, `quota` = :quota_b WHERE `username` = :username"); $stmt->execute(array( - ':modified' => date('Y-m-d H:i:s'), ':password_hashed' => $password_hashed, ':active' => $active, ':name' => $name, @@ -3883,23 +3784,19 @@ function mailbox_edit_mailbox($postarray) { } try { $stmt = $pdo->prepare("UPDATE `alias` SET - `modified` = :modified, `active` = :active WHERE `address` = :address"); $stmt->execute(array( ':address' => $username, - ':modified' => date('Y-m-d H:i:s'), ':active' => $active )); $stmt = $pdo->prepare("UPDATE `mailbox` SET - `modified` = :modified, `active` = :active, `name`= :name, `quota` = :quota_b WHERE `username` = :username"); $stmt->execute(array( ':active' => $active, - ':modified' => date('Y-m-d H:i:s'), ':name' => $name, ':quota_b' => $quota_b, ':username' => $username @@ -3962,7 +3859,6 @@ function mailbox_edit_resource($postarray) { try { $stmt = $pdo->prepare("UPDATE `mailbox` SET - `modified` = :modified, `active` = :active, `name`= :description, `kind`= :kind, @@ -3970,7 +3866,6 @@ function mailbox_edit_resource($postarray) { WHERE `username` = :name"); $stmt->execute(array( ':active' => $active, - ':modified' => date('Y-m-d H:i:s'), ':description' => $description, ':multiple_bookings' => $multiple_bookings, ':kind' => $kind, @@ -4796,12 +4691,10 @@ function mailbox_delete_mailbox($postarray) { } $gotos_rebuild = implode(',', $goto_exploded); $stmt = $pdo->prepare("UPDATE `alias` SET - `goto` = :goto, - `modified` = :modified, + `goto` = :goto WHERE `address` = :address"); $stmt->execute(array( ':goto' => $gotos_rebuild, - ':modified' => date('Y-m-d H:i:s'), ':address' => $gotos['address'] )); } diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php new file mode 100644 index 00000000..37c04a12 --- /dev/null +++ b/data/web/inc/init_db.inc.php @@ -0,0 +1,584 @@ +query("SHOW TABLES LIKE 'versions'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results != 0) { + $stmt = $pdo->query("SELECT `version` FROM `versions`"); + if ($stmt->fetch(PDO::FETCH_ASSOC)['version'] == $db_version) { + return true; + } + } + + $views = array( + "grouped_mail_aliases" => "CREATE VIEW grouped_mail_aliases (username, aliases) AS + SELECT goto, IFNULL(GROUP_CONCAT(address SEPARATOR ' '), '') AS address FROM alias + WHERE address!=goto + AND active = '1' + AND address NOT LIKE '@%' + GROUP BY goto;", + "grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as) AS + SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl + WHERE send_as NOT LIKE '@%' + GROUP BY logged_in_as;", + "grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS + SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox + LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;" + ); + + $tables = array( + "versions" => array( + "cols" => array( + "application" => "VARCHAR(255) NOT NULL", + "version" => "VARCHAR(100) NOT NULL", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + ), + "keys" => array( + "primary" => array( + "" => array("application") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "admin" => array( + "cols" => array( + "username" => "VARCHAR(255) NOT NULL", + "password" => "VARCHAR(255) NOT NULL", + "superadmin" => "TINYINT(1) NOT NULL DEFAULT '0'", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE NOW(0)", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("username") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "alias" => array( + "cols" => array( + "address" => "VARCHAR(255) NOT NULL", + "goto" => "TEXT NOT NULL", + "domain" => "VARCHAR(255) NOT NULL", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("address") + ), + "key" => array( + "domain" => array("domain") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sender_acl" => array( + "cols" => array( + "logged_in_as" => "VARCHAR(255) NOT NULL", + "send_as" => "VARCHAR(255) NOT NULL" + ), + "keys" => array(), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "domain" => array( + "cols" => array( + "domain" => "VARCHAR(255) NOT NULL", + "description" => "VARCHAR(255)", + "aliases" => "INT(10) NOT NULL DEFAULT '0'", + "mailboxes" => "INT(10) NOT NULL DEFAULT '0'", + "maxquota" => "BIGINT(20) NOT NULL DEFAULT '0'", + "quota" => "BIGINT(20) NOT NULL DEFAULT '0'", + "transport" => "VARCHAR(255) NOT NULL", + "backupmx" => "TINYINT(1) NOT NULL DEFAULT '0'", + "relay_all_recipients" => "TINYINT(1) NOT NULL DEFAULT '0'", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("domain") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "alias_domain" => array( + "cols" => array( + "alias_domain" => "VARCHAR(255) NOT NULL", + "target_domain" => "VARCHAR(255) NOT NULL", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("alias_domain") + ), + "key" => array( + "active" => array("active"), + "target_domain" => array("target_domain") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "spamalias" => array( + "cols" => array( + "address" => "VARCHAR(255) NOT NULL", + "goto" => "TEXT NOT NULL", + "validity" => "INT(11) NOT NULL" + ), + "keys" => array( + "primary" => array( + "" => array("address") + ), + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "filterconf" => array( + "cols" => array( + "object" => "VARCHAR(255) NOT NULL DEFAULT ''", + "option" => "VARCHAR(50) NOT NULL DEFAULT ''", + "value" => "VARCHAR(100) NOT NULL DEFAULT ''", + "prefid" => "INT(11) NOT NULL AUTO_INCREMENT" + ), + "keys" => array( + "primary" => array( + "" => array("prefid") + ), + "key" => array( + "object" => array("object") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "quota2" => array( + "cols" => array( + "username" => "VARCHAR(255) NOT NULL", + "bytes" => "BIGINT(20) NOT NULL DEFAULT '0'", + "messages" => "BIGINT(20) NOT NULL DEFAULT '0'" + ), + "keys" => array( + "primary" => array( + "" => array("username") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "mailbox" => array( + "cols" => array( + "username" => "VARCHAR(255) NOT NULL", + "password" => "VARCHAR(255) NOT NULL", + "name" => "VARCHAR(255)", + "maildir" => "VARCHAR(255) NOT NULL", + "quota" => "BIGINT(20) NOT NULL DEFAULT '0'", + "local_part" => "VARCHAR(255) NOT NULL", + "domain" => "VARCHAR(255) NOT NULL", + "tls_enforce_in" => "TINYINT(1) NOT NULL DEFAULT '0'", + "tls_enforce_out" => "TINYINT(1) NOT NULL DEFAULT '0'", + "kind" => "VARCHAR(100) NOT NULL DEFAULT ''", + "multiple_bookings" => "TINYINT(1) NOT NULL DEFAULT '0'", + "wants_tagged_subject" => "TINYINT(1) NOT NULL DEFAULT '0'", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "primary" => array( + "" => array("username") + ), + "key" => array( + "domain" => array("domain") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "domain_admins" => array( + "cols" => array( + "username" => "VARCHAR(255) NOT NULL", + "domain" => "VARCHAR(255) NOT NULL", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + ), + "keys" => array( + "key" => array( + "username" => array("username") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "imapsync" => array( + "cols" => array( + "id" => "INT NOT NULL AUTO_INCREMENT", + "user2" => "VARCHAR(255) NOT NULL", + "host1" => "VARCHAR(255) NOT NULL", + "authmech1" => "ENUM('PLAIN','LOGIN','CRAM-MD5') DEFAULT 'PLAIN'", + "regextrans2" => "VARCHAR(255) DEFAULT ''", + "authmd51" => "TINYINT(1) NOT NULL DEFAULT 0", + "domain2" => "VARCHAR(255) NOT NULL DEFAULT ''", + "subfolder2" => "VARCHAR(255) NOT NULL DEFAULT ''", + "user1" => "VARCHAR(255) NOT NULL", + "password1" => "VARCHAR(255) NOT NULL", + "exclude" => "VARCHAR(500) NOT NULL DEFAULT ''", + "maxage" => "SMALLINT NOT NULL DEFAULT '0'", + "mins_interval" => "VARCHAR(50) NOT NULL", + "port1" => "SMALLINT NOT NULL", + "enc1" => "ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS'", + "delete2duplicates" => "TINYINT(1) NOT NULL DEFAULT '1'", + "returned_text" => "TEXT", + "last_run" => "TIMESTAMP NULL DEFAULT NULL", + "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", + "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", + "active" => "TINYINT(1) NOT NULL DEFAULT '0'" + ), + "keys" => array( + "primary" => array( + "" => array("id") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "tfa" => array( + "cols" => array( + "id" => "INT NOT NULL AUTO_INCREMENT", + "username" => "VARCHAR(255) NOT NULL", + "authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp')", + "secret" => "VARCHAR(255) DEFAULT NULL", + "keyHandle" => "VARCHAR(255) DEFAULT NULL", + "publicKey" => "VARCHAR(255) DEFAULT NULL", + "counter" => "INT NOT NULL DEFAULT '0'", + "certificate" => "TEXT", + "active" => "TINYINT(1) NOT NULL DEFAULT '0'" + ), + "keys" => array( + "primary" => array( + "" => array("id") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_acl" => array( + "cols" => array( + "c_folder_id" => "INT NOT NULL", + "c_object" => "VARCHAR(255) NOT NULL", + "c_uid" => "VARCHAR(255) NOT NULL", + "c_role" => "VARCHAR(80) NOT NULL" + ), + "keys" => array( + "key" => array( + "sogo_acl_c_folder_id_idx" => array("c_folder_id"), + "sogo_acl_c_uid_idx" => array("c_uid") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_alarms_folder" => array( + "cols" => array( + "c_path" => "VARCHAR(255) NOT NULL", + "c_name" => "VARCHAR(255) NOT NULL", + "c_uid" => "VARCHAR(255) NOT NULL", + "c_recurrence_id" => "INT(11) DEFAULT NULL", + "c_alarm_number" => "INT(11) NOT NULL", + "c_alarm_date" => "INT(11) NOT NULL" + ), + "keys" => array(), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_cache_folder" => array( + "cols" => array( + "c_uid" => "VARCHAR(255) NOT NULL", + "c_path" => "VARCHAR(255) NOT NULL", + "c_parent_path" => "VARCHAR(255) DEFAULT NULL", + "c_type" => "TINYINT(3) unsigned NOT NULL", + "c_creationdate" => "INT(11) NOT NULL", + "c_lastmodified" => "INT(11) NOT NULL", + "c_version" => "INT(11) NOT NULL DEFAULT '0'", + "c_deleted" => "TINYINT(4) NOT NULL DEFAULT '0'", + "c_content" => "LONGTEXT" + ), + "keys" => array( + "primary" => array( + "" => array("c_uid", "c_path") + ), + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_folder_info" => array( + "cols" => array( + "c_folder_id" => "BIGINT(20) unsigned NOT NULL AUTO_INCREMENT", + "c_path" => "VARCHAR(255) NOT NULL", + "c_path1" => "VARCHAR(255) NOT NULL", + "c_path2" => "VARCHAR(255) DEFAULT NULL", + "c_path3" => "VARCHAR(255) DEFAULT NULL", + "c_path4" => "VARCHAR(255) DEFAULT NULL", + "c_foldername" => "VARCHAR(255) NOT NULL", + "c_location" => "INT NULL", + "c_quick_location" => "VARCHAR(2048) DEFAULT NULL", + "c_acl_location" => "VARCHAR(2048) DEFAULT NULL", + "c_folder_type" => "VARCHAR(255) NOT NULL" + ), + "keys" => array( + "primary" => array( + "" => array("c_path") + ), + "unique" => array( + "c_folder_id" => array("c_folder_id") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_quick_appointment" => array( + "cols" => array( + "c_folder_id" => "INT NOT NULL", + "c_name" => "VARCHAR(255) NOT NULL", + "c_uid" => "VARCHAR(255) NOT NULL", + "c_startdate" => "INT", + "c_enddate" => "INT", + "c_cycleenddate" => "INT", + "c_title" => "VARCHAR(1000) NOT NULL", + "c_participants" => "TEXT", + "c_isallday" => "INT", + "c_iscycle" => "INT", + "c_cycleinfo" => "TEXT", + "c_classification" => "INT NOT NULL", + "c_isopaque" => "INT NOT NULL", + "c_status" => "INT NOT NULL", + "c_priority" => "INT", + "c_location" => "VARCHAR(255)", + "c_orgmail" => "VARCHAR(255)", + "c_partmails" => "TEXT", + "c_partstates" => "TEXT", + "c_category" => "VARCHAR(255)", + "c_sequence" => "INT", + "c_component" => "VARCHAR(10) NOT NULL", + "c_nextalarm" => "INT", + "c_description" => "TEXT" + ), + "keys" => array( + "primary" => array( + "" => array("c_folder_id", "c_name") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_quick_contact" => array( + "cols" => array( + "c_folder_id" => "INT NOT NULL", + "c_name" => "VARCHAR(255) NOT NULL", + "c_givenname" => "VARCHAR(255)", + "c_cn" => "VARCHAR(255)", + "c_sn" => "VARCHAR(255)", + "c_screenname" => "VARCHAR(255)", + "c_l" => "VARCHAR(255)", + "c_mail" => "VARCHAR(255)", + "c_o" => "VARCHAR(255)", + "c_ou" => "VARCHAR(255)", + "c_telephonenumber" => "VARCHAR(255)", + "c_categories" => "VARCHAR(255)", + "c_component" => "VARCHAR(10) NOT NULL" + ), + "keys" => array( + "primary" => array( + "" => array("c_folder_id", "c_name") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_sessions_folder" => array( + "cols" => array( + "c_id" => "VARCHAR(255) NOT NULL", + "c_value" => "VARCHAR(255) NOT NULL", + "c_creationdate" => "INT(11) NOT NULL", + "c_lastseen" => "INT(11) NOT NULL" + ), + "keys" => array( + "primary" => array( + "" => array("c_id") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_store" => array( + "cols" => array( + "c_folder_id" => "INT NOT NULL", + "c_name" => "VARCHAR(255) NOT NULL", + "c_content" => "MEDIUMTEXT NOT NULL", + "c_creationdate" => "INT NOT NULL", + "c_lastmodified" => "INT NOT NULL", + "c_version" => "INT NOT NULL", + "c_deleted" => "INT" + ), + "keys" => array( + "primary" => array( + "" => array("c_folder_id", "c_name") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ), + "sogo_user_profile" => array( + "cols" => array( + "c_uid" => "VARCHAR(255) NOT NULL", + "c_defaults" => "TEXT", + "c_settings" => "TEXT" + ), + "keys" => array( + "primary" => array( + "" => array("c_uid") + ) + ), + "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" + ) + ); + + foreach ($tables as $table => $properties) { + $stmt = $pdo->query("SHOW TABLES LIKE '" . $table . "'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results != 0) { + foreach($properties['cols'] as $column => $type) { + $stmt = $pdo->query("SHOW COLUMNS FROM `" . $table . "` LIKE '" . $column . "'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results == 0) { + $pdo->query("ALTER TABLE `" . $table . "` ADD `" . $column . "` " . $type); + } + else { + $pdo->query("ALTER TABLE `" . $table . "` MODIFY COLUMN `" . $column . "` " . $type); + } + } + foreach($properties['keys'] as $key_type => $key_content) { + if (strtolower($key_type) == 'primary') { + foreach ($key_content as $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = 'PRIMARY'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + $is_drop = ($num_results != 0) ? "DROP PRIMARY KEY, " : ""; + $pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD PRIMARY KEY (" . $fields . ")"); + } + } + if (strtolower($key_type) == 'key') { + foreach ($key_content as $key_name => $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = '" . $key_name . "'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + $is_drop = ($num_results != 0) ? "DROP INDEX `" . $key_name . "`, " : ""; + $pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD KEY `" . $key_name . "` (" . $fields . ")"); + } + } + if (strtolower($key_type) == 'unique') { + foreach ($key_content as $key_name => $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = '" . $key_name . "'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + $is_drop = ($num_results != 0) ? "DROP INDEX `" . $key_name . "`, " : ""; + $pdo->query("ALTER TABLE `" . $table . "` " . $is_drop . "ADD UNIQUE KEY `" . $key_name . "` (" . $fields . ")"); + } + } + } + // Drop all vanished columns + $stmt = $pdo->query("SHOW COLUMNS FROM `" . $table . "`"); + $cols_in_table = $stmt->fetchAll(PDO::FETCH_ASSOC); + while ($row = array_shift($cols_in_table)) { + if (!array_key_exists($row['Field'], $properties['cols'])) { + $pdo->query("ALTER TABLE `" . $table . "` DROP COLUMN `" . $row['Field'] . "`;"); + } + } + + // Step 1: Get all non-primary keys, that currently exist and those that should exist + $stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE `Key_name` != 'PRIMARY'"); + $keys_in_table = $stmt->fetchAll(PDO::FETCH_ASSOC); + $keys_to_exist = array(); + if (isset($properties['keys']['unique']) && is_array($properties['keys']['unique'])) { + foreach ($properties['keys']['unique'] as $key_name => $key_values) { + $keys_to_exist[] = $key_name; + } + } + if (isset($properties['keys']['key']) && is_array($properties['keys']['key'])) { + foreach ($properties['keys']['key'] as $key_name => $key_values) { + $keys_to_exist[] = $key_name; + } + } + // Step 2: Drop all vanished indexes + while ($row = array_shift($keys_in_table)) { + if (!in_array($row['Key_name'], $keys_to_exist)) { + $pdo->query("ALTER TABLE `" . $table . "` DROP INDEX `" . $row['Key_name'] . "`"); + } + } + // Step 3: Drop all vanished primary keys + if (!isset($properties['keys']['primary'])) { + $stmt = $pdo->query("SHOW KEYS FROM `" . $table . "` WHERE Key_name = 'PRIMARY'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results != 0) { + $pdo->query("ALTER TABLE `" . $table . "` DROP PRIMARY KEY"); + } + } + } + else { + // Create table if it is missing + $sql = "CREATE TABLE IF NOT EXISTS `" . $table . "` ("; + foreach($properties['cols'] as $column => $type) { + $sql .= $column . " " . $type . ","; + } + foreach($properties['keys'] as $key_type => $key_content) { + if (strtolower($key_type) == 'primary') { + foreach ($key_content as $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $sql .= "PRIMARY KEY (" . $fields . ")" . ","; + } + } + elseif (strtolower($key_type) == 'key') { + foreach ($key_content as $key_name => $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $sql .= "KEY `" . $key_name . "` (" . $fields . ")" . ","; + } + } + elseif (strtolower($key_type) == 'unique') { + foreach ($key_content as $key_name => $key_values) { + $fields = "`" . implode("`, `", $key_values) . "`"; + $sql .= "UNIQUE KEY `" . $key_name . "` (" . $fields . ")" . ","; + } + } + } + $sql = rtrim($sql, ","); + $sql .= ") " . $properties['attr']; + $pdo->query($sql); + } + // Reset table attributes + $pdo->query("ALTER TABLE `" . $table . "` " . $properties['attr'] . ";"); + } + + // Recreate SQL views + foreach ($views as $view => $create) { + $pdo->query("DROP VIEW IF EXISTS `" . $view . "`;"); + $pdo->query($create); + } + + // Inject admin if not exists + $stmt = $pdo->query("INSERT INTO `admin` (`username`, `password`, `superadmin`, `created`, `modified`, `active`) + SELECT 'admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1 + WHERE NOT EXISTS (SELECT * FROM `admin`);"); + $stmt = $pdo->query("DELETE FROM `domain_admins`;"); + $stmt = $pdo->query("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) + SELECT `username`, 'ALL', NOW(), 1 FROM `admin` + WHERE superadmin='1' AND `username` NOT IN (SELECT `username` FROM `domain_admins`);"); + + // Insert new DB schema version + $stmt = $pdo->query("REPLACE INTO `versions` (`application`, `version`) VALUES ('db_schema', '" . $db_version . "');"); + + $_SESSION['return'] = array( + 'type' => 'success', + 'msg' => 'Database initialisation completed' + ); + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'Database initialisation failed: ' . $e->getMessage() + ); + } +} +?> \ No newline at end of file diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 1959ae18..c766e780 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -29,18 +29,28 @@ require_once 'inc/lib/U2F.php'; $u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']); // PDO -$dsn = "$database_type:host=$database_host;dbname=$database_name"; +// Calculate offset +$now = new DateTime(); +$mins = $now->getOffset() / 60; +$sgn = ($mins < 0 ? -1 : 1); +$mins = abs($mins); +$hrs = floor($mins / 60); +$mins -= $hrs * 60; +$offset = sprintf('%+d:%02d', $hrs*$sgn, $mins); + +$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name; $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, + PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '" . $offset . "'", ]; try { $pdo = new PDO($dsn, $database_user, $database_pass, $opt); } catch (PDOException $e) { ?> -
🐮 Connection failed, database may be in warm-up state, please try again later.

The following error was reported:
getMessage();?>
+
?? Connection failed, database may be in warm-up state, please try again later.

The following error was reported:
getMessage();?>