summaryrefslogtreecommitdiff
path: root/Mailman/MailList.py
diff options
context:
space:
mode:
authorbwarsaw1999-03-29 23:11:46 +0000
committerbwarsaw1999-03-29 23:11:46 +0000
commitea42af85c8ba1b8dceb02ae9189b3fd474f4989f (patch)
tree2a7a98a234d428fd3fa03985c7979ad3d67bbcb4 /Mailman/MailList.py
parent2294974e88331fdbb2754a1fe77f9fa2929e0c0d (diff)
downloadmailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.gz
mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.zst
mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.zip
Sweeping changes to hopefully and finally (for 1.0 at least) make sane
address case matching. These changes require the DATA_FILE_VERSION to be bumped, which should auto-update your config.db files. I sure hope this works correctly! Details of changes: MailList.GetUserSubscribedAddress(): New method. If the address is a member, this returns the case-preserved address the user is subscribed with. If not a member, None is returned. MailList.GetUserCanonicalAddress(): New method. If the address is a member, this returns the lowercased address the user is subscribed with. If not a member, None is returned. MailList.FindUser(): Wrote down, in a big comment, the constraints for the dictionaries self.members, self.digest_members, self.passwords. This wasn't always followed, but now it should be. FindUser() is now also guaranteed to return the lowercased version of the subscribed email address. This wasn't always the case. FindUser() also provides a shortcut for the common case. ApprovedAddMember(): Guarantee that passwords stored in self.passwords are keyed off the lowercased address. Deliverer.MailUserPassword(): Find the user's password using the lowercased version of their address. However, be sure to use their case-preserved address for the recipient of the password email. Digester.SetUserDigest(): Fixed a fairly old bug where a user switching from regular to digest membership (or vice versa) would get their case-preserved address blown away. I don't think there's any way to recover this information, but at least now we properly save it. SecurityManager.ConfirmUserPassword(): Simplified address matching stuff, since we now guarantee that FindUser() will return a lowercased address, and that the passwords dictionary has lowercased keys. FindUser() will return None if the address isn't found, and it also has a built-in shortcut so that the more expensive FindMatchingAddresses() isn't called in the common case. I eliminated the case-insensitive password comparision that Ken rightly questioned in his comment. admin.py: In the list of members, display a member's case-preserved address instead of their lowercased address. Also, obscure the URL in the hyperlink (probably not terribly necessary). handle_opts.py: When the password can't be found (when emailing it), put the address we tried to find in the result message. Makes for better debugging. options.py: Use a better mechanism for finding if the member has a case-preserved address different from their lowercased address.
Diffstat (limited to 'Mailman/MailList.py')
-rw-r--r--Mailman/MailList.py78
1 files changed, 69 insertions, 9 deletions
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 733d67e0e..1e0dd96e6 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -29,6 +29,7 @@ import sys, os, marshal, string, posixfile, time
import re
import Utils
import Errors
+from types import StringType, IntType
from ListAdmin import ListAdmin
from Deliverer import Deliverer
@@ -78,7 +79,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
"""returns a list of the members with username case preserved."""
res = []
for k,v in self.members.items():
- if type(v) is type(""):
+ if type(v) is StringType:
res.append(v)
else:
res.append(k)
@@ -88,7 +89,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
"""returns a list of the members with username case preserved."""
res = []
for k,v in self.digest_members.items():
- if type(v) is type(""):
+ if type(v) is StringType:
res.append(v)
else:
res.append(k)
@@ -99,7 +100,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
If the username has upercase letters in it, then the value
in the members dict is the case preserved address, otherwise,
- the value is 0."""
+ the value is 0.
+ """
if Utils.LCDomain(addr) == string.lower(addr):
if digest:
self.digest_members[addr] = 0
@@ -121,13 +123,38 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
like confirmation requests and passwords must not be sent to the
member addresses - the sublists - but rather to the administrators
of the sublists. This routine picks the right address, considering
- regular member address to be their own administrative addresses."""
+ regular member address to be their own administrative addresses.
+
+ """
if not self.umbrella_list:
return member
else:
acct, host = tuple(string.split(member, '@'))
return "%s%s@%s" % (acct, self.umbrella_member_suffix, host)
+ def GetUserSubscribedAddress(self, member):
+ """Return the member's case preserved address.
+ """
+ member = string.lower(member)
+ cpuser = self.members.get(member)
+ if type(cpuser) == IntType:
+ return member
+ elif type(cpuser) == StringType:
+ return cpuser
+ cpuser = self.digest_members.get(member)
+ if type(cpuser) == IntType:
+ return member
+ elif type(cpuser) == StringType:
+ return cpuser
+ return None
+
+ def GetUserCanonicalAddress(self, member):
+ """Return the member's address lower cased."""
+ cpuser = self.GetUserSubscribedAddress(member)
+ if cpuser is not None:
+ return string.lower(cpuser)
+ return None
+
def GetRequestEmail(self):
return '%s-request@%s' % (self._internal_name, self.host_name)
@@ -154,7 +181,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
return "%s/%s%s/%s" % (prefix, script_name, mm_cfg.CGIEXT,
self._internal_name)
- def GetAbsoluteOptionsURL(self, addr, obscured=0,):
+ def GetAbsoluteOptionsURL(self, addr, obscured=0):
# address could come in case-preserved
addr = string.lower(addr)
options = self.GetAbsoluteScriptURL('options')
@@ -183,13 +210,46 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
del self.user_options[user]
self.Save()
+ # Here are the rules for the three dictionaries self.members,
+ # self.digest_members, and self.passwords:
+ #
+ # The keys of all these dictionaries are the lowercased version of the
+ # address. This makes finding a user very quick: just lowercase the name
+ # you're matching against, and do a has_key() or get() on first
+ # self.members, then if that returns false, self.digest_members
+ #
+ # The value of the key in self.members and self.digest_members is either
+ # the integer 0, meaning the user was subscribed with an all-lowercase
+ # address, or a string which would be the address with the username part
+ # case preserved. Note that for Mailman versions before 1.0b11, the value
+ # could also have been the integer 1. This is a bug that was caused when
+ # a user switched from regular to/from digest membership. If this
+ # happened, you're screwed because there's no way to recover the case
+ # preserved address. :-(
+ #
+ # The keys for self.passwords is also lowercase, although for versions of
+ # Mailman before 1.0b11, this was not always true. 1.0b11 has a hack in
+ # Load() that forces the keys to lowercase. The value for the keys in
+ # self.passwords is, of course the password in plain text.
+
def FindUser(self, email):
+ """Return the lowercased version of the subscribed email address.
+
+ If email is not subscribed, either as a regular member or digest
+ member, None is returned. If they are subscribed, the return value is
+ guaranteed to be lowercased.
+ """
+ # shortcut
+ lcuser = self.GetUserCanonicalAddress(email)
+ if lcuser is not None:
+ return lcuser
matches = Utils.FindMatchingAddresses(email,
self.members,
self.digest_members)
+ # sadly, matches may or may not be case preserved
if not matches or not len(matches):
return None
- return matches[0]
+ return string.lower(matches[0])
def InitTempVars(self, name, lock):
"""Set transient variables of this and inherited classes."""
@@ -684,7 +744,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
fname_last = fname + ".last"
file = aside_new(fname, fname_last, reopen=1)
dict = {}
- for (key, value) in self.__dict__.items():
+ for key, value in self.__dict__.items():
if key[0] <> '_':
dict[key] = value
try:
@@ -710,7 +770,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
dict = marshal.load(file)
except (EOFError, ValueError, TypeError):
raise mm_cfg.MMBadListError, 'Failed to unmarshal config info'
- for (key, value) in dict.items():
+ for key, value in dict.items():
setattr(self, key, value)
file.close()
self._ready = 1
@@ -856,7 +916,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
1 - self.mime_is_default_digest)
self.LogMsg("subscribe", "%s: new%s %s",
self._internal_name, kind, name)
- self.passwords[name] = password
+ self.passwords[string.lower(name)] = password
if ack:
self.SendSubscribeAck(name, password, digest)
if admin_notif: