summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbwarsaw1999-03-29 23:11:46 +0000
committerbwarsaw1999-03-29 23:11:46 +0000
commitea42af85c8ba1b8dceb02ae9189b3fd474f4989f (patch)
tree2a7a98a234d428fd3fa03985c7979ad3d67bbcb4
parent2294974e88331fdbb2754a1fe77f9fa2929e0c0d (diff)
downloadmailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.gz
mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.zst
mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.zip
-rw-r--r--Mailman/Cgi/admin.py5
-rw-r--r--Mailman/Cgi/handle_opts.py6
-rw-r--r--Mailman/Cgi/options.py11
-rw-r--r--Mailman/Deliverer.py11
-rw-r--r--Mailman/Digester.py5
-rw-r--r--Mailman/MailList.py78
-rw-r--r--Mailman/SecurityManager.py26
7 files changed, 96 insertions, 46 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index 6f549c4f1..fe27c68b5 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -546,8 +546,9 @@ def FormatMembershipOptions(lst):
all.sort()
footer = "<p>"
for member in all:
- mtext = '<a href="%s">%s</a>' % (lst.GetAbsoluteOptionsURL(member),
- member)
+ mtext = '<a href="%s">%s</a>' % (
+ lst.GetAbsoluteOptionsURL(member, obscured=1),
+ lst.GetUserSubscribedAddress(member))
cells = [mtext + "<input type=hidden name=user value=%s>" % (member),
Center(CheckBox(member + "_subscribed", "on", 1).Format())]
for opt in ("hide", "nomail", "ack", "notmetoo"):
diff --git a/Mailman/Cgi/handle_opts.py b/Mailman/Cgi/handle_opts.py
index 4ef585ed9..8e6a50774 100644
--- a/Mailman/Cgi/handle_opts.py
+++ b/Mailman/Cgi/handle_opts.py
@@ -120,9 +120,9 @@ def main():
PrintResults("A reminder of your password "
"has been emailed to you.<p>")
except Errors.MMBadUserError:
- PrintResults("Your password entry has not been found. The"
- " list administrator is being notified.<p>")
-
+ PrintResults("The password entry for `%s' has not "
+ 'been found. The list administrator is being '
+ 'notified.<p>' % user)
elif form.has_key("othersubs"):
if not form.has_key('othersubspw'):
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index 56a680bb7..ace858b6d 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -75,12 +75,13 @@ def main():
print doc.Format()
sys.exit(0)
# find the case preserved email address (the one the user subscribed with)
- cpuser = mlist.members.get(mlist.FindUser(user))
- # Re-obscure the user's address for the page banner if obscure_addresses
- # set.
+ lcuser = mlist.FindUser(user)
+ cpuser = mlist.GetUserSubscribedAddress(lcuser)
+ if lcuser == cpuser:
+ cpuser = None
if mlist.obscure_addresses:
presentable_user = Utils.ObscureEmail(user, for_text=1)
- if type(cpuser) == StringType:
+ if cpuser is not None:
cpuser = Utils.ObscureEmail(cpuser, for_text=1)
else:
presentable_user = user
@@ -136,7 +137,7 @@ def main():
' To Me'))
replacements['<mm-umbrella-notice>'] = (
mlist.FormatUmbrellaNotice(user, "password"))
- if type(cpuser) == StringType:
+ if cpuser is not None:
replacements['<mm-case-preserved-user>'] = '''
You are subscribed to this list with the case-preserved address
<em>%s</em>.''' % cpuser
diff --git a/Mailman/Deliverer.py b/Mailman/Deliverer.py
index 590806b3c..127b8e86a 100644
--- a/Mailman/Deliverer.py
+++ b/Mailman/Deliverer.py
@@ -247,17 +247,16 @@ class Deliverer:
def MailUserPassword(self, user):
listfullname = '%s@%s' % (self.real_name, self.host_name)
ok = 1
- # find the case-preserved version of the user's address
- cpuser = self.members.get(self.FindUser(user))
- if type(cpuser) == type(''):
- user = cpuser
+ # find the lowercased version of the user's address
+ user = self.FindUser(user)
if user and self.passwords.has_key(user):
- recipient = self.GetMemberAdminEmail(user)
+ cpuser = self.GetUserSubscribedAddress(user)
+ recipient = self.GetMemberAdminEmail(cpuser)
subj = '%s mailing list reminder\n' % listfullname
# get the text from the template
text = Utils.maketext(
'userpass.txt',
- {'user' : user,
+ {'user' : cpuser,
'listname' : self.real_name,
'password' : self.passwords[user],
'options_url': self.GetAbsoluteOptionsURL(user),
diff --git a/Mailman/Digester.py b/Mailman/Digester.py
index 306f65150..17e57028f 100644
--- a/Mailman/Digester.py
+++ b/Mailman/Digester.py
@@ -104,6 +104,7 @@ class Digester:
addr = self.FindUser(sender)
if not addr:
raise Errors.MMNotAMemberError
+ cpuser = self.GetUserSubscribedAddress(addr)
if self.members.has_key(addr):
if value == 0:
raise Errors.MMAlreadyUndigested
@@ -111,7 +112,7 @@ class Digester:
if not self.digestable:
raise Errors.MMCantDigestError
del self.members[addr]
- self.digest_members[addr] = 1
+ self.digest_members[addr] = cpuser
else:
if value == 1:
raise Errors.MMAlreadyDigested
@@ -123,7 +124,7 @@ class Digester:
except AttributeError:
self.one_last_digest = {addr: self.digest_members[addr]}
del self.digest_members[addr]
- self.members[addr] = 1
+ self.members[addr] = cpuser
self.Save()
# Internal function, don't call this.
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:
diff --git a/Mailman/SecurityManager.py b/Mailman/SecurityManager.py
index c0a14e534..6a5d0f0a8 100644
--- a/Mailman/SecurityManager.py
+++ b/Mailman/SecurityManager.py
@@ -71,26 +71,14 @@ class SecurityManager:
"""True if password is valid for site, list admin, or specific user."""
if self.ValidAdminPassword(pw):
return 1
-
- # We need to obtain the right letter-case translated version, if any:
- got = self.members.get(string.lower(user), None)
- if got == None:
- got = self.digest_members.get(string.lower(user), None)
- if got == None:
- # Not found in either members dict, resort to expensive FindUser.
- normalized = self.FindUser(user)
- elif type(got) == types.StringType:
- # Found case translated version, use it:
- normalized = got
- else: # Found, no case translation needed:
- normalized = user
-
- try:
- # XXX Huh?? Why eliminate password case info?? klm # 11/23/98.
- if (string.lower(pw) <> string.lower(self.passwords[normalized])):
- raise Errors.MMBadPasswordError
- except KeyError:
+ addr = self.FindUser(user)
+ if addr is None:
+ raise Errors.MMNotAMemberError
+ storedpw = self.passwords.get(addr)
+ if storedpw is None:
raise Errors.MMBadUserError
+ if storedpw <> pw:
+ raise Errors.MMBadPasswordError
return 1
def ChangeUserPassword(self, user, newpw, confirm):