1 # Copyright (C) 2007-2009 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Implementation of the IUserRegistrar interface."""
23 'adapt_domain_to_registrar',
29 from pkg_resources
import resource_string
30 from zope
.interface
import implements
32 from mailman
.Message
import UserNotification
33 from mailman
.Utils
import ValidateEmail
34 from mailman
.config
import config
35 from mailman
.i18n
import _
36 from mailman
.interfaces
.domain
import IDomain
37 from mailman
.interfaces
.member
import MemberRole
38 from mailman
.interfaces
.pending
import IPendable
39 from mailman
.interfaces
.registrar
import IRegistrar
43 class PendableRegistration(dict):
45 PEND_KEY
= 'registration'
50 implements(IRegistrar
)
52 def __init__(self
, context
):
53 self
._context
= context
55 def register(self
, address
, real_name
=None, mlist
=None):
56 """See `IUserRegistrar`."""
57 # First, do validation on the email address. If the address is
58 # invalid, it will raise an exception, otherwise it just returns.
59 ValidateEmail(address
)
60 # Create a pendable for the registration.
61 pendable
= PendableRegistration(
62 type=PendableRegistration
.PEND_KEY
,
66 pendable
['list_name'] = mlist
.fqdn_listname
67 token
= config
.db
.pendings
.add(pendable
)
68 # Set up some local variables for translation interpolation.
69 domain
= IDomain(self
._context
)
70 domain_name
= _(domain
.email_host
)
71 contact_address
= domain
.contact_address
72 confirm_url
= domain
.confirm_url(token
)
73 confirm_address
= domain
.confirm_address(token
)
74 email_address
= address
75 # Calculate the message's Subject header. XXX Have to deal with
76 # translating this subject header properly. XXX Must deal with
77 # VERP_CONFIRMATIONS as well.
78 subject
= 'confirm ' + token
79 # Send a verification email to the address.
80 text
= _(resource_string('mailman.templates.en', 'verify.txt'))
81 msg
= UserNotification(address
, confirm_address
, subject
, text
)
85 def confirm(self
, token
):
86 """See `IUserRegistrar`."""
88 pendable
= config
.db
.pendings
.confirm(token
)
92 address
= pendable
.get('address', missing
)
93 real_name
= pendable
.get('real_name', missing
)
94 list_name
= pendable
.get('list_name', missing
)
95 if pendable
.get('type') != PendableRegistration
.PEND_KEY
:
96 # It seems like it would be very difficult to accurately guess
97 # tokens, or brute force an attack on the SHA1 hash, so we'll just
98 # throw the pendable away in that case. It's possible we'll need
99 # to repend the event or adjust the API to handle this case
100 # better, but for now, the simpler the better.
102 # We are going to end up with an IAddress for the verified address
103 # and an IUser linked to this IAddress. See if any of these objects
104 # currently exist in our database.
105 usermgr
= config
.db
.user_manager
106 addr
= (usermgr
.get_address(address
)
107 if address
is not missing
else None)
108 user
= (usermgr
.get_user(address
)
109 if address
is not missing
else None)
110 # If there is neither an address nor a user matching the confirmed
111 # record, then create the user, which will in turn create the address
112 # and link the two together
114 assert user
is None, 'How did we get a user but not an address?'
115 user
= usermgr
.create_user(address
, real_name
)
116 # Because the database changes haven't been flushed, we can't use
117 # IUserManager.get_address() to find the IAddress just created
118 # under the hood. Instead, iterate through the IUser's addresses,
119 # of which really there should be only one.
120 for addr
in user
.addresses
:
121 if addr
.address
== address
:
124 raise AssertionError('Could not find expected IAddress')
126 user
= usermgr
.create_user()
127 user
.real_name
= real_name
130 # The IAddress and linked IUser already exist, so all we need to
131 # do is verify the address.
133 addr
.verified_on
= datetime
.datetime
.now()
134 # If this registration is tied to a mailing list, subscribe the person
135 # to the list right now.
136 list_name
= pendable
.get('list_name')
137 if list_name
is not None:
138 mlist
= config
.db
.list_manager
.get(list_name
)
140 addr
.subscribe(mlist
, MemberRole
.member
)
143 def discard(self
, token
):
144 # Throw the record away.
145 config
.db
.pendings
.confirm(token
)
149 def adapt_domain_to_registrar(iface
, obj
):
150 """Adapt `IDomain` to `IRegistrar`.
152 :param iface: The interface to adapt to.
153 :type iface: `zope.interface.Interface`
154 :param obj: The object being adapted.
156 :return: An `IRegistrar` instance if adaptation succeeded or None if it
159 return (Registrar(obj
)
160 if IDomain
.providedBy(obj
) and iface
is IRegistrar