summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcotton1998-10-10 20:39:57 +0000
committercotton1998-10-10 20:39:57 +0000
commit734e659191d58fc6cdf0651bfc75635a292803c9 (patch)
tree0fde5cef3720ac3ecd92b7006d35ce0837a06472
parent989b9c54b07ad980a3c739031e8cd3d8111ac1d2 (diff)
downloadmailman-734e659191d58fc6cdf0651bfc75635a292803c9.tar.gz
mailman-734e659191d58fc6cdf0651bfc75635a292803c9.tar.zst
mailman-734e659191d58fc6cdf0651bfc75635a292803c9.zip
These changing are a cleanup of the sorry state the subscription policy
was in (which was largely my fault from before). Before: 1) web_subscribe_requires_confirmation was showing in the admin cgi, but not working. 2) all subscribes that required admin approval were going through the confirmation process. 3) the code implementing the confirmation process was distributed between the subscribe cgi and MailCommandHandler, duplicated in places and disrupting the previous interface to list.AddMember. 4) the open_subscribe variable was confusing because it didn't pay any attention to confirmations. Now, things are organized a little differently, but in a much cleaner way. there is one variable that deals with subscription policy, called "subscribe_policy". It's setting determines what happens with both the web based and the mail based subscriptions. there are 4 options: 0 - open subscribe policy 1 - confirmation required 2 - admin approval required 3 - confirmation and then admin approval required there is a site configuration variable in Defaults.py called ALLOW_OPEN_SUBSCRIBE, which determines whether or not an open subscribe policy is an option. If it's not, the admin cgi interface does not present it as an option. I have restored a slightly modified version of the prior-to-confirmations interface for list.AddMember, where all you have to code is: try: list.AddMember(email, digest, password) except Errors.MMBadEmail: except Errors.MMAlreadySubscribed: [ ... all those other good things it used to check ...] except Errors.MMSubscribeNeedsConfirmation: # the confirmation has already been sent. # so just report accordingly to whatever the ap is. In addition, I have moved the code for processing a confirmation request to MailList.py so that it can be used in both a confirmation cgi (which does not yet exist, but will) and the mailcmd script. it's interface is: try: list.ProcessConfirmation(cookie) except Errors.MMBadConfirmation: # the cookie doesn't correspond to anything except Errors.MMNeedApproval: # the list is set to approve+confirm subscribe_policy. A listing of the changes to the files follows: Mailman/Defaults.in: added ALLOW_OPEN_SUBSCRIBE,DEFAULT_SUBSCRIBE_POLICY deleted DEFAULT_OPEN_SUBSCRIBE, changed DATA_FILE_VERSION to 5 Mailman/Errors.py: added MMBadConfirmation and MMSubscribeNeedsConfirmation Mailman/MailCommandHandler.py: moved the confirmation code to MailList.py and use the new (old) list.AddMember interface MailMan/MailList.py: added .ProcessConfirmation(cookie), changed AddMember to fit new (old) interface. deleted config info for open_subscribe and replaced with config info for subscribe_policy that acts according to mm_cfg.ALLOW_OPEN_SUBSCRIBE. Also made list.ApprovedAddMember's "noack" argument just "ack" for simplicities sake and made it default to None instead of 0 so that if the ack variable isn't passed, it sets it to the value of the lists .send_welcome_msg setting. Mailman/versions.py: added handling for new data file format, replacing open_subscribe with a reasonable value for subscribe_policy based on a combination of what open_subscribe is and what mm_cfg.ALLOW_OPEN_SUBSCRIBE is set to. Mailman/Cgi/admin.py: made the cgi handle the output and processing of subscribe_policy based on the setting of mm_cfg.ALLOW_OPEN_SUBSCRIBE. removed erroneous processing of whether or not to send an ack with mass subscription based on new interface to list.ApprovedAddMember (this processing is to be replaced with a good idea from john -- making mass subscribe have it's own option of whether or not to send welcome messages). Mailman/Cgi/subscribe.py: made backgrounds white, and made it use the MailList.AddMember interface described above. Mailman/Makefile.in: looks like this part of that distclean patch from NAGY didn't make it in yet (rm'ing mm_cfg.py as well as Defaults.py) scott
-rw-r--r--Mailman/Cgi/admin.py28
-rw-r--r--Mailman/Cgi/subscribe.py87
-rw-r--r--Mailman/Defaults.py.in24
-rw-r--r--Mailman/Errors.py3
-rw-r--r--Mailman/MailCommandHandler.py134
-rw-r--r--Mailman/MailList.py134
-rw-r--r--Mailman/Makefile.in2
-rw-r--r--Mailman/versions.py9
8 files changed, 236 insertions, 185 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index 3a16d3760..95a671765 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -428,7 +428,18 @@ def GetItemCharacteristics(table_entry):
def GetItemGuiValue(lst, kind, varname, params):
"""Return a representation of an item's settings."""
if kind == mm_cfg.Radio or kind == mm_cfg.Toggle:
- return RadioButtonArray(varname, params, getattr(lst, varname))
+ #
+ # if we are sending returning the option for subscribe
+ # policy and this site doesn't allow open subscribes,
+ # then we have to alter the value of lst.subscribe_policy
+ # as passed to RadioButtonArray in order to compensate
+ # for the fact that there is one fewer option. correspondingly,
+ # we alter the value back in the change options function -scott
+ #
+ if varname == "subscribe_policy" and not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
+ return RadioButtonArray(varname, params, getattr(lst, varname) - 1)
+ else:
+ return RadioButtonArray(varname, params, getattr(lst, varname))
elif (kind == mm_cfg.String or kind == mm_cfg.Email or
kind == mm_cfg.Host or kind == mm_cfg.Number):
return TextBox(varname, getattr(lst, varname), params)
@@ -669,6 +680,15 @@ def ChangeOptions(lst, category, cgi_info, document):
#
if category != 'members' and not cgi_info.has_key("request_login") and\
len(cgi_info.keys()) > 1:
+ if cgi_info.has_key("subscribe_policy"):
+ if not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
+ #
+ # we have to add one to the value because the
+ # page didn't present an open list as an option
+ #
+ page_setting = string.atoi(cgi_info["subscribe_policy"].value)
+ cgi_info["subscribe_policy"].value = str(page_setting + 1)
+
opt_list = GetConfigOptions(lst, category)
for item in opt_list:
if len(item) < 5:
@@ -699,10 +719,6 @@ def ChangeOptions(lst, category, cgi_info, document):
names.remove('')
subscribe_success = []
subscribe_errors = []
- if lst.send_welcome_msg:
- noack = 0
- else:
- noack = 1
for new_name in map(string.strip,names):
digest = 0
if not lst.digestable:
@@ -711,7 +727,7 @@ def ChangeOptions(lst, category, cgi_info, document):
digest = 1
try:
lst.ApprovedAddMember(new_name, (Utils.GetRandomSeed() +
- Utils.GetRandomSeed()), digest, noack)
+ Utils.GetRandomSeed()), digest)
subscribe_success.append(new_name)
except Errors.MMAlreadyAMember:
subscribe_errors.append((new_name, 'Already a member'))
diff --git a/Mailman/Cgi/subscribe.py b/Mailman/Cgi/subscribe.py
index 1366ab6ca..f6a5e68b2 100644
--- a/Mailman/Cgi/subscribe.py
+++ b/Mailman/Cgi/subscribe.py
@@ -34,7 +34,7 @@ def main():
if len(list_info) < 1:
doc.AddItem(htmlformat.Header(2, "Error"))
doc.AddItem(htmlformat.Bold("Invalid options to CGI script."))
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
sys.exit(0)
try:
@@ -42,14 +42,14 @@ def main():
except:
doc.AddItem(htmlformat.Header(2, "Error"))
doc.AddItem(htmlformat.Bold("%s: No such list." % list_name ))
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
sys.exit(0)
if not list._ready:
doc.AddItem(htmlformat.Header(2, "Error"))
doc.AddItem(htmlformat.Bold("%s: No such list." % list_name ))
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
list.Unlock()
sys.exit(0)
@@ -80,7 +80,7 @@ def main():
doc.AddItem(
htmlformat.Bold("You must supply your email address."))
doc.AddItem(list.GetMailmanFooter())
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
list.Unlock()
sys.exit(0)
addr = form['info'].value
@@ -90,7 +90,7 @@ def main():
doc.AddItem(htmlformat.Bold("%s has no subscribed addr <i>%s</i>."
% (list.real_name, addr)))
doc.AddItem(list.GetMailmanFooter())
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
list.Unlock()
sys.exit(0)
list.Unlock()
@@ -150,41 +150,11 @@ def main():
digesting = " digest"
else:
digesting = ""
- cookie = Pending.gencookie()
- Pending.add2pending(email, pw, digest, cookie)
- frmremote = " from %s" % remote
- text = Utils.maketext('verify.txt',
- {"email" : email,
- "listaddr" : list.GetListEmail(),
- "listname" : list.real_name,
- "cookie" : cookie,
- "hostname" : frmremote,
- "requestaddr": list.GetRequestEmail(),
- "remote" : frmremote,
- "listadmin" : list.GetAdminEmail(),
- })
- list.SendTextToUser(
- subject=("%s -- confirmation of subscription -- request %d" %
- (list.real_name, cookie)),
- recipient = email,
- sender = list.GetRequestEmail(),
- text = text,
- add_headers = ["Reply-to: %s" % list.GetRequestEmail(),
- "Errors-To: %s" % list.GetAdminEmail()])
- results = results + ("Confirmation from your email address is "
- "required, to prevent anyone from covertly "
- "subscribing you. Instructions are being "
- "sent to you at %s." % email)
- if remote:
- by = " " + remote
- else:
- by = ""
- list.LogMsg("subscribe", "%s: pending %s %s%s",
- list._internal_name,
- "web",
- email,
- by)
-
+ list.AddMember(email, pw, digest, remote)
+ #
+ # check for all the errors that list.AddMember can throw
+ # options on the web page for this cgi
+ #
except Errors.MMBadEmailError:
results = results + ("Mailman won't accept the given email "
"address as a valid address. (Does it "
@@ -192,22 +162,35 @@ def main():
except Errors.MMListNotReady:
results = results + ("The list is not fully functional, and "
"can not accept subscription requests.<p>")
- #
- # deprecating this, it might be useful if we decide to
- # allow approved based subscriptions without confirmation
- #
- ## except Errors.MMNeedApproval, x:
- ## results = results + ("Subscription was <em>deferred</em> "
- ## "because:<br> %s<p>Your request must "
- ## "be approved by the list admin. "
- ## "You will receive email informing you "
- ## "of the moderator's descision when they "
- ## "get to your request.<p>" % x)
+ except Errors.MMSubscribeNeedsConfirmation:
+ results = results + ("Confirmation from your email address is "
+ "required, to prevent anyone from covertly "
+ "subscribing you. Instructions are being "
+ "sent to you at %s." % email)
+
+ except Errors.MMNeedApproval, x:
+ results = results + ("Subscription was <em>deferred</em> "
+ "because:<br> %s<p>Your request must "
+ "be approved by the list admin. "
+ "You will receive email informing you "
+ "of the moderator's descision when they "
+ "get to your request.<p>" % x)
except Errors.MMHostileAddress:
results = results + ("Your subscription is not allowed because "
"the email address you gave is insecure.<p>")
except Errors.MMAlreadyAMember:
results = results + "You are already subscribed!<p>"
+ #
+ # these shouldn't happen, but if someone's futzing with the cgi
+ # they might -scott
+ #
+ except Errors.MMCantDigestError:
+ results = results + "No one can subscribe to the digest of this list!"
+ except Errors.MMMustDigestError:
+ results = results + "This list only supports digest subscriptions!"
+ else:
+ results = results + "You have been successfully subscribed to %s." % (list.real_name)
+
PrintResults(list, results, doc)
@@ -221,7 +204,7 @@ def PrintResults(list, results, doc):
output = list.ParseTags('subscribe.html', replacements)
doc.AddItem(output)
- print doc.Format()
+ print doc.Format(bgcolor="#ffffff")
list.Unlock()
sys.exit(0)
diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in
index 780d06fc6..45a88301d 100644
--- a/Mailman/Defaults.py.in
+++ b/Mailman/Defaults.py.in
@@ -51,6 +51,7 @@ PRIVATE_ARCHIVE_URL_EXT = '/'
HOME_PAGE = 'index.html'
MAILMAN_OWNER = 'mailman-owner@%s' % DEFAULT_HOST_NAME
+
# System ceiling on number of batches into which deliveries are divided:
MAX_SPAWNS = 40
@@ -113,8 +114,23 @@ from: .*@uplinkpro.com
"""
# Replies to posts inherently directed to list or original sender?
DEFAULT_REPLY_GOES_TO_LIST = 0
-# Admin approval unnecessary for subscribes?
-DEFAULT_OPEN_SUBSCRIBE = 1
+#
+# SUBSCRIBE POLICY
+# 0 - open list (only when ALLOW_OPEN_SUBSCRIBE is set to 1) **
+# 1 - confirmations required for subscribes
+# 2 - admin approval required for subscribes
+# 3 - both confirmations and admin approval required
+#
+# ** please do not choose option 0 if you are not allowing open
+# subscribes (next variable)
+#
+DEFAULT_SUBSCRIBE_POLICY = 1
+#
+# does this site allow completely unchecked subscriptions?
+#
+ALLOW_OPEN_SUBSCRIBE = 0
+
+
# Private_roster == 0: anyone can see, 1: members only, 2: admin only.
DEFAULT_PRIVATE_ROSTER = 0
# When exposing members, make them unrecognizable as email addrs. To
@@ -122,8 +138,6 @@ DEFAULT_PRIVATE_ROSTER = 0
DEFAULT_OBSCURE_ADDRESSES = 1
# Make it 1 when it works.
DEFAULT_MEMBER_POSTING_ONLY = 0
-# 1 for email subscription verification, 2 for admin confirmation:
-DEFAULT_WEB_SUBSCRIBE_REQUIRES_CONFIRMATION = 1
# Digestification Defaults #
@@ -229,4 +243,4 @@ PRIVATE_ARCHIVE_FILE_DIR = os.path.join(PREFIX, 'archives/private')
VERSION = '@VERSION@'
# Data file version number
-DATA_FILE_VERSION = 3
+DATA_FILE_VERSION = 5
diff --git a/Mailman/Errors.py b/Mailman/Errors.py
index 224d4c4bb..5cdfc1b88 100644
--- a/Mailman/Errors.py
+++ b/Mailman/Errors.py
@@ -32,13 +32,14 @@ MMListNotReady = "MMListNotReady"
MMNoSuchUserError = "MMNoSuchUserError"
MMBadPasswordError = "MMBadPasswordError"
MMNeedApproval = "MMNeedApproval"
+MMSubscribeNeedsConfirmation = "MMSubscribeNeedsConfirmation"
+MMBadConfirmation = "MMBadConfirmation"
MMHostileAddress = "MMHostileAddress"
MMAlreadyAMember = "MMAlreadyAMember"
MMPasswordsMustMatch = "MMPasswordsMustMatch"
MMAlreadyDigested = "MMAlreadyDigested"
MMAlreadyUndigested = "MMAlreadyUndigested"
MMBadRequestId = "MMBadRequestId"
-MMWebSubscribeRequiresConfirmation = "MMWebSubscribeRequiresConfirmation"
MODERATED_LIST_MSG = "Moderated list"
IMPLICIT_DEST_MSG = "Implicit destination"
diff --git a/Mailman/MailCommandHandler.py b/Mailman/MailCommandHandler.py
index 4459b36d4..9ef4a5c42 100644
--- a/Mailman/MailCommandHandler.py
+++ b/Mailman/MailCommandHandler.py
@@ -445,45 +445,46 @@ class MailCommandHandler:
password = "%s%s" % (Utils.GetRandomSeed(),
Utils.GetRandomSeed())
if not address:
- pending_addr = mail.GetSender()
+ subscribe_address = string.lower(mail.GetSender())
else:
- pending_addr = address
+ subscribe_address = address
remote = mail.GetSender()
- if pending_addr == self.GetListEmail():
- badremote = "\n\tfrom "
- if remote: badremote = badremote + remote
- else: badremote = badremote + "unidentified sender"
- self.LogMsg("mischief", ("Attempt to self subscribe %s:%s"
- % (pending_addr, badremote)))
- self.AddApprovalMsg("Attempt to subscribe a list to itself!")
- return
- if self.FindUser(pending_addr):
- self.AddError("%s is already a list member." % pending_addr)
- return
- cookie = Pending.gencookie()
- Pending.add2pending(pending_addr, password, digest, cookie)
- if remote == pending_addr:
- remote = ""
+ try:
+ self.AddMember(subscribe_address, password, digest, remote)
+ except Errors.MMSubscribeNeedsConfirmation:
+ #
+ # the confirmation message that's been sent takes place
+ # of the results of the mail command message
+ #
+ self.__NoMailCmdResponse = 1
+ except Errors.MMNeedApproval, admin_email:
+ self.AddToResponse("your subscription request has been forwarded the list "
+ "administrator\nat %s for review.\n" % admin_email)
+ except Errors.MMBadEmailError:
+ self.AddError("Mailman won't accept the given email "
+ "address as a valid address. \n(Does it "
+ "have an @ in it???)")
+ except Errors.MMListNotReady:
+ self.AddError("The list is not fully functional, and "
+ "can not accept subscription requests.")
+ except Errors.MMHostileAddress:
+ self.AddError("Your subscription is not allowed because\n"
+ "the email address you gave is insecure.")
+ except Errors.MMAlreadyAMember:
+ self.AddError("You are already subscribed!")
+ except Errors.MMCantDigestError:
+ self.AddError("No one can subscribe to the digest of this list!")
+ except Errors.MMMustDigestError:
+ self.AddError("This list only supports digest subscriptions!")
else:
- remote = " from " + remote
- text = Utils.maketext(
- 'verify.txt',
- {'email' : pending_addr,
- 'listaddr' : self.GetListEmail(),
- 'listname' : self.real_name,
- 'listadmin' : self.GetAdminEmail(),
- 'cookie' : cookie,
- 'remote' : remote,
- 'requestaddr' : self.GetRequestEmail(),
- })
- self.SendTextToUser(
- subject = "%s -- confirmation of subscription -- request %d" %
- (self.real_name, cookie),
- recipient = pending_addr,
- sender = self.GetRequestEmail(),
- text = text)
- self.__NoMailCmdResponse = 1
- return
+ #
+ # if the list sends a welcome message, we don't need a response
+ # from the mailcommand handler.
+ #
+ if self.send_welcome_msg:
+ self.__NoMailCmdResponse = 1
+ else:
+ self.AddToResponse("Succeeded")
@@ -497,56 +498,27 @@ class MailCommandHandler:
except:
self.AddError("Usage: confirm <confirmation number>\n")
return
- pending = Pending.get_pending()
- if not pending.has_key(cookie):
+ try:
+ self.ProcessConfirmation(cookie)
+ except Errors.MMBadConfirmation:
self.AddError("Invalid confirmation number!\n"
"Please recheck the confirmation number and"
" try again.")
- return
- (email_addr, password, digest, ts) = pending[cookie]
- if self.open_subscribe:
- self.FinishSubscribe(email_addr, password, digest,
- approved=1)
+ except Errors.MMNeedApproval, admin_addr:
+ self.AddToResponse("your request has been forwarded to the list "
+ "administrator for approval")
+
else:
- self.FinishSubscribe(email_addr, password, digest)
- del pending[cookie]
- Pending.set_pending(pending)
-
- def FinishSubscribe(self, addr, password, digest, approved=0):
- try:
- if approved:
- self.ApprovedAddMember(addr, password, digest)
+ #
+ # if the list sends a welcome message, we don't need a response
+ # from the mailcommand handler.
+ #
+ if self.send_welcome_msg:
+ self.__NoMailCmdResponse = 1
else:
- self.AddMember(addr, password, digest)
- self.AddToResponse("Succeeded.")
- except Errors.MMBadEmailError:
- self.AddError("Email address '%s' not accepted by Mailman." %
- addr)
- except Errors.MMMustDigestError:
- self.AddError("List only accepts digest members.")
- except Errors.MMCantDigestError:
- self.AddError("List doesn't accept digest members.")
- except Errors.MMListNotReady:
- self.AddError("List is not functional.")
- except Errors.MMNeedApproval:
- self.AddApprovalMsg("Subscription is pending list admin approval.")
- except Errors.MMHostileAddress:
- self.AddError("Email address '%s' not accepted by Mailman "
- "(insecure address)" % addr)
- except Errors.MMAlreadyAMember:
- self.AddError("%s is already a list member." % addr)
- except:
- self.AddError("An unknown Mailman error occured.")
- self.AddError("Please forward your request to %s" %
- self.GetAdminEmail())
- self.AddError("%s" % sys.exc_type)
- self.LogMsg("error", ("%s:\n\t%s.FinishSubscribe() encountered"
- " unexpected exception:\n\t'%s', '%s'"
- % (__name__,
- self._internal_name,
- str(sys.exc_info()[0]),
- str(sys.exc_info()[1]))))
-
+ self.AddToResponse("Succeeded")
+
+
def AddApprovalMsg(self, cmd):
text = Utils.maketext(
'approve.txt',
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 5f05b9e82..6ef9e9c0c 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -172,12 +172,10 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
self.info = ''
self.welcome_msg = ''
self.goodbye_msg = ''
- self.open_subscribe = mm_cfg.DEFAULT_OPEN_SUBSCRIBE
+ self.subscribe_policy = mm_cfg.DEFAULT_SUBSCRIBE_POLICY
self.private_roster = mm_cfg.DEFAULT_PRIVATE_ROSTER
self.obscure_addresses = mm_cfg.DEFAULT_OBSCURE_ADDRESSES
self.member_posting_only = mm_cfg.DEFAULT_MEMBER_POSTING_ONLY
- self.web_subscribe_requires_confirmation = \
- mm_cfg.DEFAULT_WEB_SUBSCRIBE_REQUIRES_CONFIRMATION
self.host_name = mm_cfg.DEFAULT_HOST_NAME
# Analogs to these are initted in Digester.InitVars
@@ -353,6 +351,34 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
" list. It can be useful for selecting a particular URL"
" of a host that has multiple addresses."),
]
+ if mm_cfg.ALLOW_OPEN_SUBSCRIBE:
+ sub_cfentry = ('subscribe_policy', mm_cfg.Radio,
+ ('none', 'confirm', 'require approval', 'confirm+approval'), 0,
+ "What steps are required for subscription?",
+ "None - no verification steps (<em>Not Recommended </em>)<br>"
+ "confirm (*) - email confirmation step required <br>"
+ "require approval - require list administrator approval for subscriptions <br>"
+ "confirm+approval - both confirm and approve"
+
+ "<p> (*) when someone requests a subscription, mailman sends"
+ "them a notice with a unique subscription request number that "
+ "they must reply to in order to subscribe.<br> This prevents "
+ "list abusers from subscribing people to your list who don't want"
+ "to be subscribed."
+ )
+ else:
+ sub_cfentry = ('subscribe_policy', mm_cfg.Radio,
+ ('confirm', 'require approval', 'confirm+approval'), 1,
+ "What steps are required for subscription?",
+ "confirm (*) - email confirmation required <br>"
+ "require approval - require list administrator approval for subscription"
+ "confirm+approval - both confirm and approve"
+ "<p> (*) when someone requests a subscription, mailman sends"
+ "them a notice with a unique subscription request number that "
+ "they must reply to in order to subscribe.<br> This prevents "
+ "list abusers from subscribing people to your list who don't want"
+ "to be subscribed." )
+
config_info['privacy'] = [
"List access policies, including anti-spam measures,"
@@ -367,25 +393,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
'Advertise this list when people ask what lists are on '
'this machine?'),
- ('open_subscribe', mm_cfg.Radio, ('No', 'Yes'), 0,
- 'Are subscribes done without admins approval (ie, is this'
- ' an <em>open</em> list)?',
-
- "Disabling this option makes the list <em>closed</em>, where"
- " members are admitted only at the discretion of the list"
- " administrator."),
-
- ('web_subscribe_requires_confirmation', mm_cfg.Radio,
- ('None', 'Requestor confirms via email', 'Admin approves'), 0,
- 'What confirmation is required for on-the-web subscribes?',
-
- "This option determines whether web-initiated subscribes"
- " require further confirmation, either from the subscribed"
- " address or from the list administrator. Absence of"
- " <em>any</em> confirmation makes web-based subscription a"
- " tempting opportunity for mischievous subscriptions by third"
- " parties."),
-
+ sub_cfentry,
+
"Membership exposure",
('private_roster', mm_cfg.Radio,
@@ -633,8 +642,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
file.close()
self._ready = 1
if check_version:
- self.CheckValues()
- self.CheckVersion(dict)
+ self.CheckValues()
+ self.CheckVersion(dict)
def LogMsg(self, kind, msg, *args):
"""Append a message to the log file for messages of specified kind."""
@@ -669,7 +678,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
if not self._ready:
raise Errors.MMListNotReady
- def AddMember(self, name, password, digest=0, web_subscribe=0):
+ def AddMember(self, name, password, digest=0, remote=None):
self.IsListInitialized()
# Remove spaces... it's a common thing for people to add...
name = string.join(string.split(string.lower(name)), '')
@@ -688,25 +697,56 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
elif not digest and not self.nondigestable:
raise Errors.MMMustDigestError
- if self.open_subscribe:
- if (web_subscribe and self.web_subscribe_requires_confirmation):
- if self.web_subscribe_requires_confirmation == 1:
- # Requester confirmation required.
- raise Errors.MMWebSubscribeRequiresConfirmation
- else:
- # Admin approval required.
- self.AddRequest('add_member', digest, name, password)
+ if self.subscribe_policy == 0: # no confirmation or approval necessary
+ self.ApprovedAddMember(name, password, digest)
+ elif self.subscribe_policy == 1 or self.subscribe_policy == 3: # confirmation
+ import Pending
+ cookie = Pending.gencookie()
+ Pending.add2pending(name, password, digest, cookie)
+ if remote is not None:
+ by = " " + remote
+ remote = " from %s" % remote
else:
- # No approval required.
- self.ApprovedAddMember(name, password, digest)
- else:
- # Blanket admin approval requred...
+ by = ""
+ remote = ""
+ text = Utils.maketext('verify.txt',
+ {"email" : name,
+ "listaddr" : self.GetListEmail(),
+ "listname" : self.real_name,
+ "cookie" : cookie,
+ "hostname" : remote,
+ "requestaddr": self.GetRequestEmail(),
+ "remote" : remote,
+ "listadmin" : self.GetAdminEmail(),
+ })
+ self.SendTextToUser(
+ subject=("%s -- confirmation of subscription -- request %d" %
+ (self.real_name, cookie)),
+ recipient = name,
+ sender = self.GetRequestEmail(),
+ text = text,
+ add_headers = ["Reply-to: %s" % self.GetRequestEmail(),
+ "Errors-To: %s" % self.GetAdminEmail()])
+ self.LogMsg("subscribe", "%s: pending %s %s",
+ self._internal_name,
+ name,
+ by)
+ raise Errors.MMSubscribeNeedsConfirmation
+ else: # approval needed
self.AddRequest('add_member', digest, name, password)
+ raise Errors.MMNeedApproval, self.GetAdminEmail()
+
- def ApprovedAddMember(self, name, password, digest, noack=0):
+
+ def ApprovedAddMember(self, name, password, digest, ack=None):
# XXX klm: It *might* be nice to leave the case of the name alone,
# but provide a common interface that always returns the
# lower case version for computations.
+ if ack is None:
+ if self.send_welcome_msg:
+ ack = 1
+ else:
+ ack = 0
name = string.lower(name)
if self.IsMember(name):
raise Errors.MMAlreadyAMember
@@ -720,9 +760,25 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
self._internal_name, kind, name)
self.passwords[name] = password
self.Save()
- if not noack:
+ if ack:
self.SendSubscribeAck(name, password, digest)
+
+ def ProcessConfirmation(self, cookie):
+ import Pending
+ pending = Pending.get_pending()
+ if not pending.has_key(cookie):
+ raise Errors.MMBadConfirmation
+ (email_addr, password, digest, ts) = pending[cookie]
+ del pending[cookie]
+ Pending.set_pending(pending)
+ if self.subscribe_policy == 3: # confirm + approve
+ self.AddRequest('add_member', digest, email_addr, password)
+ raise Errors.MMNeedApproval, self.GetAdminEmail()
+ self.ApprovedAddMember(email_addr, password, digest)
+
+
+
def DeleteMember(self, name, whence=None):
self.IsListInitialized()
# FindMatchingAddresses *should* never return more than 1 address.
diff --git a/Mailman/Makefile.in b/Mailman/Makefile.in
index 85075f7f3..5a1f63f29 100644
--- a/Mailman/Makefile.in
+++ b/Mailman/Makefile.in
@@ -83,7 +83,7 @@ clean:
done
distclean:
- -rm Makefile Defaults.py
+ -rm Makefile Defaults.py mm_cfg.py
for d in $(SUBDIRS); \
do \
(cd $$d; $(MAKE) distclean); \
diff --git a/Mailman/versions.py b/Mailman/versions.py
index 94ece1ad6..d030f2e3d 100644
--- a/Mailman/versions.py
+++ b/Mailman/versions.py
@@ -61,6 +61,15 @@ def UpdateOldVars(l, stored_state):
'mimimum_post_count_before_bounce_action')
PreferStored('bad_posters', 'forbidden_posters')
PreferStored('automatically_remove', 'automatic_bounce_action')
+ if hasattr(l, "open_subscribe"):
+ if l.open_subscribe:
+ if mm_cfg.ALLOW_OPEN_SUBSCRIBE:
+ l.subscribe_policy = 0
+ else:
+ l.subscribe_policy = 1
+ else:
+ l.subscribe_policy = 2 # admin approval
+ delattr(l, "open_subscribe")
# - dropped vars:
# for a in ['archive_retain_text_copy',
# 'archive_update_frequency']: