From d7f9f242e3b53c48a69a6c868bbba4ef6ebeeb24 Mon Sep 17 00:00:00 2001
From: Jesse Norell <jesse@kci.net>
Date: Fri, 16 Jul 2021 15:40:37 -0600
Subject: [PATCH] send spamfilter_{users,wblist}_update events when mail_user
 email changes

---
 interface/web/mail/mail_domain_edit.php | 74 +++++++++++++++++++------
 interface/web/mail/mail_user_edit.php   | 40 +++++++++++--
 2 files changed, 93 insertions(+), 21 deletions(-)

diff --git a/interface/web/mail/mail_domain_edit.php b/interface/web/mail/mail_domain_edit.php
index 67a724dbaf..a980aeef50 100644
--- a/interface/web/mail/mail_domain_edit.php
+++ b/interface/web/mail/mail_domain_edit.php
@@ -405,14 +405,48 @@ class page_action extends tform_actions {
 		global $app, $conf;
 
 		$domain = $app->functions->idn_encode($this->dataRecord["domain"]);
+		$old_domain = $app->functions->idn_encode($this->oldDataRecord["domain"]);
 
 		// Spamfilter policy
 		$policy_id = $app->functions->intval($this->dataRecord["policy"]);
+
+		// If domain changes, update spamfilter_users
+		// and fire spamfilter_wblist_update events so rspamd files are rewritten
+		$skip_spamfilter_users_update = false;
+		if(isset($old_domain != $domain) {
+			$tmp_old = $app->db->queryOneRecord("SELECT id,fullname FROM spamfilter_users WHERE email = ?", '@' . $old_domain);
+			if($tmp_old['id'] > 0) {
+				$tmp_new = $app->db->queryOneRecord("SELECT id,fullname FROM spamfilter_users WHERE email = ?", '@' . $domain);
+				if($tmp_new['id'] > 0) {
+					// There is a spamfilter_users for both old and new domain, we'll update old wblist entries
+					$tmp_wblist = $app->db->queryAllRecords("SELECT wblist_id FROM spamfilter_users WHERE rid = ?", $tmp_old['id']);
+					foreach ($tmp_wblist as $tmp) {
+						$app->db->datalogUpdate('spamfilter_wblist', array('rid' => $tmp_new['id']), 'wblist_id', $tmp['wblist_id']);
+					}
+
+					// now delete old spamfilter_users entry
+					$app->db->datalogDelete('spamfilter_users', 'id', $tmp_old['id']);
+				} else {
+					$update_data = array(
+						'email' => '@' . $domain,
+						'policy_id' => $policy_id,
+					);
+					if($tmp_old['fullname'] == '@' . $old_domain) {
+						$update_data['fullname'] = '@' . $domain;
+					}
+					$app->db->datalogUpdate('spamfilter_users', $update_data, 'id', $tmp_old['id']);
+					$skip_spamfilter_users_update = true;
+				}
+			}
+		}
+
 		$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = ?", '@' . $domain);
 		if($policy_id > 0) {
 			if($tmp_user["id"] > 0) {
 				// There is already a record that we will update
-				$app->db->datalogUpdate('spamfilter_users', array("policy_id" => $policy_id), 'id', $tmp_user["id"]);
+				if(!$skip_spamfilter_users_update) {
+					$app->db->datalogUpdate('spamfilter_users', array("policy_id" => $policy_id), 'id', $tmp_user["id"]);
+				}
 			} else {
 				$tmp_domain = $app->db->queryOneRecord("SELECT sys_groupid FROM mail_domain WHERE domain_id = ?", $this->id);
 				// We create a new record
@@ -435,20 +469,23 @@ class page_action extends tform_actions {
 		} else {
 			if($tmp_user["id"] > 0) {
 				// There is already a record but the user shall have no policy, so we delete it
+// fixme: this abandons any spamfilter_wblist entries tied to this spamfilter_users id
+// https://git.ispconfig.org/ispconfig/ispconfig3/-/issues/6201
 				$app->db->datalogDelete('spamfilter_users', 'id', $tmp_user["id"]);
 			}
 		} // endif spamfilter policy
 		//** If the domain name or owner has been changed, change the domain and owner in all mailbox records
-		if($this->oldDataRecord['domain'] != $domain || (isset($this->dataRecord['client_group_id']) && $this->oldDataRecord['sys_groupid'] != $this->dataRecord['client_group_id'])) {
+		if($old_domain != $domain || (isset($this->dataRecord['client_group_id']) && $this->oldDataRecord['sys_groupid'] != $this->dataRecord['client_group_id'])) {
 			$app->uses('getconf');
 			$mail_config = $app->getconf->get_server_config($this->dataRecord["server_id"], 'mail');
 
 			//* Update the mailboxes
-			$mailusers = $app->db->queryAllRecords("SELECT * FROM mail_user WHERE email like ?", '%@' . $this->oldDataRecord['domain']);
+			$mailusers = $app->db->queryAllRecords("SELECT * FROM mail_user WHERE email like ?", '%@' . $old_domain;
 			$sys_groupid = $app->functions->intval((isset($this->dataRecord['client_group_id']))?$this->dataRecord['client_group_id']:$this->oldDataRecord['sys_groupid']);
 			$tmp = $app->db->queryOneRecord("SELECT userid FROM sys_user WHERE default_group = ?", $sys_groupid);
 			$client_user_id = $app->functions->intval(($tmp['userid'] > 0)?$tmp['userid']:1);
 			if(is_array($mailusers)) {
+// fixme: change spamfilter_users and fire wblist events
 				foreach($mailusers as $rec) {
 					// setting Maildir, Homedir, UID and GID
 					$mail_parts = explode("@", $rec['email']);
@@ -460,32 +497,38 @@ class page_action extends tform_actions {
 			}
 
 			//* Update the aliases
-			$forwardings = $app->db->queryAllRecords("SELECT * FROM mail_forwarding WHERE source like ? OR destination like ?", '%@' . $this->oldDataRecord['domain'], '%@' . $this->oldDataRecord['domain']);
+			$forwardings = $app->db->queryAllRecords("SELECT * FROM mail_forwarding WHERE source like ? OR destination like ?", '%@' . $old_domain, '%@' . $old_domain);
 			if(is_array($forwardings)) {
+// fixme: change spamfilter_users and fire wblist events  for aliases/forwards
 				foreach($forwardings as $rec) {
-					$destination = str_replace($this->oldDataRecord['domain'], $domain, $rec['destination']);
-					$source = str_replace($this->oldDataRecord['domain'], $domain, $rec['source']);
+					$destination = str_replace($old_domain, $domain, $rec['destination']);
+					$source = str_replace($old_domain, $domain, $rec['source']);
 					$app->db->datalogUpdate('mail_forwarding', array("source" => $source, "destination" => $destination, "sys_userid" => $client_user_id, "sys_groupid" => $sys_groupid), 'forwarding_id', $rec['forwarding_id']);
 				}
 			}
 
 			//* Update the mailinglist
-			$app->db->query("UPDATE mail_mailinglist SET sys_userid = ?, sys_groupid = ? WHERE domain = ?", $client_user_id, $sys_groupid, $this->oldDataRecord['domain']);
+			$mailinglists = $app->db->queryAllRecords("SELECT * FROM mail_mailinglist WHERE domain = ?", $old_domain);
+			if(is_array($mailinglists)) {
+				foreach($mailinglists as $rec) {
+					$update_data = array(
+						'sys_userid' => $client_user_id,
+						'sys_groupid' => $sys_groupid,
+						'domain' => $domain,
+						'email' => str_replace($old_domain, $domain, $rec['email']),
+					);
+					$app->db->datalogUpdate('mail_mailinglist', $update_data, 'mailinglist_id', $rec['mailinglist_id']);
+				}
+			}
 
 			//* Update fetchmail accounts
-			$fetchmail = $app->db->queryAllRecords("SELECT * FROM mail_get WHERE destination like ?", '%@' . $this->oldDataRecord['domain']);
+			$fetchmail = $app->db->queryAllRecords("SELECT * FROM mail_get WHERE destination like ?", '%@' . $old_domain);
 			if(is_array($fetchmail)) {
 				foreach($fetchmail as $rec) {
-					$destination = str_replace($this->oldDataRecord['domain'], $domain, $rec['destination']);
+					$destination = str_replace($old_domain, $domain, $rec['destination']);
 					$app->db->datalogUpdate('mail_get', array("destination" => $destination, "sys_userid" => $client_user_id, "sys_groupid" => $sys_groupid), 'mailget_id', $rec['mailget_id']);
 				}
 			}
-
-			//* Delete the old spamfilter record
-			$tmp = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = ?", '@' . $this->oldDataRecord["domain"]);
-			$app->db->datalogDelete('spamfilter_users', 'id', $tmp["id"]);
-			unset($tmp);
-
 		} // end if domain name changed
 
 		//* update dns-record when the dkim record was changed
@@ -559,4 +602,3 @@ class page_action extends tform_actions {
 $page = new page_action;
 $page->onLoad();
 
-?>
diff --git a/interface/web/mail/mail_user_edit.php b/interface/web/mail/mail_user_edit.php
index 1dca1db848..e24ca17c8f 100644
--- a/interface/web/mail/mail_user_edit.php
+++ b/interface/web/mail/mail_user_edit.php
@@ -329,11 +329,42 @@ class page_action extends tform_actions {
 
 			// Spamfilter policy
 			$policy_id = $app->functions->intval($this->dataRecord["policy"]);
+			// Handle email change
+			$skip_spamfilter_users_update = false;
+			if(isset($this->dataRecord['email']) && $this->oldDataRecord['email'] != $this->dataRecord['email']) {
+				$tmp_olduser = $app->db->queryOneRecord("SELECT id,fullname FROM spamfilter_users WHERE email = ?", $this->oldDataRecord['email']);
+				if($tmp_olduser["id"] > 0) {
+					$tmp_newuser = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = ?", $this->dataRecord['email']);
+					if($tmp_newuser['id'] > 0) {
+						// There is a spamfilter_users for both old and new email, we'll update old wblist entries
+						$tmp_wblist = $app->db->queryAllRecords("SELECT wblist_id FROM spamfilter_users WHERE rid = ?", $tmp_olduser['id']);
+						foreach ($tmp_wblist as $tmp) {
+							$app->db->datalogUpdate('spamfilter_wblist', array('rid' => $tmp_newuser['id']), 'wblist_id', $tmp['wblist_id']);
+						}
+
+						// now delete old spamfilter_users entry
+						$app->db->datalogDelete('spamfilter_users', 'id', $tmp_olduser['id']);
+					} else {
+						$update_data = array(
+							'email' => $this->dataRecord['email'],
+							'policy_id' => $policy_id,
+						);
+						if($tmp_olduser['fullname'] == $app->functions->idn_decode($this->oldDataRecord["email"])) {
+							$update_data['fullname'] = $app->functions->idn_decode($this->dataRecord["email"]);
+						}
+						$app->db->datalogUpdate('spamfilter_users', $update_data, 'id', $tmp_olduser['id']);
+						$skip_spamfilter_users_update = true;
+					}
+				}
+			}
+
 			$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = ?", $this->dataRecord["email"]);
 			if($policy_id > 0) {
 				if($tmp_user["id"] > 0) {
 					// There is already a record that we will update
-					$app->db->datalogUpdate('spamfilter_users', array("policy_id" => $policy_id), 'id', $tmp_user["id"]);
+					if(!$skip_spamfilter_users_update) {
+						$app->db->datalogUpdate('spamfilter_users', array("policy_id" => $policy_id), 'id', $tmp_user["id"]);
+					}
 				} else {
 					// We create a new record
 					$insert_data = array(
@@ -351,9 +382,11 @@ class page_action extends tform_actions {
 					);
 					$app->db->datalogInsert('spamfilter_users', $insert_data, 'id');
 				}
-			}else {
+			} else {
 				if($tmp_user["id"] > 0) {
 					// There is already a record but the user shall have no policy, so we delete it
+// fixme: don't delete or we abandon users' wblist entries
+// https://git.ispconfig.org/ispconfig/ispconfig3/-/issues/6201
 					$app->db->datalogDelete('spamfilter_users', 'id', $tmp_user["id"]);
 				}
 			} // endif spamfilter policy
@@ -372,8 +405,6 @@ class page_action extends tform_actions {
 
 		//** If the email address has been changed, change it in all aliases too
 		if(isset($this->dataRecord['email']) && $this->oldDataRecord['email'] != $this->dataRecord['email']) {
-			//if($this->oldDataRecord['email'] != $this->dataRecord['email']) {
-
 			//* Update the aliases
 			$forwardings = $app->db->queryAllRecords("SELECT * FROM mail_forwarding WHERE destination = ?", $this->oldDataRecord['email']);
 			if(is_array($forwardings)) {
@@ -401,4 +432,3 @@ class page_action extends tform_actions {
 $app->tform_actions = new page_action;
 $app->tform_actions->onLoad();
 
-?>
-- 
GitLab