28
from mailman.app.subscriptions import SubscriptionWorkflow
28
29
from mailman.core.i18n import _
30
from mailman.database.transaction import flush
29
31
from mailman.email.message import UserNotification
30
from mailman.interfaces.address import IEmailValidator
31
from mailman.interfaces.listmanager import IListManager
32
from mailman.interfaces.member import DeliveryMode, MemberRole
33
32
from mailman.interfaces.pending import IPendable, IPendings
34
33
from mailman.interfaces.registrar import ConfirmationNeededEvent, IRegistrar
35
34
from mailman.interfaces.templates import ITemplateLoader
36
from mailman.interfaces.usermanager import IUserManager
37
from mailman.utilities.datetime import now
35
from mailman.interfaces.workflow import IWorkflowStateManager
38
36
from zope.component import getUtility
39
from zope.event import notify
40
37
from zope.interface import implementer
55
52
"""Handle registrations and confirmations for subscriptions."""
57
def register(self, mlist, email, display_name=None, delivery_mode=None):
54
def __init__(self, mlist):
57
def register(self, subscriber=None, *,
58
pre_verified=False, pre_confirmed=False, pre_approved=False):
58
59
"""See `IRegistrar`."""
59
if delivery_mode is None:
60
delivery_mode = DeliveryMode.regular
61
# First, do validation on the email address. If the address is
62
# invalid, it will raise an exception, otherwise it just returns.
63
getUtility(IEmailValidator).validate(email)
64
# Create a pendable for the registration.
65
pendable = PendableRegistration(
66
type=PendableRegistration.PEND_KEY,
68
display_name=display_name,
69
delivery_mode=delivery_mode.name,
70
list_id=mlist.list_id)
71
token = getUtility(IPendings).add(pendable)
72
# We now have everything we need to begin the confirmation dance.
73
# Trigger the event to start the ball rolling, and return the
75
notify(ConfirmationNeededEvent(mlist, pendable, token))
60
workflow = SubscriptionWorkflow(
61
self._mlist, subscriber,
62
pre_verified=pre_verified,
63
pre_confirmed=pre_confirmed,
64
pre_approved=pre_approved)
78
68
def confirm(self, token):
79
69
"""See `IRegistrar`."""
81
pendable = getUtility(IPendings).confirm(token)
85
email = pendable.get('email', missing)
86
display_name = pendable.get('display_name', missing)
87
pended_delivery_mode = pendable.get('delivery_mode', 'regular')
89
delivery_mode = DeliveryMode[pended_delivery_mode]
91
log.error('Invalid pended delivery_mode for {0}: {1}',
92
email, pended_delivery_mode)
93
delivery_mode = DeliveryMode.regular
94
if pendable.get('type') != PendableRegistration.PEND_KEY:
95
# It seems like it would be very difficult to accurately guess
96
# tokens, or brute force an attack on the SHA1 hash, so we'll just
97
# throw the pendable away in that case. It's possible we'll need
98
# to repend the event or adjust the API to handle this case
99
# better, but for now, the simpler the better.
101
# We are going to end up with an IAddress for the verified address
102
# and an IUser linked to this IAddress. See if any of these objects
103
# currently exist in our database.
104
user_manager = getUtility(IUserManager)
105
address = (user_manager.get_address(email)
106
if email is not missing else None)
107
user = (user_manager.get_user(email)
108
if email is not missing else None)
109
# If there is neither an address nor a user matching the confirmed
110
# record, then create the user, which will in turn create the address
111
# and link the two together
113
assert user is None, 'How did we get a user but not an address?'
114
user = user_manager.create_user(email, display_name)
115
# Because the database changes haven't been flushed, we can't use
116
# IUserManager.get_address() to find the IAddress just created
117
# under the hood. Instead, iterate through the IUser's addresses,
118
# of which really there should be only one.
119
for address in user.addresses:
120
if address.email == email:
123
raise AssertionError('Could not find expected IAddress')
125
user = user_manager.create_user()
126
user.display_name = display_name
129
# The IAddress and linked IUser already exist, so all we need to
130
# do is verify the address.
132
address.verified_on = now()
133
# If this registration is tied to a mailing list, subscribe the person
134
# to the list right now. That will generate a SubscriptionEvent,
135
# which can be used to send a welcome message.
136
list_id = pendable.get('list_id')
137
if list_id is not None:
138
mlist = getUtility(IListManager).get_by_list_id(list_id)
139
if mlist is not None:
140
member = mlist.subscribe(address, MemberRole.member)
141
member.preferences.delivery_mode = delivery_mode
70
workflow = SubscriptionWorkflow(self._mlist)
71
workflow.token = token
144
76
def discard(self, token):
145
# Throw the record away.
146
getUtility(IPendings).confirm(token)
77
"""See `IRegistrar`."""
79
getUtility(IPendings).confirm(token)
80
getUtility(IWorkflowStateManager).discard(
81
SubscriptionWorkflow.__name__, token)
156
91
# the Subject header, or they can click on the URL in the body of the
157
92
# message and confirm through the web.
158
93
subject = 'confirm ' + event.token
159
mlist = getUtility(IListManager).get_by_list_id(event.pendable['list_id'])
160
confirm_address = mlist.confirm_address(event.token)
94
confirm_address = event.mlist.confirm_address(event.token)
161
95
# For i18n interpolation.
162
confirm_url = mlist.domain.confirm_url(event.token)
163
email_address = event.pendable['email']
164
domain_name = mlist.domain.mail_host
165
contact_address = mlist.owner_address
96
confirm_url = event.mlist.domain.confirm_url(event.token)
97
email_address = event.email
98
domain_name = event.mlist.domain.mail_host
99
contact_address = event.mlist.owner_address
166
100
# Send a verification email to the address.
167
101
template = getUtility(ITemplateLoader).get(
168
102
'mailman:///{0}/{1}/confirm.txt'.format(
170
mlist.preferred_language.code))
103
event.mlist.fqdn_listname,
104
event.mlist.preferred_language.code))
171
105
text = _(template)
172
106
msg = UserNotification(email_address, confirm_address, subject, text)
107
msg.send(event.mlist)