2015-01-19 21:44:57 +01:00
|
|
|
"""
|
|
|
|
This module defines the database models for mail handling.
|
|
|
|
|
|
|
|
"""
|
2014-05-18 20:02:39 +02:00
|
|
|
from django.db import models
|
2023-02-18 22:46:48 +01:00
|
|
|
from django.utils.translation import gettext as _
|
2014-05-24 21:56:30 +02:00
|
|
|
from model_utils.models import TimeStampedModel
|
2023-02-18 22:46:48 +01:00
|
|
|
from passlib.handlers.sha2_crypt import sha512_crypt
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2014-05-25 15:17:08 +02:00
|
|
|
from domains.models import MailDomain
|
2014-05-24 22:50:43 +02:00
|
|
|
from osusers.models import User as OsUser
|
2015-01-19 21:44:57 +01:00
|
|
|
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2014-05-24 14:58:54 +02:00
|
|
|
class ActivateAbleMixin(models.Model):
|
|
|
|
"""
|
|
|
|
Mixin for model classes that can be active or inactive.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
|
2014-05-24 14:58:54 +02:00
|
|
|
active = models.BooleanField(default=True)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
|
|
|
|
2014-12-27 00:24:05 +01:00
|
|
|
class MailboxManager(models.Manager):
|
2015-01-19 21:44:57 +01:00
|
|
|
"""
|
|
|
|
This is the default manager class for :py:class:`Mailbox`.
|
|
|
|
"""
|
2014-12-27 00:24:05 +01:00
|
|
|
|
|
|
|
def get_next_mailbox_name(self, osuser):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
Get the next available mailbox name for the given operating system
|
|
|
|
user.
|
|
|
|
|
|
|
|
:param osuser: a :py:class:`operating system user <osuser.models.User>`
|
|
|
|
instance
|
|
|
|
:return: name of the mailbox
|
|
|
|
:rtype: str
|
|
|
|
|
|
|
|
"""
|
2014-12-27 00:24:05 +01:00
|
|
|
count = 1
|
|
|
|
mailboxformat = "{0}p{1:02d}"
|
|
|
|
mailboxname = mailboxformat.format(osuser.username, count)
|
|
|
|
|
2023-02-18 19:07:33 +01:00
|
|
|
for box in self.values("username").filter(osuser=osuser).order_by("username"):
|
|
|
|
if box["username"] == mailboxname:
|
2014-12-27 00:24:05 +01:00
|
|
|
count += 1
|
|
|
|
mailboxname = mailboxformat.format(osuser.username, count)
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
return mailboxname
|
|
|
|
|
2015-01-25 22:07:54 +01:00
|
|
|
def unused_or_own(self, mailaddress, osuser):
|
|
|
|
"""
|
|
|
|
Returns a queryset that fetches those mailboxes that belong to the
|
|
|
|
given operating system user and are either not used by any mail address
|
|
|
|
or used by the given mailaddress itself.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return self.filter(
|
2023-02-18 19:07:33 +01:00
|
|
|
models.Q(mailaddressmailbox__isnull=True)
|
|
|
|
| models.Q(mailaddressmailbox__mailaddress=mailaddress),
|
|
|
|
active=True,
|
|
|
|
osuser=osuser,
|
2015-01-25 22:07:54 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
def unused(self, osuser):
|
|
|
|
"""
|
|
|
|
Returns a queryset that fetches unused mailboxes belonging to the
|
|
|
|
given operating system user.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return self.filter(
|
|
|
|
mailaddressmailbox__isnull=True,
|
2023-02-18 19:07:33 +01:00
|
|
|
active=True,
|
|
|
|
osuser=osuser,
|
2015-01-25 22:07:54 +01:00
|
|
|
)
|
|
|
|
|
2015-11-28 15:07:34 +01:00
|
|
|
def create_mailbox(self, osuser, password=None, commit=True):
|
|
|
|
"""
|
|
|
|
Create a new mailbox for the given operating system user.
|
|
|
|
|
|
|
|
:param osuser: a :py:class:`osuser.models.OsUser` instance
|
|
|
|
:param password: an optional password
|
|
|
|
:param commit: whether the mailbox should be commited to the database
|
|
|
|
:return: mailbox instance
|
|
|
|
:rtype: :py:class:`managemails.models.Mailbox`
|
|
|
|
|
|
|
|
"""
|
|
|
|
mailbox = self.create(
|
2023-02-18 19:07:33 +01:00
|
|
|
osuser=osuser, username=self.get_next_mailbox_name(osuser)
|
|
|
|
)
|
2015-11-28 15:07:34 +01:00
|
|
|
if password is not None:
|
|
|
|
mailbox.set_password(password)
|
|
|
|
return mailbox
|
|
|
|
|
2014-12-27 00:24:05 +01:00
|
|
|
|
2015-01-24 20:58:20 +01:00
|
|
|
class Mailbox(ActivateAbleMixin, TimeStampedModel):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
This is the model class for a mailbox.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
|
2018-11-19 23:28:40 +01:00
|
|
|
osuser = models.ForeignKey(OsUser, on_delete=models.CASCADE)
|
2014-05-19 22:28:25 +02:00
|
|
|
username = models.CharField(max_length=128, unique=True)
|
|
|
|
password = models.CharField(max_length=255)
|
|
|
|
|
2014-12-27 00:24:05 +01:00
|
|
|
objects = MailboxManager()
|
|
|
|
|
2014-05-19 22:28:25 +02:00
|
|
|
class Meta:
|
2023-02-18 19:07:33 +01:00
|
|
|
ordering = ["osuser", "username"]
|
|
|
|
verbose_name = _("Mailbox")
|
|
|
|
verbose_name_plural = _("Mailboxes")
|
2014-05-19 22:28:25 +02:00
|
|
|
|
|
|
|
def set_password(self, password):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
Set the hashed password for the mailbox.
|
|
|
|
|
|
|
|
:param str password: the clear text password
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
self.password = sha512_crypt.hash(password)
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2015-01-24 20:58:20 +01:00
|
|
|
def get_mailaddresses(self):
|
|
|
|
"""
|
|
|
|
Get a list of mail addresses assigned to this mailbox.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
addrs = [mbadr.mailaddress for mbadr in self.mailaddressmailbox_set.all()]
|
2015-01-24 20:58:20 +01:00
|
|
|
return addrs
|
2023-02-18 19:07:33 +01:00
|
|
|
|
2015-01-24 20:58:20 +01:00
|
|
|
mailaddresses = property(get_mailaddresses)
|
|
|
|
|
2014-05-24 14:58:54 +02:00
|
|
|
def __str__(self):
|
|
|
|
return self.username
|
|
|
|
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2014-05-24 21:56:30 +02:00
|
|
|
class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
This is the model class for a mail address.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
|
|
|
|
localpart = models.CharField(_("local part"), max_length=128)
|
2018-11-19 23:28:40 +01:00
|
|
|
domain = models.ForeignKey(
|
2023-02-18 19:07:33 +01:00
|
|
|
MailDomain, verbose_name=_("domain"), on_delete=models.CASCADE
|
|
|
|
)
|
2014-05-19 22:28:25 +02:00
|
|
|
|
|
|
|
class Meta:
|
2023-02-18 19:07:33 +01:00
|
|
|
ordering = ["domain", "localpart"]
|
|
|
|
unique_together = ("localpart", "domain")
|
|
|
|
verbose_name = _("Mail address")
|
|
|
|
verbose_name_plural = _("Mail addresses")
|
2014-05-19 22:28:25 +02:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "{0}@{1}".format(self.localpart, self.domain)
|
|
|
|
|
2015-01-25 22:07:54 +01:00
|
|
|
def set_mailbox(self, mailbox, commit=True):
|
|
|
|
"""
|
|
|
|
Set the target for this mail address to the given mailbox. This method
|
|
|
|
takes care of removing forwarding addresses or changing existing
|
|
|
|
mailbox relations.
|
|
|
|
|
|
|
|
:param mailbox: :py:class:`Mailbox`
|
|
|
|
:param boolean commit: whether to persist changes or not
|
|
|
|
:return: :py:class:`MailAddressMailbox` instance
|
|
|
|
|
|
|
|
"""
|
|
|
|
if self.pk is not None:
|
|
|
|
if MailAddressMailbox.objects.filter(mailaddress=self).exists():
|
|
|
|
mabox = MailAddressMailbox.objects.get(mailaddress=self)
|
|
|
|
mabox.mailbox = mailbox
|
|
|
|
else:
|
|
|
|
mabox = MailAddressMailbox(mailaddress=self, mailbox=mailbox)
|
|
|
|
if commit:
|
|
|
|
mabox.save()
|
|
|
|
for mafwd in MailAddressForward.objects.filter(mailaddress=self):
|
|
|
|
mafwd.delete()
|
|
|
|
else:
|
|
|
|
if commit:
|
2015-11-28 15:07:34 +01:00
|
|
|
self.save()
|
|
|
|
mabox = MailAddressMailbox(mailaddress=self, mailbox=mailbox)
|
2015-01-25 22:07:54 +01:00
|
|
|
mabox.save()
|
2015-11-28 15:07:34 +01:00
|
|
|
else:
|
|
|
|
mabox = MailAddressMailbox(mailaddress=self, mailbox=mailbox)
|
2015-01-25 22:07:54 +01:00
|
|
|
return mabox
|
|
|
|
|
|
|
|
def set_forward_addresses(self, addresses, commit=True):
|
|
|
|
"""
|
|
|
|
Set the forwarding addresses for this mail address to the given
|
|
|
|
addresses. This method takes care of removing other mail forwards and
|
|
|
|
mailboxes.
|
|
|
|
|
|
|
|
:param addresses: list of email address strings
|
|
|
|
:param boolean commit: whether to persist changes or not
|
|
|
|
:return: list of :py:class:`MailAddressForward` instances
|
|
|
|
|
|
|
|
"""
|
|
|
|
retval = []
|
|
|
|
if self.pk is not None:
|
|
|
|
if MailAddressMailbox.objects.filter(mailaddress=self).exists():
|
|
|
|
mabox = MailAddressMailbox.objects.get(mailaddress=self)
|
|
|
|
mabox.delete()
|
2023-02-18 19:07:33 +01:00
|
|
|
forwards = MailAddressForward.objects.filter(mailaddress=self).all()
|
2015-01-25 22:07:54 +01:00
|
|
|
for item in forwards:
|
2015-11-22 15:03:47 +01:00
|
|
|
if item.target not in addresses:
|
2015-01-25 22:07:54 +01:00
|
|
|
item.delete()
|
|
|
|
else:
|
|
|
|
retval.append(item)
|
|
|
|
for target in addresses:
|
2015-11-22 15:03:47 +01:00
|
|
|
if target not in [item.target for item in forwards]:
|
2015-01-25 22:07:54 +01:00
|
|
|
mafwd = MailAddressForward(mailaddress=self, target=target)
|
|
|
|
if commit:
|
|
|
|
mafwd.save()
|
2015-02-06 11:17:03 +01:00
|
|
|
retval.append(mafwd)
|
2015-01-25 22:07:54 +01:00
|
|
|
else:
|
2015-11-28 15:07:34 +01:00
|
|
|
if commit:
|
|
|
|
self.save()
|
2015-01-25 22:07:54 +01:00
|
|
|
for target in addresses:
|
|
|
|
mafwd = MailAddressForward(mailaddress=self, target=target)
|
|
|
|
if commit:
|
|
|
|
mafwd.save()
|
2015-02-06 11:17:03 +01:00
|
|
|
retval.append(mafwd)
|
2015-01-25 22:07:54 +01:00
|
|
|
return retval
|
|
|
|
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2014-05-24 21:56:30 +02:00
|
|
|
class MailAddressMailbox(TimeStampedModel, models.Model):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
This is the model class to assign a mail address to a mailbox.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
|
2015-01-24 20:58:20 +01:00
|
|
|
mailaddress = models.OneToOneField(
|
2023-02-18 19:07:33 +01:00
|
|
|
MailAddress,
|
|
|
|
verbose_name=_("mailaddress"),
|
|
|
|
primary_key=True,
|
|
|
|
on_delete=models.CASCADE,
|
|
|
|
)
|
2018-11-19 23:28:40 +01:00
|
|
|
mailbox = models.ForeignKey(
|
2023-02-18 19:07:33 +01:00
|
|
|
Mailbox, verbose_name=_("mailbox"), on_delete=models.CASCADE
|
|
|
|
)
|
2014-05-19 22:28:25 +02:00
|
|
|
|
|
|
|
class Meta:
|
2023-02-18 19:07:33 +01:00
|
|
|
unique_together = ("mailaddress", "mailbox")
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2015-01-17 12:25:54 +01:00
|
|
|
def __str__(self):
|
|
|
|
return self.mailbox.username
|
|
|
|
|
2014-05-19 22:28:25 +02:00
|
|
|
|
2014-05-24 21:56:30 +02:00
|
|
|
class MailAddressForward(TimeStampedModel, models.Model):
|
2015-01-19 23:00:01 +01:00
|
|
|
"""
|
|
|
|
This is a model class to map mail addresses to forwarding addresses.
|
|
|
|
|
|
|
|
"""
|
2023-02-18 19:07:33 +01:00
|
|
|
|
2018-11-19 23:28:40 +01:00
|
|
|
mailaddress = models.ForeignKey(MailAddress, on_delete=models.CASCADE)
|
2014-05-19 22:28:25 +02:00
|
|
|
target = models.EmailField(max_length=254)
|
|
|
|
|
|
|
|
class Meta:
|
2023-02-18 19:07:33 +01:00
|
|
|
unique_together = ("mailaddress", "target")
|