Repository: modoboa/modoboa

Issue:
forward : allow local destination
hi

forwarding an account to a local destination isn't working, even when set as admin:
Permission denied: You can't define a forward to a local destination. Please ask your administrator to create an alias instead.

maybe i miss something?
there is
- account1@foo.bar
- account2@foo.bar

during holyday of account1 the emails should be forwarded to account2@foo.bar (with/without keeping local copy as account1 likes).
how to solve this?

thanks a lot




Discussion:

Resolving patch:
diff --git a/modoboa/admin/forms/forward.py b/modoboa/admin/forms/forward.py
--- a/modoboa/admin/forms/forward.py
+++ b/modoboa/admin/forms/forward.py
@@ -1,15 +1,13 @@
 """Forms related to forwards management."""
 
 from django import forms
-from django.utils.translation import ugettext as _, ugettext_lazy
-
-from modoboa.lib.email_utils import split_mailbox
-from modoboa.lib.exceptions import BadRequest, PermDeniedException
-
-from ..models import Domain
+from django.core import validators
+from django.utils.translation import ugettext_lazy
 
 
 class ForwardForm(forms.Form):
+    """Forward definition form."""
+
     dest = forms.CharField(
         label=ugettext_lazy("Recipient(s)"),
         widget=forms.Textarea(attrs={"class": "form-control"}),
@@ -24,23 +22,13 @@ class ForwardForm(forms.Form):
             "Forward messages and store copies into your local mailbox")
     )
 
-    def get_recipients(self):
+    def clean_dest(self):
+        """Check recipients validity."""
+        rawdata = self.cleaned_data.get("dest", "").strip()
         recipients = []
-        rawdata = self.cleaned_data["dest"].strip()
         if not rawdata:
             return recipients
         for rcpt in rawdata.split(","):
-            local_part, domname = split_mailbox(rcpt)
-            if not local_part or not domname:
-                raise BadRequest("Invalid mailbox syntax for %s" % rcpt)
-            try:
-                Domain.objects.get(name=domname)
-            except Domain.DoesNotExist:
-                recipients += [rcpt]
-            else:
-                raise PermDeniedException(
-                    _("You can't define a forward to a local destination. "
-                      "Please ask your administrator to create an alias "
-                      "instead.")
-                )
+            validators.validate_email(rcpt)
+            recipients += [rcpt]
         return recipients
diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py
--- a/modoboa/admin/models/alias.py
+++ b/modoboa/admin/models/alias.py
@@ -100,8 +100,9 @@ def post_create(self, creator):
                 grant_access_to_object(admin, self)
 
     def set_recipients(self, address_list):
-        """Set recipients for this alias. Special recipients:
+        """Set recipients for this alias.
 
+        Special recipients:
         * local mailbox + extension: r_mailbox will be set to local mailbox
         * alias address == recipient address: valid only to keep local copies
           (when a forward is defined) and to create exceptions when a catchall
diff --git a/modoboa/admin/views/user.py b/modoboa/admin/views/user.py
--- a/modoboa/admin/views/user.py
+++ b/modoboa/admin/views/user.py
@@ -27,11 +27,14 @@ def forward(request, tplname="admin/forward.html"):
                 al = Alias.objects.create(
                     address=mb.full_address, domain=mb.domain,
                     enabled=mb.user.is_active)
-            recipients = form.get_recipients()
+            recipients = form.cleaned_data["dest"]
             if form.cleaned_data["keepcopies"]:
                 recipients.append(mb.full_address)
             al.set_recipients(recipients)
-            al.post_create(request.user)
+            if len(recipients) == 0:
+                al.delete()
+            else:
+                al.post_create(request.user)
             return render_to_json_response(_("Forward updated"))
 
         return render_to_json_response(


Test patch:
diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py
--- a/modoboa/admin/tests/test_user.py
+++ b/modoboa/admin/tests/test_user.py
@@ -20,19 +20,26 @@ def setUp(self):
 
     def test_set_forward(self):
         self.client.logout()
-        self.client.login(username='user@test.com', password='toto')
-        url = reverse('user_forward')
+        self.client.login(username="user@test.com", password="toto")
+        url = reverse("user_forward")
         self.ajax_post(
             url,
-            {'dest': 'user@extdomain.com', 'keepcopies': True}
+            {"dest": "user@extdomain.com", "keepcopies": True}
         )
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         content = json.loads(response.content)
-        self.assertIn('user@extdomain.com', content["content"])
-        self.assertNotIn('user@test.com', content["content"])
-        forward = Alias.objects.get(address='user@test.com', internal=False)
-        sadmin = User.objects.get(username='admin')
+        self.assertIn("user@extdomain.com", content["content"])
+        self.assertNotIn("user@test.com", content["content"])
+        forward = Alias.objects.get(address="user@test.com", internal=False)
+        sadmin = User.objects.get(username="admin")
         self.assertTrue(sadmin.can_access(forward))
-        domadmin = User.objects.get(username='admin@test.com')
+        domadmin = User.objects.get(username="admin@test.com")
         self.assertTrue(domadmin.can_access(forward))
+
+        # Now, empty forward
+        self.ajax_post(url, {"dest": "", "keepcopies": False})
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        content = json.loads(response.content)
+        self.assertNotIn("user@extdomain.com", content["content"])