summaryrefslogtreecommitdiff
path: root/Mailman/app
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/app')
-rwxr-xr-xMailman/app/__init__.py0
-rw-r--r--Mailman/app/registrar.py149
2 files changed, 149 insertions, 0 deletions
diff --git a/Mailman/app/__init__.py b/Mailman/app/__init__.py
new file mode 100755
index 000000000..e69de29bb
--- /dev/null
+++ b/Mailman/app/__init__.py
diff --git a/Mailman/app/registrar.py b/Mailman/app/registrar.py
new file mode 100644
index 000000000..e44394134
--- /dev/null
+++ b/Mailman/app/registrar.py
@@ -0,0 +1,149 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Implementation of the IUserRegistrar interface."""
+
+__metaclass__ = type
+__all__ = [
+ 'Registrar',
+ ]
+
+
+import datetime
+import pkg_resources
+
+from zope.interface import implements
+
+from Mailman.Message import UserNotification
+from Mailman.Utils import ValidateEmail
+from Mailman.configuration import config
+from Mailman.i18n import _
+from Mailman.interfaces import IDomain, IPendable, IPending, IRegistrar
+
+__i18n_templates__ = True
+
+
+
+class PendableRegistration(dict):
+ implements(IPendable)
+ PEND_KEY = 'registration'
+
+
+
+class Registrar:
+ implements(IRegistrar)
+
+ def __init__(self, context):
+ self._context = context
+
+ def register(self, address, real_name=None):
+ """See `IUserRegistrar`."""
+ # First, do validation on the email address. If the address is
+ # invalid, it will raise an exception, otherwise it just returns.
+ ValidateEmail(address)
+ # Check to see if there is already a verified IAddress in the database
+ # matching this address. If so, there's nothing to do.
+ usermgr = config.user_manager
+ addr = usermgr.get_address(address)
+ if addr and addr.verified_on:
+ # Before returning, see if this address is linked to a user. If
+ # not, create one and link it now since no future verification
+ # will be done.
+ user = usermgr.get_user(address)
+ if user is None:
+ user = usermgr.create_user()
+ user.real_name = (real_name if real_name else addr.real_name)
+ user.link(addr)
+ return None
+ # Calculate the token for this confirmation record.
+ pendable = PendableRegistration(type=PendableRegistration.PEND_KEY,
+ address=address,
+ real_name=real_name)
+ pendingdb = IPending(config.db)
+ token = pendingdb.add(pendable)
+ # Set up some local variables for translation interpolation.
+ domain = IDomain(self._context)
+ domain_name = _(domain.domain_name)
+ contact_address = domain.contact_address
+ confirm_url = domain.confirm_url(token)
+ confirm_address = domain.confirm_address(token)
+ email_address = address
+ # Calculate the message's Subject header. XXX Have to deal with
+ # translating this subject header properly. XXX Must deal with
+ # VERP_CONFIRMATIONS as well.
+ subject = 'confirm ' + token
+ # Send a verification email to the address.
+ text = _(pkg_resources.resource_string(
+ 'Mailman.templates.en', 'verify.txt'))
+ msg = UserNotification(address, confirm_address, subject, text)
+ msg.send(mlist=None)
+ return token
+
+ def confirm(self, token):
+ """See `IUserRegistrar`."""
+ # For convenience
+ pendingdb = IPending(config.db)
+ pendable = pendingdb.confirm(token)
+ if pendable is None:
+ return False
+ missing = object()
+ address = pendable.get('address', missing)
+ real_name = pendable.get('real_name', missing)
+ if (pendable.get('type') <> PendableRegistration.PEND_KEY or
+ address is missing or real_name is missing):
+ # It seems like it would be very difficult to accurately guess
+ # tokens, or brute force an attack on the SHA1 hash, so we'll just
+ # throw the pendable away in that case. It's possible we'll need
+ # to repend the event or adjust the API to handle this case
+ # better, but for now, the simpler the better.
+ return False
+ # We are going to end up with an IAddress for the verified address
+ # and an IUser linked to this IAddress. See if any of these objects
+ # currently exist in our database.
+ usermgr = config.user_manager
+ addr = usermgr.get_address(address)
+ user = usermgr.get_user(address)
+ # If there is neither an address nor a user matching the confirmed
+ # record, then create the user, which will in turn create the address
+ # and link the two together
+ if addr is None:
+ assert user is None, 'How did we get a user but not an address?'
+ user = usermgr.create_user(address, real_name)
+ # Because the database changes haven't been flushed, we can't use
+ # IUserManager.get_address() to find the IAddress just created
+ # under the hood. Instead, iterate through the IUser's addresses,
+ # of which really there should be only one.
+ for addr in user.addresses:
+ if addr.address == address:
+ break
+ else:
+ raise AssertionError('Could not find expected IAddress')
+ elif user is None:
+ user = usermgr.create_user()
+ user.real_name = real_name
+ user.link(addr)
+ else:
+ # The IAddress and linked IUser already exist, so all we need to
+ # do is verify the address.
+ pass
+ addr.verified_on = datetime.datetime.now()
+ return True
+
+ def discard(self, token):
+ pendingdb = IPending(config.db)
+ # Throw the record away.
+ pendingdb.confirm(token)