summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Cgi/admin.py30
-rw-r--r--Mailman/Cgi/handle_opts.py8
-rw-r--r--Mailman/Cgi/options.py5
-rw-r--r--Mailman/Defaults.py.in2
-rw-r--r--Mailman/Digester.py28
-rw-r--r--Mailman/HTMLFormatter.py20
-rw-r--r--Mailman/MailCommandHandler.py11
-rw-r--r--Mailman/MailList.py39
-rw-r--r--Mailman/SecurityManager.py3
-rw-r--r--Mailman/Utils.py48
-rw-r--r--Mailman/versions.py23
11 files changed, 143 insertions, 74 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
index 410edb0e9..0ebf2bc93 100644
--- a/Mailman/Cgi/admin.py
+++ b/Mailman/Cgi/admin.py
@@ -144,12 +144,12 @@ def main():
' (does it have the colon?)<ul> %s </ul>',
line)
- if not lst.digestable and len(lst.digest_members):
+ if not lst.digestable and len(lst.GetDigestMembers()):
AddErrorMessage(doc,
'Warning: you have digest members,'
' but digests are turned off.'
' Those people will not receive mail.')
- if not lst.nondigestable and len(lst.members):
+ if not lst.nondigestable and len(lst.GetMembers()):
AddErrorMessage(doc,
'Warning: you have lst members,'
' but non-digestified mail is turned'
@@ -487,13 +487,7 @@ def FormatMembershipOptions(lst):
user_table.GetCurrentCellIndex(),
bgcolor="#cccccc", colspan=8)
- members = {}
- digests = {}
- for member in lst.members:
- members[member] = 1
- for member in lst.digest_members:
- digests[member] = 1
- all = lst.members + lst.digest_members
+ all = lst.GetMembers() + lst.GetDigestMembers()
if len(all) > lst.admin_member_chunksize:
chunks = Utils.chunkify(all, lst.admin_member_chunksize)
if not cgi_data.has_key("chunk"):
@@ -520,7 +514,7 @@ def FormatMembershipOptions(lst):
cells = [member + "<input type=hidden name=user value=%s>" % (member),
"subscribed " +CheckBox(member + "_subscribed", "on", 1).Format(),
]
- if members.get(member):
+ if lst.members.get(member):
cells.append("digest " + CheckBox(member + "_digest", "off", 0).Format())
else:
cells.append("digest " + CheckBox(member + "_digest", "on", 1).Format())
@@ -772,18 +766,18 @@ def ChangeOptions(lst, category, cgi_info, document):
dirty = 1
continue
if not cgi_info.has_key("%s_digest" % (user)):
- if user in lst.digest_members:
- lst.digest_members.remove(user)
+ if lst.digest_members.has_key(user):
+ del lst.digest_members[user]
dirty = 1
- if user not in lst.members:
- lst.members.append(user)
+ if not lst.members.has_key(user):
+ lst.members[user] = 1
dirty = 1
else:
- if user not in lst.digest_members:
- lst.digest_members.append(user)
+ if not lst.digest_members.has_key(user):
+ lst.digest_members[user] = 1
dirty = 1
- if user in lst.members:
- lst.members.remove(user)
+ if lst.members.has_key(user):
+ del lst.members[user]
dirty = 1
for opt in ("hide", "nomail", "ack", "norcv", "plain"):
diff --git a/Mailman/Cgi/handle_opts.py b/Mailman/Cgi/handle_opts.py
index 0c64716f5..a96c4bbbe 100644
--- a/Mailman/Cgi/handle_opts.py
+++ b/Mailman/Cgi/handle_opts.py
@@ -88,8 +88,12 @@ def main():
error = 0
operation = ""
-
- if string.lower(user) not in list.members + list.digest_members:
+ user = Utils.LCDomain(user)
+ #
+ # XXX shouldn't this check use Utils.FindMatchingAddresses?
+ # -scott
+ if not list.members.has_key(user) \
+ and not list.digest_members.has_key(user):
PrintResults("%s not a member!<p>" % user)
if form.has_key("unsub"):
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index 97f2c1631..768678385 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -63,8 +63,9 @@ def main():
doc.AddItem(htmlformat.Bold("%s: No such list." % list_name ))
print doc.Format()
sys.exit(0)
-
- if Utils.LCDomain(user) not in list.members + list.digest_members:
+ user = Utils.LCDomain(user)
+ if not list.members.has_key(user) \
+ and not list.digest_members.has_key(user):
doc.AddItem(htmlformat.Header(2, "Error"))
doc.AddItem(htmlformat.Bold("%s: No such member %s."
% (list_name, `user`)))
diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in
index 2a3f6dcef..12e7b04a8 100644
--- a/Mailman/Defaults.py.in
+++ b/Mailman/Defaults.py.in
@@ -269,4 +269,4 @@ PRIVATE_ARCHIVE_FILE_DIR = os.path.join(PREFIX, 'archives/private')
VERSION = '@VERSION@'
# Data file version number
-DATA_FILE_VERSION = 10
+DATA_FILE_VERSION = 11
diff --git a/Mailman/Digester.py b/Mailman/Digester.py
index 6b6a3ef6b..a794bd865 100644
--- a/Mailman/Digester.py
+++ b/Mailman/Digester.py
@@ -60,7 +60,7 @@ class Digester:
self.digest_footer = mm_cfg.DEFAULT_DIGEST_FOOTER
# Non-configurable.
- self.digest_members = []
+ self.digest_members = {}
self.next_digest_number = 1
def GetConfigInfo(self):
@@ -104,25 +104,25 @@ class Digester:
addr = self.FindUser(sender)
if not addr:
raise Errors.MMNotAMemberError
- if addr in self.members:
+ if self.members.has_key(addr):
if value == 0:
raise Errors.MMAlreadyUndigested
else:
if not self.digestable:
raise Errors.MMCantDigestError
- self.members.remove(addr)
- self.digest_members.append(addr)
+ del self.members[addr]
+ self.digest_members[addr] = 1
else:
if value == 1:
raise Errors.MMAlreadyDigested
else:
if not self.nondigestable:
raise Errors.MMMustDigestError
- self.digest_members.remove(addr)
- self.members.append(addr)
+ del self.digest_members[addr]
+ self.members[addr] = 1
self.Save()
-# Internal function, don't call this.
+ # Internal function, don't call this.
def SaveForDigest(self, post):
"""Add message to index, and to the digest. If the digest is large
enough when we're done writing, send it out."""
@@ -228,7 +228,8 @@ class Digester:
def HatesMime(x, s=self, v=mm_cfg.DisableMime):
return s.GetUserOption(x, v)
- recipients = filter(DeliveryEnabled, self.digest_members)
+ digestmembers = self.GetDigestMembers()
+ recipients = filter(DeliveryEnabled, digestmembers)
mime_recipients = filter(LikesMime, recipients)
text_recipients = filter(HatesMime, recipients)
self.LogMsg("digest",
@@ -237,8 +238,8 @@ class Digester:
self.LogMsg("digest",
('Fake %d digesters, %d disabled. '
'Active: %d MIMEers, %d non.'),
- len(self.digest_members),
- len(self.digest_members) - len(recipients),
+ len(digestmembers),
+ len(digestmembers) - len(recipients),
len(mime_recipients), len(text_recipients))
def SendDigest(self):
@@ -255,7 +256,8 @@ class Digester:
return not s.GetUserOption(x, v)
def HatesMime(x, s=self, v=mm_cfg.DisableMime):
return s.GetUserOption(x, v)
- recipients = filter(DeliveryEnabled, self.digest_members)
+ digestmembers = self.GetDigestMembers()
+ recipients = filter(DeliveryEnabled, digestmembers)
mime_recipients = filter(LikesMime, recipients)
text_recipients = filter(HatesMime, recipients)
@@ -265,10 +267,10 @@ class Digester:
self.real_name,
self.next_digest_number,
topics_number,
- len(self.digest_members),
+ len(digestmembers),
len(mime_recipients),
len(text_recipients),
- len(self.digest_members) - len(recipients))
+ len(digestmembers) - len(recipients))
if mime_recipients or text_recipients:
d = Digest(self, topics_text, digest_file.read())
diff --git a/Mailman/HTMLFormatter.py b/Mailman/HTMLFormatter.py
index fcd63ad39..2318653d6 100644
--- a/Mailman/HTMLFormatter.py
+++ b/Mailman/HTMLFormatter.py
@@ -69,12 +69,15 @@ class HTMLFormatter:
def NotHidden(x, s=self, v=mm_cfg.ConcealSubscription):
return not s.GetUserOption(x, v)
+
if digest:
- people = filter(NotHidden, self.digest_members)
- num_concealed = len(self.digest_members) - len(people)
+ digestmembers = self.GetDigestMembers()
+ people = filter(NotHidden, digestmembers)
+ num_concealed = len(digestmembers) - len(people)
else:
- people = filter(NotHidden, self.members)
- num_concealed = len(self.members) - len(people)
+ members = self.GetMembers()
+ people = filter(NotHidden, members)
+ num_concealed = len(members) - len(people)
people.sort()
if (num_concealed > 0):
plurality = (((num_concealed > 1) and "s") or "")
@@ -339,6 +342,8 @@ class HTMLFormatter:
# This needs to wait until after the list is inited, so let's build it
# when it's needed only.
def GetStandardReplacements(self):
+ dmember_len = len(self.GetDigestMembers())
+ member_len = len(self.GetMembers())
return {
'<mm-mailman-footer>' : self.GetMailmanFooter(),
'<mm-list-name>' : self.real_name,
@@ -355,10 +360,9 @@ class HTMLFormatter:
self.RestrictedListMessage('current archive',
self.archive_private),
'<mm-digest-users>' : self.FormatUsers(1),
- '<mm-num-reg-users>' : `len(self.members)`,
- '<mm-num-digesters>' : `len(self.digest_members)`,
- '<mm-num-members>' : (`len(self.members)`
- + `len(self.digest_members)`),
+ '<mm-num-reg-users>' : `member_len`,
+ '<mm-num-digesters>' : `dmember_len`,
+ '<mm-num-members>' : (`member_len + dmember_len`),
'<mm-posting-addr>' : '%s' % self.GetListEmail(),
'<mm-request-addr>' : '%s' % self.GetRequestEmail(),
'<mm-owner>' : self.GetAdminEmail(),
diff --git a/Mailman/MailCommandHandler.py b/Mailman/MailCommandHandler.py
index 7702eae82..f78eda456 100644
--- a/Mailman/MailCommandHandler.py
+++ b/Mailman/MailCommandHandler.py
@@ -358,25 +358,26 @@ class MailCommandHandler:
self.AddError("Private list: only members may see list "
"of subscribers.")
return
- if not len(self.digest_members) and not len(self.members):
+ digestmembers = self.GetDigestMembers()
+ members = self.GetMembers()
+ if not len(digestmembers) and not len(members):
self.AddToResponse("NO MEMBERS.")
return
def NotHidden(x, s=self, v=mm_cfg.ConcealSubscription):
return not s.GetUserOption(x, v)
- if len(self.digest_members):
+
+ if len(digestmembers):
self.AddToResponse("")
self.AddToResponse("Digest Members:")
- digestmembers = self.digest_members[:]
digestmembers.sort()
self.AddToResponse(string.join(map(AddTab,
filter(NotHidden,
digestmembers)),
"\n"))
- if len(self.members):
+ if len(members):
self.AddToResponse("Non-Digest Members:")
- members = self.members[:]
members.sort()
self.AddToResponse(string.join(map(AddTab,
filter(NotHidden, members)),
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 1fdebd3de..68d57a807 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -63,8 +63,17 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
for f in self._log_files.values():
f.close()
+ def GetMembers(self):
+ """returns a list of the members."""
+ return self.members.keys()
+
+ def GetDigestMembers(self):
+ """returns a list of digest members."""
+ return self.digest_members.keys()
+
def GetAdminEmail(self):
return '%s-admin@%s' % (self._internal_name, self.host_name)
+
def GetMemberAdminEmail(self, member):
"""Usually the member addr, but modified for umbrella lists.
@@ -114,7 +123,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
def GetUserOption(self, user, option):
if option == mm_cfg.Digests:
- return user in self.digest_members
+ return self.digest_members.has_key(user)
if not self.user_options.has_key(user):
return 0
return not not self.user_options[user] & option
@@ -132,8 +141,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
def FindUser(self, email):
matches = Utils.FindMatchingAddresses(email,
- (self.members
- + self.digest_members))
+ (self.members,
+ self.digest_members))
if not matches or not len(matches):
return None
return matches[0]
@@ -157,7 +166,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
# Must save this state, even though it isn't configurable
self.volume = 1
- self.members = [] # self.digest_members is initted in mm_digest
+ self.members = {} # self.digest_members is initted in mm_digest
self.data_version = mm_cfg.VERSION
self.last_post_time = 0
@@ -767,10 +776,10 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
if self.IsMember(name):
raise Errors.MMAlreadyAMember
if digest:
- self.digest_members.append(name)
+ self.digest_members[name] = 1
kind = " (D)"
else:
- self.members.append(name)
+ self.members[name] = 1
kind = ""
self.SetUserOption(name, mm_cfg.DisableMime,
1 - self.mime_is_default_digest)
@@ -800,8 +809,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
self.IsListInitialized()
# FindMatchingAddresses *should* never return more than 1 address.
# However, should log this, just to make sure.
- aliases = Utils.FindMatchingAddresses(name, self.members +
- self.digest_members)
+ aliases = Utils.FindMatchingAddresses(name, self.members,
+ self.digest_members)
if not len(aliases):
raise Errors.MMNoSuchUserError
@@ -814,14 +823,14 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
if me.user_options.has_key(alias):
del me.user_options[alias]
try:
- me.members.remove(alias)
+ del me.members[alias]
kind = "regular"
- except ValueError:
+ except KeyError:
pass
try:
- me.digest_members.remove(alias)
+ del me.digest_members[alias]
kind = "digest"
- except ValueError:
+ except KeyError:
pass
map(DoActualRemoval, aliases)
@@ -835,8 +844,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
self._internal_name, name, whence)
def IsMember(self, address):
- return len(Utils.FindMatchingAddresses(address, self.members +
- self.digest_members))
+ return len(Utils.FindMatchingAddresses(address, self.members,
+ self.digest_members))
def HasExplicitDest(self, msg):
"""True if list name or any acceptable_alias is included among the
@@ -1038,7 +1047,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
if self.GetUserOption(sender, mm_cfg.AcknowlegePosts):
ack_post = 1
# Deliver the mail.
- recipients = self.members[:]
+ recipients = self.GetMembers()
if dont_send_to_sender:
try:
recipients.remove(sender)
diff --git a/Mailman/SecurityManager.py b/Mailman/SecurityManager.py
index c25b59900..c3fd72adf 100644
--- a/Mailman/SecurityManager.py
+++ b/Mailman/SecurityManager.py
@@ -70,7 +70,8 @@ class SecurityManager:
def ConfirmUserPassword(self, user, pw):
if self.ValidAdminPassword(pw):
return 1
- if not user in self.members and not user in self.digest_members:
+ if not self.members.has_key(user) \
+ and not self.digest_members.has_key(user):
user = self.FindUser(user)
try:
if string.lower(pw) <> string.lower(self.passwords[user]):
diff --git a/Mailman/Utils.py b/Mailman/Utils.py
index 3dc248856..8009cb2d9 100644
--- a/Mailman/Utils.py
+++ b/Mailman/Utils.py
@@ -319,16 +319,48 @@ def AddressesMatch(addr1, addr2):
return 1
-def FindMatchingAddresses(name, array):
- """Given an email address, and a list of email addresses, returns the
- subset of the list that matches the given address. Should sort based
- on exactness of match, just in case."""
- def CallAddressesMatch (x, y=name):
- return AddressesMatch(x,y)
+def GetPossibleMatchingAddrs(name):
+ """returns a sorted list of addresses that could possibly match
+ a given name.
+
+ For Example, given scott@pobox.com, return ['scott@pobox.com'],
+ given scott@blackbox.pobox.com return ['scott@blackbox.pobox.com',
+ 'scott@pobox.com']"""
+
+ name = LCDomain(name)
+ user, domain = ParseEmail(name)
+ res = [name]
+ domain = domain[1:]
+ while len(domain) >= 2:
+ res.append("%s@%s" % (user, string.join(domain, ".")))
+ domain = domain[1:]
+ return res
+
+
+
+def FindMatchingAddresses(name, *dicts):
+ """Given an email address, and any number of dictionaries keyed by
+ email addresses, returns the subset of the list that matches the
+ given address. Should sort based on exactness of match,
+ just in case."""
+
+ if not mm_cfg.SMART_ADDRESS_MATCH:
+ for d in dicts:
+ if d.has_key(LCDomain(name)):
+ return [name]
+ return []
+ #
+ # GetPossibleMatchingAddrs return LCDomain'd values
+ #
+ p_matches = GetPossibleMatchingAddrs(name)
+ res = []
+ for pm in p_matches:
+ for d in dicts:
+ if d.has_key(pm):
+ res.append(pm)
+ return res
- matches = filter(CallAddressesMatch, array)
- return matches
def GetRandomSeed():
chr1 = int(random.random() * 57) + 65
diff --git a/Mailman/versions.py b/Mailman/versions.py
index 80fee9260..fa6c6c28f 100644
--- a/Mailman/versions.py
+++ b/Mailman/versions.py
@@ -100,13 +100,29 @@ def UpdateOldVars(l, stored_state):
else: # make sure everyone gets the behavior the list used to have
if l.posters:
l.member_posting_only = 0
+ #
+ # transfer the list data type for holding members and digest members
+ # to the dict data type starting file format version 11
+ #
+ if type(l.members) is type([]):
+ members = {}
+ for m in l.members:
+ members[m] = 1
+ l.members = members
+ if type(l.digest_members) is type([]):
+ dmembers = {}
+ for dm in l.digest_members:
+ dmembers[dm] = 1
+ l.digest_members = dmembers
+
+
def UpdateOldUsers(l):
"""Transform sense of changed user options."""
if older(l.data_version, "1.0b1.2"):
# Mime-digest bitfield changed from Enable to Disable after 1.0b1.1.
- for m in l.members + l.digest_members:
+ for m in l.GetMembers() + l.GetDigestMembers():
was = l.GetUserOption(m, mm_cfg.DisableMime)
l.SetUserOption(m, mm_cfg.DisableMime, not was)
@@ -165,3 +181,8 @@ def older(version, reference):
# section = int(section)
# got.append(section)
# return got
+
+
+
+
+