summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Defaults.py.in6
-rw-r--r--Mailman/MailList.py23
-rw-r--r--Mailman/Utils.py60
-rw-r--r--Mailman/versions.py2
4 files changed, 84 insertions, 7 deletions
diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in
index 4307298c4..7dce4fce6 100644
--- a/Mailman/Defaults.py.in
+++ b/Mailman/Defaults.py.in
@@ -184,6 +184,8 @@ DEFAULT_AUTOMATIC_BOUNCE_ACTION = 1
# get in 1 hour.
DEFAULT_MAX_POSTS_BETWEEN_BOUNCES = 5
+DEFAULT_ADMINISTRIVIA = 1
+
#
# how long the cookie authorizing administrative
# changes via the admin cgi lasts
@@ -195,6 +197,8 @@ ADMIN_COOKIE_LIFE = 60 * 20 # 20 minutes
#
ADMIN_MEMBER_CHUNKSIZE = 10
+
+
# These directories are used to find various important files in the Mailman
# installation. PREFIX and EXEC_PREFIX are set by configure and should point
# to the installation directory of the Mailman package.
@@ -250,4 +254,4 @@ PRIVATE_ARCHIVE_FILE_DIR = os.path.join(PREFIX, 'archives/private')
VERSION = '@VERSION@'
# Data file version number
-DATA_FILE_VERSION = 5
+DATA_FILE_VERSION = 6
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 82a12efd4..5aa8a2b90 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -278,6 +278,18 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
'"Reply-To" Munging Considered Harmful</a> for a general.'
" discussion of this issue."),
+ ('administrivia', mm_cfg.Radio, ('No', 'Yes'), 0,
+ "check messages that are destined for the list for"
+ " adminsitrative request content?",
+
+ "Administrivia tests will chech mail that is destined for the list "
+ " for adminstriative contect (like subscribe, unsubscribe, etc). "
+ " If the message looks like an adminitrative request, it will "
+ "be added to the administrative requests database and the administrator "
+ "will be notified. "),
+
+
+
('reminders_to_admins', mm_cfg.Radio, ('No', 'Yes'), 0,
'Send password reminders to "-admin" address instead of'
' directly to user.',
@@ -780,8 +792,8 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
def DeleteMember(self, name, whence=None):
self.IsListInitialized()
-# FindMatchingAddresses *should* never return more than 1 address.
-# However, should log this, just to make sure.
+ # 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)
if not len(aliases):
@@ -897,7 +909,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
return line
return 0
-#msg should be an IncomingMessage object.
+ # msg should be an IncomingMessage object.
def Post(self, msg, approved=0):
self.IsListInitialized()
# Be sure to ExtractApproval, whether or not flag is already set!
@@ -953,6 +965,11 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin,
self.AddRequest('post', Utils.SnarfMessage(msg),
Errors.IMPLICIT_DEST_MSG,
msg.getheader('subject'))
+ if self.administrivia and Utils.IsAdministrivia(msg):
+ self.AddRequest('post', Utils.SnarfMessage(msg),
+ 'possible administrivia to list',
+ msg.getheader("subject"))
+
if self.bounce_matching_headers:
triggered = self.HasMatchingHeader(msg)
if triggered:
diff --git a/Mailman/Utils.py b/Mailman/Utils.py
index 88f2ea5c7..b78731314 100644
--- a/Mailman/Utils.py
+++ b/Mailman/Utils.py
@@ -368,15 +368,16 @@ def ObscureEmail(addr, for_text=0):
When for_text option is set (not default), make a sentence fragment
instead of a token."""
if for_text:
- return re.sub("@", " at ", addr)
+ return string.replace(addr, "@", " at ")
else:
- return re.sub("@", "__at__", addr)
+ return string.replace(addr, "@", "__at__")
def UnobscureEmail(addr):
"""Invert ObscureEmail() conversion."""
# Contrived to act as an identity operation on already-unobscured
# emails, so routines expecting obscured ones will accept both.
- return re.sub("__at__", "@", addr)
+ return string.replace(addr, "__at__", "@")
+
def map_maillists(func, names=None, unlock=None, verbose=0):
"""Apply function (of one argument) to all list objs in turn.
@@ -436,6 +437,59 @@ def maketext(templatefile, dict, raw=0):
return wrap(template % dict)
+#
+# given an IncomingMessage object,
+# test for administrivia (eg subscribe, unsubscribe, etc).
+# the test must be a good guess -- messages that return true
+# get sent to the list admin instead of the entire list.
+#
+def IsAdministrivia(msg):
+ lines = map(string.lower, msg.readlines())
+ if len(lines) > 30:
+ return 0
+ #
+ # check to see how many lines that actually have text in them there are
+ #
+ admin_data = {"subscribe": (0, 3),
+ "unsubscribe": (0, 1),
+ "who": (0,0),
+ "info": (0,0),
+ "lists": (0,0),
+ "set": (2, 3),
+ "help": (0,0),
+ "password": (2, 2),
+ "options": (0,0),
+ "remove": (0, 0)}
+ lines_with_text = 0
+ for line in lines:
+ if string.strip(line):
+ lines_with_text = lines_with_text + 1
+ if lines_with_text > 10: # we might want to change this to mm_cfg.DEFAULT_MAIL_COMMANDS_MAX_LINES.
+ return 0
+ if admin_data.has_key(string.lower(string.strip(msg.body))):
+ return 1
+ try:
+ if admin_data.has_key(string.lower(string.strip(msg["subject"]))):
+ return 1
+ except KeyError:
+ pass
+ for line in lines[:5]:
+ if not string.strip(line):
+ return
+ words = string.split(line)
+ if admin_data.has_key(words[0]):
+ min_args, max_args = admin_data[words[0]]
+ if min_args <= len(words[1:]) <= max_args:
+ return 1
+ return 0
+
+
+
+
+
+
+
+
diff --git a/Mailman/versions.py b/Mailman/versions.py
index d030f2e3d..83de0fd92 100644
--- a/Mailman/versions.py
+++ b/Mailman/versions.py
@@ -70,6 +70,8 @@ def UpdateOldVars(l, stored_state):
else:
l.subscribe_policy = 2 # admin approval
delattr(l, "open_subscribe")
+ if not hasattr(l, "administrivia"):
+ setatrr(l, "administrivia", mm_cfg.DEFAULT_ADMINISTRIVIA)
# - dropped vars:
# for a in ['archive_retain_text_copy',
# 'archive_update_frequency']: