28
from mailman.app.subscriptions import SubscriptionWorkflow
28
29
from mailman.core.i18n import _
29
30
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
31
from mailman.interfaces.pending import IPendable, IPendings
34
32
from mailman.interfaces.registrar import ConfirmationNeededEvent, IRegistrar
35
33
from mailman.interfaces.templates import ITemplateLoader
36
from mailman.interfaces.usermanager import IUserManager
37
from mailman.utilities.datetime import now
38
34
from zope.component import getUtility
39
from zope.event import notify
40
35
from zope.interface import implementer
55
50
"""Handle registrations and confirmations for subscriptions."""
57
def register(self, mlist, email, display_name=None, delivery_mode=None):
52
def __init__(self, mlist):
55
def register(self, subscriber=None, *,
56
pre_verified=False, pre_confirmed=False, pre_approved=False):
58
57
"""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))
58
workflow = SubscriptionWorkflow(
59
self._mlist, subscriber,
60
pre_verified=pre_verified,
61
pre_confirmed=pre_confirmed,
62
pre_approved=pre_approved)
78
66
def confirm(self, token):
79
67
"""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
68
workflow = SubscriptionWorkflow(self._mlist)
69
workflow.token = token
144
74
def discard(self, token):
145
75
# Throw the record away.