From 7de8304a8f4116845d773f736f3df79dea8ea0c6 Mon Sep 17 00:00:00 2001
From: Herman van Rink <rink@initfour.nl>
Date: Sun, 12 Jun 2022 20:23:31 +0200
Subject: [PATCH] Squashed commit of the following: 6113-undo-delete-mailbox

commit 0fd8879dd95feb2edee9cffb6f8009ff8f1699aa
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 22 23:41:40 2022 +0200

    comment

commit a1fd6fdeb0de4d3800a978138e0382bc1768afde
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 22 23:40:37 2022 +0200

    undo intentional crippling

commit 396b21da1d6d0c3af0432960012596b5281c3e76
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 22 23:40:18 2022 +0200

    whitespace

commit 8e8e97e08d0162bd894a56d0099a343a19d7db3b
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 22 23:21:37 2022 +0200

    Make the thrash filename extra obvious, also for a mail_domain

commit 61bdec807c268d1b4ffaa385f161bd4dfd012aee
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 8 21:24:23 2022 +0200

    Make the thrash filename extra obvious

commit 174d70a11ed18942864817fed8c0d3ca226dc62b
Merge: 401931f04 88edc27ec
Author: Herman van Rink <rink@initfour.nl>
Date:   Fri Apr 1 21:02:34 2022 +0200

    Merge remote-tracking branch 'origin/develop' into 6113-undo-delete-mailbox

commit 401931f04523492c696f09e31d04329ab77086de
Author: Herman van Rink <rink@initfour.nl>
Date:   Mon Jan 24 08:25:52 2022 +0100

    Add Extra mailbox_soft_delete_info_txt string + translations

commit f16295d8d2547d2b1b9107c6e1cbc1cc461a045f
Author: Herman van Rink <rink@initfour.nl>
Date:   Mon Jan 24 08:23:36 2022 +0100

    whitespace

commit 757bcbcd8b6d8d199a3f5963fa1862986e224350
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 23:28:35 2022 +0100

    Default mailbox_soft_delete to n, to preserve current behaviour

commit 5e3c26dde6159bd108a02f74f87d91b468be95a9
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 22:41:26 2022 +0100

    Add script to handle soft deleted mailboxes, #6113

commit 93a3ed05eee33b1cad7cbf1a969fa33469da6faa
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 22:31:11 2022 +0100

    Update the dir's timestamp to make filtering on age easier in any cleanup cronjob.

commit 718a7b29da0a49d9ee82b07c5e76620fd79cf39c
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 22:17:19 2022 +0100

    s/safe_delete/soft_delete/

commit ff445dc549528d41ef6963f5340f82afeccced18
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 20:58:04 2022 +0100

    logic fix

commit 13edba6b7903e9c8771bc5f1f4ea711069223c18
Author: Herman van Rink <rink@initfour.nl>
Date:   Sun Jan 23 20:49:39 2022 +0100

    Change mailbox_safe_delete default to n, so existing behaviour does not changes

commit 6a890347ef3b20fe00e7b90ab541c6246bfcd4f1
Author: Herman van Rink <rink@initfour.nl>
Date:   Thu Mar 18 23:00:13 2021 +0100

    Safe delete for maildomains

commit 1d041126d5462e5db6e59e0102c62dcd4432647b
Author: Herman van Rink <rink@initfour.nl>
Date:   Thu Mar 18 21:56:01 2021 +0100

    Safe delete for mailboxes

    Move it, adding a date based suffix. A cronjob should purge or archive.
---
 install/lib/installer_base.lib.php            |  4 ++
 install/tpl/server.ini.master                 |  1 +
 .../web/admin/form/server_config.tform.php    |  6 +++
 .../web/admin/lib/lang/en_server_config.lng   |  2 +
 .../web/admin/lib/lang/nl_server_config.lng   |  2 +
 .../templates/server_config_mail_edit.htm     |  6 +++
 server/plugins-available/mail_plugin.inc.php  | 30 ++++++++++--
 server/scripts/handle_mailbox_soft_deleted.sh | 49 +++++++++++++++++++
 8 files changed, 96 insertions(+), 4 deletions(-)
 create mode 100644 server/scripts/handle_mailbox_soft_deleted.sh

diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index a73b9d0922..71753a6b33 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -3847,6 +3847,10 @@ class installer_base {
 			$root_cron_jobs[] = "0 0 * * * ".$install_dir."/server/scripts/create_daily_nginx_access_logs.sh &> /dev/null";
 		}
 
+		if ($conf['services']['mail'] == 1) {
+			$root_cron_jobs[] = "30 23 * * * ".$install_dir."/server/scripts/handle_mailbox_soft_deleted.sh &> /dev/null";
+		}
+
 		foreach($root_cron_jobs as $cron_job) {
 			if(!in_array($cron_job."\n", $existing_root_cron_jobs)) {
 				$existing_root_cron_jobs[] = $cron_job."\n";
diff --git a/install/tpl/server.ini.master b/install/tpl/server.ini.master
index f6ab6365df..a0faf66908 100644
--- a/install/tpl/server.ini.master
+++ b/install/tpl/server.ini.master
@@ -56,6 +56,7 @@ relayhost_user=
 relayhost_password=
 mailbox_size_limit=0
 message_size_limit=0
+mailbox_soft_delete=n
 mailbox_quota_stats=y
 realtime_blackhole_list=zen.spamhaus.org
 overquota_notify_threshold=90
diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php
index e1ca3c7ca0..7af535e600 100644
--- a/interface/web/admin/form/server_config.tform.php
+++ b/interface/web/admin/form/server_config.tform.php
@@ -734,6 +734,12 @@ $form["tabs"]['mail'] = array(
 			'default' => 'y',
 			'value' => array(0 => 'n', 1 => 'y')
 		),
+		'mailbox_soft_delete' => array (
+			'datatype' => 'VARCHAR',
+			'formtype' => 'CHECKBOX',
+			'default' => 'n',
+			'value' => array(0 => 'n', 1 => 'y')
+		),
 		'mailbox_quota_stats' => array (
 			'datatype' => 'VARCHAR',
 			'formtype' => 'CHECKBOX',
diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng
index d730862ba7..1ee224a44a 100644
--- a/interface/web/admin/lib/lang/en_server_config.lng
+++ b/interface/web/admin/lib/lang/en_server_config.lng
@@ -355,3 +355,5 @@ $wb['tooltip_jailkit_hardlinks_txt'] = 'Using hardlinks is insecure, but saves d
 $wb['jailkit_hardlinks_allow_txt'] = 'Allow hardlinks within the jail';
 $wb['jailkit_hardlinks_no_txt'] = 'No, remove hardlinked files';
 $wb['jailkit_hardlinks_yes_txt'] = 'Yes, use hardlinks if possible';
+$wb['mailbox_soft_delete_txt'] = 'Mailbox soft delete';
+$wb['mailbox_soft_delete_info_txt'] = 'by default cleaned up after 7 days.';
diff --git a/interface/web/admin/lib/lang/nl_server_config.lng b/interface/web/admin/lib/lang/nl_server_config.lng
index 66394fe32e..2af5d6a9ea 100644
--- a/interface/web/admin/lib/lang/nl_server_config.lng
+++ b/interface/web/admin/lib/lang/nl_server_config.lng
@@ -349,3 +349,5 @@ $wb['bind_keyfiles_dir_error_empty'] = 'BIND keyfiles directory is empty.';
 $wb['bind_zonefiles_masterprefix_error_regex'] = 'Invalid BIND zonefiles master prefix.';
 $wb['bind_zonefiles_slaveprefix_error_regex'] = 'Invalid BIND zonefiles slave prefix.';
 $wb['bind_keyfiles_dir_error_regex'] = 'Invalid BIND keyfiles directory.';
+$wb['mailbox_soft_delete_txt'] = 'Mailbox soft delete';
+$wb['mailbox_soft_delete_info_txt'] = 'by default cleaned up after 7 days.';
diff --git a/interface/web/admin/templates/server_config_mail_edit.htm b/interface/web/admin/templates/server_config_mail_edit.htm
index 1876b78f4b..84683191c8 100644
--- a/interface/web/admin/templates/server_config_mail_edit.htm
+++ b/interface/web/admin/templates/server_config_mail_edit.htm
@@ -140,6 +140,12 @@
                     <a data-toggle="tooltip" title="{tmpl_var name='tooltip_stress_adaptive_txt'}">{tmpl_var name="stress_adaptive"}</a>
                 </div>
             </div>
+            <div class="form-group">
+                <label class="col-sm-3 control-label">{tmpl_var name='mailbox_soft_delete_txt'}</label>
+                <div class="col-sm-9">
+                    {tmpl_var name='mailbox_soft_delete'}&nbsp;{tmpl_var name='mailbox_soft_delete_info_txt'}
+                </div>
+            </div>
             <div class="form-group">
                 <label class="col-sm-3 control-label">{tmpl_var name='mailbox_quota_stats_txt'}</label>
                 <div class="col-sm-9">
diff --git a/server/plugins-available/mail_plugin.inc.php b/server/plugins-available/mail_plugin.inc.php
index 2baf07ec7d..53836a1392 100644
--- a/server/plugins-available/mail_plugin.inc.php
+++ b/server/plugins-available/mail_plugin.inc.php
@@ -429,8 +429,19 @@ class mail_plugin {
 		$maildir_path_deleted = false;
 		$old_maildir_path = $data['old']['maildir'];
 		if($old_maildir_path != $mail_config['homedir_path'] && strlen($old_maildir_path) > strlen($mail_config['homedir_path']) && !stristr($old_maildir_path, '//') && !stristr($old_maildir_path, '..') && !stristr($old_maildir_path, '*') && strlen($old_maildir_path) >= 10) {
-			$app->system->exec_safe('rm -rf ?', $old_maildir_path);
-			$app->log('Deleted the Maildir: '.$data['old']['maildir'], LOGLEVEL_DEBUG);
+			if ($mail_config['mailbox_soft_delete'] == 'y') {
+				// Move it, adding a date based suffix. A cronjob should purge or archive.
+				$thrash_maildir_path = $old_maildir_path . '-deleted-' . date("YmdHis");
+				$app->system->exec_safe('mv ? ?', $old_maildir_path, $thrash_maildir_path);
+
+				// Update the dir's timestamp to make filtering on age easier in any cleanup cronjob.
+				$app->system->exec_safe('touch ?', $thrash_maildir_path);
+
+				$app->log('Renamed the Maildir: ' . $data['old']['maildir'] . ' to ' . $thrash_maildir_path, LOGLEVEL_DEBUG);
+			} else  {
+				$app->system->exec_safe('rm -rf ?', $old_maildir_path);
+				$app->log('Deleted the Maildir: '.$data['old']['maildir'], LOGLEVEL_DEBUG);
+			}
 			$maildir_path_deleted = true;
 		} else {
 			$app->log('Possible security violation when deleting the maildir: '.$data['old']['maildir'], LOGLEVEL_ERROR);
@@ -473,8 +484,19 @@ class mail_plugin {
 		//* Delete maildomain path
 		$old_maildomain_path = $mail_config['homedir_path'].'/'.$data['old']['domain'];
 		if($old_maildomain_path != $mail_config['homedir_path'] && !stristr($old_maildomain_path, '//') && !stristr($old_maildomain_path, '..') && !stristr($old_maildomain_path, '*') && !stristr($old_maildomain_path, '&') && strlen($old_maildomain_path) >= 10  && !empty($data['old']['domain'])) {
-			$app->system->exec_safe('rm -rf ?', $old_maildomain_path);
-			$app->log('Deleted the mail domain directory: '.$old_maildomain_path, LOGLEVEL_DEBUG);
+			if ($mail_config['mailbox_soft_delete'] == 'y') {
+				// Move it, adding a date based suffix. A cronjob should purge or archive.
+				$thrash_maildomain_path = $old_maildomain_path . '-deleted-' . date("YmdHis");
+				$app->system->exec_safe('mv ? ?', $old_maildomain_path, $thrash_maildomain_path);
+
+				// Update the dir's timestamp to make filtering on age easier in any cleanup cronjob.
+				$app->system->exec_safe('touch ?', $thrash_maildomain_path);
+
+				$app->log('Renamed the mail domain directory: ' . $old_maildomain_path . ' to ' . $thrash_maildomain_path, LOGLEVEL_DEBUG);
+			} else  {
+				$app->system->exec_safe('rm -rf ?', $old_maildomain_path);
+				$app->log('Deleted the mail domain directory: '.$old_maildomain_path, LOGLEVEL_DEBUG);
+			}
 			$maildomain_path_deleted = true;
 		} else {
 			$app->log('Possible security violation when deleting the mail domain directory: '.$old_maildomain_path, LOGLEVEL_ERROR);
diff --git a/server/scripts/handle_mailbox_soft_deleted.sh b/server/scripts/handle_mailbox_soft_deleted.sh
new file mode 100644
index 0000000000..7c7f280839
--- /dev/null
+++ b/server/scripts/handle_mailbox_soft_deleted.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Archive directories for deleted mailboxes.
+delay_days=7
+
+# Test if there is something to do... to avoid 'No such file or directory' from find later.
+ls /var/vmail/*/[a-z0-9.-]*-deleted-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] >/dev/null 2>&1
+if [ $? != 0 ]; then
+        exit 0;
+fi
+
+function remove_soft_deleted_mailbox {
+        dir=$1
+
+        echo "Purging $dir"
+        rm -r "$dir"
+}
+
+function compress_soft_deleted_mailbox {
+        dir=$1
+
+        backupfile="${dir}.tar.bz2"
+
+        # Test if backup file already exists
+        if [ -f $backupfile ]; then
+                # Skip
+                echo "ERROR: Backupfile($backupfile) exists!" >&2
+                continue
+        fi
+
+        echo "Compressing for $dir"
+        tar cvfj "$backupfile" --remove-files "$dir" 2> >( grep -v "tar: Removing leading" >&2)
+}
+
+# List deleted mailboxs to archive
+# -mtime +7 ===> Only mailboxes deleted more then 7 days ago
+# Test that the last dir component matches e.g. xxx-deleted-20220101094242 (14 digits)
+# command: xxx-`date "+%Y%m%d%H%M%S"`
+find /var/vmail/*/[a-z0-9.-]*-deleted-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]  -maxdepth 0 -type d -mtime +$delay_days | while read line; do
+        # example $line: "/var/vmail/example.com/info-20220101094242"
+
+        dir=$line
+
+        # Uncomment the desired cleanup method below, or be creative and create your own.
+
+        remove_soft_deleted_mailbox $dir
+        #compress_soft_deleted_mailbox $dir
+
+done
-- 
GitLab