Repository: frappe/frappe

Issue:
Activity Email not linking to the DocType due to lower casing while sending
## Description of the issue
We have 2 separate email accounts. One for sending and one for receiving. 
For sending, we are using SES and Google Workspace inbox for receiving the emails.

We want to receive the emails in the DocType activity itself. When we started to utilize the activity email as mentioned in the below screenshot. 
<img width="616" alt="image" src="https://github.com/frappe/frappe/assets/26240780/ecb1df04-b0d0-483d-a1ce-57b1c13926ff">
<br>
We noticed that Frappe is lowering the case of the email when sending the email from code.
https://github.com/frappe/frappe/blob/develop/frappe/email/email_body.py#L268
https://github.com/frappe/frappe/blob/develop/frappe/utils/__init__.py#L182

This doesn't impact the receiving but the email, but it impacts the linking of the email with that DocType value.

## Additional info
We are sending the Activity Email Address in the `reply-to` field.

## What we have tried so far
We have tried by manually sending an email to the Activity Email (Maintaing the case) and it did work out perfectly.

## Versions
ERPNext: v15.0.0-dev (version-15-beta)
Frappe Framework: v15.0.0-dev (version-15-beta)
Frappe HR: v15.0.0-dev (version-15-beta)




Discussion:

Resolving patch:
diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py
--- a/frappe/utils/__init__.py
+++ b/frappe/utils/__init__.py
@@ -42,6 +42,10 @@
 PERSON_NAME_PATTERN = re.compile(r"^[\w][\w\'\-]*( \w[\w\'\-]*)*$")
 WHITESPACE_PATTERN = re.compile(r"[\t\n\r]")
 MULTI_EMAIL_STRING_PATTERN = re.compile(r'[,\n](?=(?:[^"]|"[^"]*")*$)')
+EMAIL_MATCH_PATTERN = re.compile(
+	r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
+	re.IGNORECASE,
+)
 
 
 def get_fullname(user=None):
@@ -176,21 +180,10 @@ def _check(e):
 
 		else:
 			email_id = extract_email_id(e)
-			match = (
-				re.match(
-					r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
-					email_id.lower(),
-				)
-				if email_id
-				else None
-			)
+			match = EMAIL_MATCH_PATTERN.match(email_id) if email_id else None
 
 			if not match:
 				_valid = False
-			else:
-				matched = match.group(0)
-				if match:
-					match = matched == email_id.lower()
 
 		if not _valid:
 			if throw:
@@ -201,7 +194,7 @@ def _check(e):
 				)
 			return None
 		else:
-			return matched
+			return email_id
 
 	out = []
 	for e in email_str.split(","):


Test patch:
diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py
--- a/frappe/tests/test_utils.py
+++ b/frappe/tests/test_utils.py
@@ -384,11 +384,12 @@ def test_valid_email(self):
 		# Valid addresses
 		self.assertTrue(validate_email_address("someone@frappe.com"))
 		self.assertTrue(validate_email_address("someone@frappe.com, anyone@frappe.io"))
+		self.assertTrue(validate_email_address("test%201@frappe.com"))
 
 		# Invalid address
 		self.assertFalse(validate_email_address("someone"))
 		self.assertFalse(validate_email_address("someone@----.com"))
-
+		self.assertFalse(validate_email_address("test 1@frappe.com"))
 		self.assertFalse(
 			validate_email_address("test@example.com test2@example.com,undisclosed-recipient")
 		)
@@ -398,6 +399,12 @@ def test_valid_email(self):
 			frappe.InvalidEmailAddressError, validate_email_address, "someone.com", throw=True
 		)
 
+		self.assertEqual(validate_email_address("Some%20One@frappe.com"), "Some%20One@frappe.com")
+		self.assertEqual(
+			validate_email_address("erp+Job%20Applicant=JA00004@frappe.com"),
+			"erp+Job%20Applicant=JA00004@frappe.com",
+		)
+
 	def test_valid_phone(self):
 		valid_phones = ["+91 1234567890", ""]