# Copyright (C) 2008-2014 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see .
"""Domains."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'Domain',
'DomainManager',
]
from sqlalchemy import Column, Integer, Unicode
from urlparse import urljoin, urlparse
from zope.event import notify
from zope.interface import implementer
from mailman.database.model import Model
from mailman.database.transaction import dbconnection
from mailman.interfaces.domain import (
BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent,
DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager)
from mailman.model.mailinglist import MailingList
@implementer(IDomain)
class Domain(Model):
"""Domains."""
__tablename__ = 'domain'
id = Column(Integer, primary_key=True)
mail_host = Column(Unicode)
base_url = Column(Unicode)
description = Column(Unicode)
contact_address = Column(Unicode)
def __init__(self, mail_host,
description=None,
base_url=None,
contact_address=None):
"""Create and register a domain.
:param mail_host: The host name for the email interface.
:type mail_host: string
:param description: An optional description of the domain.
:type description: string
:param base_url: The optional base url for the domain, including
scheme. If not given, it will be constructed from the
`mail_host` using the http protocol.
:type base_url: string
:param contact_address: The email address to contact a human for this
domain. If not given, postmaster@`mail_host` will be used.
:type contact_address: string
"""
self.mail_host = mail_host
self.base_url = (base_url
if base_url is not None
else 'http://' + mail_host)
self.description = description
self.contact_address = (contact_address
if contact_address is not None
else 'postmaster@' + mail_host)
@property
def url_host(self):
"""See `IDomain`."""
return urlparse(self.base_url).netloc
@property
def scheme(self):
"""See `IDomain`."""
return urlparse(self.base_url).scheme
@property
@dbconnection
def mailing_lists(self, store):
"""See `IDomain`."""
mailing_lists = store.query(MailingList).filter(
MailingList.mail_host == self.mail_host)
for mlist in mailing_lists:
yield mlist
def confirm_url(self, token=''):
"""See `IDomain`."""
return urljoin(self.base_url, 'confirm/' + token)
def __repr__(self):
"""repr(a_domain)"""
if self.description is None:
return ('').format(self)
else:
return ('').format(self)
@implementer(IDomainManager)
class DomainManager:
"""Domain manager."""
@dbconnection
def add(self, store,
mail_host,
description=None,
base_url=None,
contact_address=None):
"""See `IDomainManager`."""
# Be sure the mail_host is not already registered. This is probably
# a constraint that should (also) be maintained in the database.
if self.get(mail_host) is not None:
raise BadDomainSpecificationError(
'Duplicate email host: %s' % mail_host)
notify(DomainCreatingEvent(mail_host))
domain = Domain(mail_host, description, base_url, contact_address)
store.add(domain)
notify(DomainCreatedEvent(domain))
return domain
@dbconnection
def remove(self, store, mail_host):
domain = self[mail_host]
notify(DomainDeletingEvent(domain))
store.delete(domain)
notify(DomainDeletedEvent(mail_host))
return domain
@dbconnection
def get(self, store, mail_host, default=None):
"""See `IDomainManager`."""
domains = store.query(Domain).filter_by(mail_host=mail_host)
if domains.count() < 1:
return default
assert domains.count() == 1, (
'Too many matching domains: %s' % mail_host)
return domains.one()
def __getitem__(self, mail_host):
"""See `IDomainManager`."""
missing = object()
domain = self.get(mail_host, missing)
if domain is missing:
raise KeyError(mail_host)
return domain
@dbconnection
def __len__(self, store):
return store.query(Domain).count()
@dbconnection
def __iter__(self, store):
"""See `IDomainManager`."""
for domain in store.query(Domain).all():
yield domain
@dbconnection
def __contains__(self, store, mail_host):
"""See `IDomainManager`."""
return store.query(Domain).filter_by(mail_host=mail_host).count() > 0