aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Mailman/Archiver/HyperArch.py6
-rw-r--r--Mailman/Cgi/confirm.py7
-rw-r--r--Mailman/Cgi/options.py32
-rw-r--r--Mailman/Commands/cmd_confirm.py8
-rwxr-xr-xMailman/Defaults.py.in4
-rw-r--r--Mailman/Gui/Privacy.py11
-rw-r--r--Mailman/Gui/Topics.py10
-rw-r--r--Mailman/HTMLFormatter.py5
-rw-r--r--Mailman/Handlers/Moderate.py9
-rw-r--r--Mailman/Handlers/Tagger.py6
-rwxr-xr-xMailman/MailList.py81
-rw-r--r--Mailman/Utils.py58
-rw-r--r--Mailman/Version.py4
-rwxr-xr-xMailman/versions.py13
14 files changed, 199 insertions, 55 deletions
diff --git a/Mailman/Archiver/HyperArch.py b/Mailman/Archiver/HyperArch.py
index 9b1df75a..0c0e3356 100644
--- a/Mailman/Archiver/HyperArch.py
+++ b/Mailman/Archiver/HyperArch.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -509,7 +509,7 @@ class Article(pipermail.Article):
subject = self._get_subject_enc(self.prev)
prev = ('<LINK REL="Previous" HREF="%s">'
% (url_quote(self.prev.filename)))
- prev_wsubj = ('<LI>' + _('Previous message:') +
+ prev_wsubj = ('<LI>' + _('Previous message (by thread):') +
' <A HREF="%s">%s\n</A></li>'
% (url_quote(self.prev.filename),
self.quote(subject)))
@@ -531,7 +531,7 @@ class Article(pipermail.Article):
subject = self._get_subject_enc(self.next)
next = ('<LINK REL="Next" HREF="%s">'
% (url_quote(self.next.filename)))
- next_wsubj = ('<LI>' + _('Next message:') +
+ next_wsubj = ('<LI>' + _('Next message (by thread):') +
' <A HREF="%s">%s\n</A></li>'
% (url_quote(self.next.filename),
self.quote(subject)))
diff --git a/Mailman/Cgi/confirm.py b/Mailman/Cgi/confirm.py
index bb529318..97297e10 100644
--- a/Mailman/Cgi/confirm.py
+++ b/Mailman/Cgi/confirm.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -99,8 +99,9 @@ def main():
%(safecookie)s.
<p>Note that confirmation strings expire approximately
- %(days)s days after the initial subscription request. If your
- confirmation has expired, please try to re-submit your subscription.
+ %(days)s days after the initial request. They also expire if the
+ request has already been handled in some way. If your confirmation
+ has expired, please try to re-submit your request.
Otherwise, <a href="%(confirmurl)s">re-enter</a> your confirmation
string.''')
diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py
index 69ac52a9..74f186d7 100644
--- a/Mailman/Cgi/options.py
+++ b/Mailman/Cgi/options.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -33,6 +33,7 @@ from Mailman import i18n
from Mailman.htmlformat import *
from Mailman.Logging.Syslog import syslog
+OR = '|'
SLASH = '/'
SETLANGUAGE = -1
@@ -176,6 +177,9 @@ def main():
return
# Are we processing an unsubscription request from the login screen?
+ msgc = _('If you are a list member, a confirmation email has been sent.')
+ msga = _("""If you are a list member, your unsubscription request has been
+ forwarded to the list administrator for approval.""")
if cgidata.has_key('login-unsub'):
# Because they can't supply a password for unsubscribing, we'll need
# to do the confirmation dance.
@@ -187,14 +191,11 @@ def main():
# be held. Otherwise, send a confirmation.
if mlist.unsubscribe_policy:
mlist.HoldUnsubscription(user)
- doc.addError(_("""Your unsubscription request has been
- forwarded to the list administrator for approval."""),
- tag='')
+ doc.addError(msga, tag='')
else:
ip = os.environ.get('REMOTE_ADDR')
mlist.ConfirmUnsubscription(user, userlang, remote=ip)
- doc.addError(_('The confirmation email has been sent.'),
- tag='')
+ doc.addError(msgc, tag='')
mlist.Save()
finally:
mlist.Unlock()
@@ -207,19 +208,21 @@ def main():
syslog('mischief',
'Unsub attempt of non-member w/ private rosters: %s',
user)
- doc.addError(_('The confirmation email has been sent.'),
- tag='')
+ if mlist.unsubscribe_policy:
+ doc.addError(msga, tag='')
+ else:
+ doc.addError(msgc, tag='')
loginpage(mlist, doc, user, language)
print doc.Format()
return
# Are we processing a password reminder from the login screen?
+ msg = _("""If you are a list member,
+ your password has been emailed to you.""")
if cgidata.has_key('login-remind'):
if mlist.isMember(user):
mlist.MailUserPassword(user)
- doc.addError(
- _('A reminder of your password has been emailed to you.'),
- tag='')
+ doc.addError(msg, tag='')
else:
# Not a member
if mlist.private_roster == 0:
@@ -229,9 +232,7 @@ def main():
syslog('mischief',
'Reminder attempt of non-member w/ private rosters: %s',
user)
- doc.addError(
- _('A reminder of your password has been emailed to you.'),
- tag='')
+ doc.addError(msg, tag='')
loginpage(mlist, doc, user, language)
print doc.Format()
return
@@ -1068,7 +1069,8 @@ def topic_details(mlist, doc, user, cpuser, userlang, varhelp):
table.AddRow([Bold(Label(_('Name:'))),
Utils.websafe(name)])
table.AddRow([Bold(Label(_('Pattern (as regexp):'))),
- '<pre>' + Utils.websafe(pattern) + '</pre>'])
+ '<pre>' + Utils.websafe(OR.join(pattern.splitlines()))
+ + '</pre>'])
table.AddRow([Bold(Label(_('Description:'))),
Utils.websafe(description)])
# Make colors look nice
diff --git a/Mailman/Commands/cmd_confirm.py b/Mailman/Commands/cmd_confirm.py
index a3accf64..379d23c2 100644
--- a/Mailman/Commands/cmd_confirm.py
+++ b/Mailman/Commands/cmd_confirm.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2011 by the Free Software Foundation, Inc.
+# Copyright (C) 2002-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -49,9 +49,9 @@ def process(res, args):
days = int(mm_cfg.PENDING_REQUEST_LIFE / mm_cfg.days(1) + 0.5)
res.results.append(_("""\
Invalid confirmation string. Note that confirmation strings expire
-approximately %(days)s days after the initial subscription request. If your
-confirmation has expired, please try to re-submit your original request or
-message."""))
+approximately %(days)s days after the initial request. They also expire if
+the request has already been handled in some way. If your confirmation has
+expired, please try to re-submit your original request or message."""))
except Errors.MMNeedApproval:
res.results.append(_("""\
Your request has been forwarded to the list moderator for approval."""))
diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in
index bec3e441..8a5e6b0e 100755
--- a/Mailman/Defaults.py.in
+++ b/Mailman/Defaults.py.in
@@ -1188,6 +1188,10 @@ DEFAULT_SUBSCRIBE_POLICY = 1
# Does this site allow completely unchecked subscriptions?
ALLOW_OPEN_SUBSCRIBE = No
+# This is the default list of addresses and regular expressions (beginning
+# with ^) that are exempt from approval if SUBSCRIBE_POLICY is 2 or 3.
+DEFAULT_SUBSCRIBE_AUTO_APPROVAL = []
+
# The default policy for unsubscriptions. 0 (unmoderated unsubscribes) is
# highly recommended!
# 0 - unmoderated unsubscribes
diff --git a/Mailman/Gui/Privacy.py b/Mailman/Gui/Privacy.py
index 7f1e12f3..3c32bf50 100644
--- a/Mailman/Gui/Privacy.py
+++ b/Mailman/Gui/Privacy.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -113,6 +113,15 @@ class Privacy(GUIBase):
sub_cfentry,
+ ('subscribe_auto_approval', mm_cfg.EmailListEx, (10, WIDTH), 1,
+ _("""List of addresses (or regexps) whose subscriptions do not
+ require approval."""),
+
+ _("""When subscription requires approval, addresses in this list
+ are allowed to subscribe without administrator approval. Add
+ addresses one per line. You may begin a line with a ^ character
+ to designate a (case insensitive) regular expression match.""")),
+
('unsubscribe_policy', mm_cfg.Radio, (_('No'), _('Yes')), 0,
_("""Is the list moderator's approval required for unsubscription
requests? (<em>No</em> is recommended)"""),
diff --git a/Mailman/Gui/Topics.py b/Mailman/Gui/Topics.py
index 96f9b421..ec60dbda 100644
--- a/Mailman/Gui/Topics.py
+++ b/Mailman/Gui/Topics.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2009 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -29,6 +29,8 @@ except NameError:
True = 1
False = 0
+OR = '|'
+
class Topics(GUIBase):
@@ -126,10 +128,10 @@ class Topics(GUIBase):
# Make sure the pattern was a legal regular expression
name = Utils.websafe(name)
try:
- # Tagger compiles in verbose mode so we do too.
- re.compile(pattern, re.VERBOSE)
+ orpattern = OR.join(pattern.splitlines())
+ re.compile(orpattern)
except (re.error, TypeError):
- safepattern = Utils.websafe(pattern)
+ safepattern = Utils.websafe(orpattern)
doc.addError(_("""The topic pattern '%(safepattern)s' is not a
legal regular expression. It will be discarded."""))
continue
diff --git a/Mailman/HTMLFormatter.py b/Mailman/HTMLFormatter.py
index dad51e74..df22e5f2 100644
--- a/Mailman/HTMLFormatter.py
+++ b/Mailman/HTMLFormatter.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -90,6 +90,9 @@ class HTMLFormatter:
showing = Utils.ObscureEmail(person, for_text=1)
else:
showing = person
+ realname = Utils.uncanonstr(self.getMemberName(person), lang)
+ if realname:
+ showing += " (%s)" % Utils.websafe(realname)
got = Link(url, showing)
if self.getDeliveryStatus(person) <> MemberAdaptor.ENABLED:
got = Italic('(', got, ')')
diff --git a/Mailman/Handlers/Moderate.py b/Mailman/Handlers/Moderate.py
index 4400d086..225ee37f 100644
--- a/Mailman/Handlers/Moderate.py
+++ b/Mailman/Handlers/Moderate.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -162,9 +162,10 @@ def do_reject(mlist):
Utils.wrap(_(mlist.nonmember_rejection_notice))
else:
raise Errors.RejectMessage, Utils.wrap(_("""\
-You are not allowed to post to this mailing list, and your message has been
-automatically rejected. If you think that your messages are being rejected in
-error, contact the mailing list owner at %(listowner)s."""))
+Your message has been rejected, probably because you are not subscribed to the
+mailing list and the list's policy is to prohibit non-members from posting to
+it. If you think that your messages are being rejected in error, contact the
+mailing list owner at %(listowner)s."""))
diff --git a/Mailman/Handlers/Tagger.py b/Mailman/Handlers/Tagger.py
index cb90bfc4..ed9a7e71 100644
--- a/Mailman/Handlers/Tagger.py
+++ b/Mailman/Handlers/Tagger.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2013 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -29,6 +29,7 @@ from Mailman import Utils
from Mailman.Logging.Syslog import syslog
from Mailman.Handlers.CookHeaders import change_header
+OR = '|'
CRNL = '\r\n'
EMPTYSTRING = ''
NLTAB = '\n\t'
@@ -63,7 +64,8 @@ def process(mlist, msg, msgdata):
# added to the specific topics bucket.
hits = {}
for name, pattern, desc, emptyflag in mlist.topics:
- cre = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
+ pattern = OR.join(pattern.splitlines())
+ cre = re.compile(pattern, re.IGNORECASE)
for line in matchlines:
if cre.search(line):
hits[name] = 1
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 0ebf8ab8..dca8c8f5 100755
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -74,6 +74,7 @@ from Mailman.Logging.Syslog import syslog
_ = i18n._
EMPTYSTRING = ''
+OR = '|'
try:
True, False
@@ -356,6 +357,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
self.welcome_msg = ''
self.goodbye_msg = ''
self.subscribe_policy = mm_cfg.DEFAULT_SUBSCRIBE_POLICY
+ self.subscribe_auto_approval = mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL
self.unsubscribe_policy = mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY
self.private_roster = mm_cfg.DEFAULT_PRIVATE_ROSTER
self.obscure_addresses = mm_cfg.DEFAULT_OBSCURE_ADDRESSES
@@ -773,10 +775,11 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
goodtopics = []
for name, pattern, desc, emptyflag in self.topics:
try:
- re.compile(pattern)
+ orpattern = OR.join(pattern.splitlines())
+ re.compile(orpattern)
except (re.error, TypeError):
syslog('error', 'Bad topic pattern "%s" for list: %s',
- pattern, self.internal_name())
+ orpattern, self.internal_name())
else:
goodtopics.append((name, pattern, desc, emptyflag))
self.topics = goodtopics
@@ -941,6 +944,9 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
syslog('subscribe', '%s: pending %s %s',
self.internal_name(), who, by)
raise Errors.MMSubscribeNeedsConfirmation
+ elif self.HasAutoApprovedSender(email):
+ # no approval necessary:
+ self.ApprovedAddMember(userdesc)
else:
# Subscription approval is required. Add this entry to the admin
# requests database. BAW: this should probably take a userdesc
@@ -1164,12 +1170,14 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
# CP address of a member, then if the old address yields a different
# CP address, we can simply remove the old address, otherwise we can
# do nothing.
+ cpoldaddr = self.getMemberCPAddress(oldaddr)
if self.isMember(newaddr) and (self.getMemberCPAddress(newaddr) ==
newaddr):
- if self.getMemberCPAddress(oldaddr) <> newaddr:
+ if cpoldaddr <> newaddr:
self.removeMember(oldaddr)
else:
self.changeMemberAddress(oldaddr, newaddr)
+ self.log_and_notify_admin(cpoldaddr, newaddr)
# If globally is true, then we also include every list for which
# oldaddr is a member.
if not globally:
@@ -1189,16 +1197,46 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
mlist.Lock()
try:
# Same logic as above, re newaddr is already a member
+ cpoldaddr = mlist.getMemberCPAddress(oldaddr)
if mlist.isMember(newaddr) and (
mlist.getMemberCPAddress(newaddr) == newaddr):
- if mlist.getMemberCPAddress(oldaddr) <> newaddr:
+ if cpoldaddr <> newaddr:
mlist.removeMember(oldaddr)
else:
mlist.changeMemberAddress(oldaddr, newaddr)
+ mlist.log_and_notify_admin(cpoldaddr, newaddr)
mlist.Save()
finally:
mlist.Unlock()
+ def log_and_notify_admin(self, oldaddr, newaddr):
+ """Log member address change and notify admin if requested."""
+ syslog('subscribe', '%s: changed member address from %s to %s',
+ self.internal_name(), oldaddr, newaddr)
+ if self.admin_notify_mchanges:
+ lang = self.preferred_language
+ otrans = i18n.get_translation()
+ i18n.set_language(lang)
+ try:
+ realname = self.real_name
+ subject = _('%(realname)s address change notification')
+ finally:
+ i18n.set_translation(otrans)
+ name = self.getMemberName(newaddr)
+ if name is None:
+ name = ''
+ if isinstance(name, UnicodeType):
+ name = name.encode(Utils.GetCharSet(lang), 'replace')
+ text = Utils.maketext(
+ 'adminaddrchgack.txt',
+ {'name' : name,
+ 'oldaddr' : oldaddr,
+ 'newaddr' : newaddr,
+ 'listname': self.real_name,
+ }, mlist=self)
+ msg = Message.OwnerNotification(self, subject, text)
+ msg.send(self)
+
#
# Confirmation processing
@@ -1242,7 +1280,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
# list administrators.
self.SendHostileSubscriptionNotice(invitation, addr)
raise Errors.HostileSubscriptionError
- elif self.subscribe_policy in (2, 3):
+ elif self.subscribe_policy in (2, 3) and \
+ not self.HasAutoApprovedSender(addr):
self.HoldSubscription(addr, fullname, password, digest, lang)
name = self.real_name
raise Errors.MMNeedApproval, _(
@@ -1521,13 +1560,30 @@ bad regexp in bounce_matching_header line: %s
"""Returns matched entry in ban_list if email matches.
Otherwise returns None.
"""
- ban = False
- for pattern in self.ban_list:
+ return self.GetPattern(email, self.ban_list)
+
+ def HasAutoApprovedSender(self, sender):
+ """Returns True and logs if sender matches address or pattern
+ in subscribe_auto_approval. Otherwise returns False.
+ """
+ auto_approve = False
+ if self.GetPattern(sender, self.subscribe_auto_approval):
+ auto_approve = True
+ syslog('vette', '%s: auto approved subscribe from %s',
+ self.internal_name(), sender)
+ return auto_approve
+
+ def GetPattern(self, email, pattern_list):
+ """Returns matched entry in pattern_list if email matches.
+ Otherwise returns None.
+ """
+ matched = None
+ for pattern in pattern_list:
if pattern.startswith('^'):
# This is a regular expression match
try:
if re.search(pattern, email, re.IGNORECASE):
- ban = True
+ matched = pattern
break
except re.error:
# BAW: we should probably remove this pattern
@@ -1535,12 +1591,9 @@ bad regexp in bounce_matching_header line: %s
else:
# Do the comparison case insensitively
if pattern.lower() == email.lower():
- ban = True
+ matched = pattern
break
- if ban:
- return pattern
- else:
- return None
+ return matched
diff --git a/Mailman/Utils.py b/Mailman/Utils.py
index 1a08c119..0cb9f122 100644
--- a/Mailman/Utils.py
+++ b/Mailman/Utils.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -80,6 +80,7 @@ except ImportError:
EMPTYSTRING = ''
UEMPTYSTRING = u''
+CR = '\r'
NL = '\n'
DOT = '.'
IDENTCHARS = ascii_letters + digits + '_'
@@ -918,6 +919,61 @@ def oneline(s, cset):
return EMPTYSTRING.join(s.splitlines())
+def strip_verbose_pattern(pattern):
+ # Remove white space and comments from a verbose pattern and return a
+ # non-verbose, equivalent pattern. Replace CR and NL in the result
+ # with '\\r' and '\\n' respectively to avoid multi-line results.
+ if not isinstance(pattern, str):
+ return pattern
+ newpattern = ''
+ i = 0
+ inclass = False
+ skiptoeol = False
+ copynext = False
+ while i < len(pattern):
+ c = pattern[i]
+ if copynext:
+ if c == NL:
+ newpattern += '\\n'
+ elif c == CR:
+ newpattern += '\\r'
+ else:
+ newpattern += c
+ copynext = False
+ elif skiptoeol:
+ if c == NL:
+ skiptoeol = False
+ elif c == '#' and not inclass:
+ skiptoeol = True
+ elif c == '[' and not inclass:
+ inclass = True
+ newpattern += c
+ copynext = True
+ elif c == ']' and inclass:
+ inclass = False
+ newpattern += c
+ elif re.search('\s', c):
+ if inclass:
+ if c == NL:
+ newpattern += '\\n'
+ elif c == CR:
+ newpattern += '\\r'
+ else:
+ newpattern += c
+ elif c == '\\' and not inclass:
+ newpattern += c
+ copynext = True
+ else:
+ if c == NL:
+ newpattern += '\\n'
+ elif c == CR:
+ newpattern += '\\r'
+ else:
+ newpattern += c
+ i += 1
+ return newpattern
+
+
# Patterns and functions to flag possible XSS attacks in HTML.
# This list is compiled from information at http://ha.ckers.org/xss.html,
# http://www.quirksmode.org/js/events_compinfo.html,
diff --git a/Mailman/Version.py b/Mailman/Version.py
index 66eed767..93616e5f 100644
--- a/Mailman/Version.py
+++ b/Mailman/Version.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -37,7 +37,7 @@ HEX_VERSION = ((MAJOR_REV << 24) | (MINOR_REV << 16) | (MICRO_REV << 8) |
(REL_LEVEL << 4) | (REL_SERIAL << 0))
# config.pck schema version number
-DATA_FILE_VERSION = 105
+DATA_FILE_VERSION = 106
# qfile/*.db schema version number
QFILE_SCHEMA_VERSION = 3
diff --git a/Mailman/versions.py b/Mailman/versions.py
index 9006ec0c..d0960e0d 100755
--- a/Mailman/versions.py
+++ b/Mailman/versions.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2014 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -313,6 +313,15 @@ def UpdateOldVars(l, stored_state):
pass
else:
l.digest_members[k] = 0
+ #
+ # Convert pre 2.2 topics regexps which were compiled in verbose mode
+ # to a non-verbose equivalent.
+ #
+ if stored_state['data_version'] < 106 and stored_state.has_key('topics'):
+ l.topics = []
+ for name, pattern, description, emptyflag in stored_state['topics']:
+ pattern = Utils.strip_verbose_pattern(pattern)
+ l.topics.append((name, pattern, description, emptyflag))
# from_is_list was called author_is_list in 2.1.16rc2 (only).
PreferStored('author_is_list', 'from_is_list',
mm_cfg.DEFAULT_FROM_IS_LIST)
@@ -352,6 +361,8 @@ def NewVars(l):
add_only_if_missing('personalize', 0)
add_only_if_missing('first_strip_reply_to',
mm_cfg.DEFAULT_FIRST_STRIP_REPLY_TO)
+ add_only_if_missing('subscribe_auto_approval',
+ mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL)
add_only_if_missing('unsubscribe_policy',
mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY)
add_only_if_missing('send_goodbye_msg', mm_cfg.DEFAULT_SEND_GOODBYE_MSG)