diff options
| author | bwarsaw | 1999-03-29 23:11:46 +0000 |
|---|---|---|
| committer | bwarsaw | 1999-03-29 23:11:46 +0000 |
| commit | ea42af85c8ba1b8dceb02ae9189b3fd474f4989f (patch) | |
| tree | 2a7a98a234d428fd3fa03985c7979ad3d67bbcb4 | |
| parent | 2294974e88331fdbb2754a1fe77f9fa2929e0c0d (diff) | |
| download | mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.gz mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.tar.zst mailman-ea42af85c8ba1b8dceb02ae9189b3fd474f4989f.zip | |
| -rw-r--r-- | Mailman/Cgi/admin.py | 5 | ||||
| -rw-r--r-- | Mailman/Cgi/handle_opts.py | 6 | ||||
| -rw-r--r-- | Mailman/Cgi/options.py | 11 | ||||
| -rw-r--r-- | Mailman/Deliverer.py | 11 | ||||
| -rw-r--r-- | Mailman/Digester.py | 5 | ||||
| -rw-r--r-- | Mailman/MailList.py | 78 | ||||
| -rw-r--r-- | Mailman/SecurityManager.py | 26 |
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): |
