Merge authors:
  jimpop@template.hostname
Related merge proposals:
  
https://code.launchpad.net/~jimpop/mailman/mailman-auto-mod-verbose-members/+merge/276706
  proposed by: Jim Popovitch (jimpop)
  review: Approve - Mark Sapiro (msapiro)
------------------------------------------------------------
revno: 1595 [merge]
committer: Mark Sapiro <m...@msapiro.net>
branch nick: 2.1
timestamp: Mon 2016-01-18 15:56:58 -0800
message:
  Merged and tweaked Jim P's mailman-auto-mod-verbose-members branch.
modified:
  Mailman/Defaults.py.in
  Mailman/Gui/Privacy.py
  Mailman/Handlers/SpamDetect.py
  Mailman/Utils.py
  Mailman/Version.py
  Mailman/versions.py
  NEWS


--
lp:mailman/2.1
https://code.launchpad.net/~mailman-coders/mailman/2.1

Your team Mailman Checkins is subscribed to branch lp:mailman/2.1.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/2.1/+edit-subscription
=== modified file 'Mailman/Defaults.py.in'
--- Mailman/Defaults.py.in	2015-09-19 02:18:34 +0000
+++ Mailman/Defaults.py.in	2016-01-18 23:56:58 +0000
@@ -1118,6 +1118,25 @@
 # The total time to spend trying to get an answer to the question.
 DMARC_RESOLVER_LIFETIME = seconds(5)
 
+# Should the list server auto-moderate members who post too frequently
+# This is intended to stop people who join a list and then use a bot to
+# send many spam messages in a short interval.  These are default settings
+# for new lists.  See the web admin Privacy options -> Sender filters page
+# and the Details for member_verbosity_threshold and member_verbosity_interval
+# links for more information.
+# DEFAULT_MEMBER_VERBOSITY_INTERVAL = number of seconds to track posts
+# DEFAULT_MEMBER_VERBOSITY_THRESHOLD = number of allowed posts per interval
+#                                     (0 to disable).
+DEFAULT_MEMBER_VERBOSITY_INTERVAL = 300
+DEFAULT_MEMBER_VERBOSITY_THRESHOLD = 0
+ 
+# This controls how often to clean old post time entries from the dictionary
+# used to implement the member verbosity feature. This is a compromise between
+# using resources for cleaning and allowing the dictionary to grow very large.
+# The setting is the number of passes through the code before the dictionary
+# is cleaned.
+VERBOSE_CLEAN_LIMIT = 1000
+
 # What domains should be considered equivalent when testing list membership
 # for posting/moderation.
 # If two poster addresses with the same local part but

=== modified file 'Mailman/Gui/Privacy.py'
--- Mailman/Gui/Privacy.py	2015-02-04 04:48:25 +0000
+++ Mailman/Gui/Privacy.py	2016-01-18 23:56:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2016 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
@@ -61,7 +61,7 @@
                             _('Confirm and approve')),
                            0,
                            _('What steps are required for subscription?<br>'),
-                           _('''None - no verification steps (<em>Not
+                           _("""None - no verification steps (<em>Not
                            Recommended </em>)<br>
                            Confirm (*) - email confirmation step required <br>
                            Require approval - require list administrator
@@ -75,7 +75,7 @@
 
                            This prevents mischievous (or malicious) people
                            from creating subscriptions for others without
-                           their consent.'''))
+                           their consent."""))
         else:
             sub_cfentry = ('subscribe_policy', mm_cfg.Radio,
                            # choices
@@ -84,7 +84,7 @@
                             _('Confirm and approve')),
                            1,
                            _('What steps are required for subscription?<br>'),
-                           _('''Confirm (*) - email confirmation required <br>
+                           _("""Confirm (*) - email confirmation required <br>
                            Require approval - require list administrator
                            approval for subscriptions <br>
                            Confirm and approve - both confirm and approve
@@ -94,7 +94,7 @@
                            subscription request number that they must reply to
                            in order to subscribe.<br> This prevents
                            mischievous (or malicious) people from creating
-                           subscriptions for others without their consent.'''))
+                           subscriptions for others without their consent."""))
 
         # some helpful values
         admin = mlist.GetScriptURL('admin')
@@ -108,8 +108,8 @@
 
             _('Subscribing'),
             ('advertised', mm_cfg.Radio, (_('No'), _('Yes')), 0,
-             _('''Advertise this list when people ask what lists are on this
-             machine?''')),
+             _("""Advertise this list when people ask what lists are on this
+             machine?""")),
 
             sub_cfentry,
 
@@ -155,8 +155,8 @@
              (_('Anyone'), _('List members'), _('List admin only')), 0,
              _('Who can view subscription list?'),
 
-             _('''When set, the list of subscribers is protected by member or
-             admin password authentication.''')),
+             _("""When set, the list of subscribers is protected by member or
+             admin password authentication.""")),
 
             ('obscure_addresses', mm_cfg.Radio, (_('No'), _('Yes')), 0,
              _("""Show member addresses so they're not directly recognizable
@@ -229,6 +229,37 @@
              <a href="%(adminurl)s/members">membership management
              screens</a>.""")),
 
+            ('member_verbosity_threshold', mm_cfg.Number, 5, 0,
+             _("""Ceiling on acceptable number of member posts, per interval,
+               before automatic moderation."""),
+
+             _("""If a member posts this many times, within a period of time
+               the member is automatically moderated.  Use 0 to disable.  See
+               <a href="?VARHELP=privacy/sender/member_verbosity_interval"
+               >member_verbosity_interval</a> for details on the time period.
+
+               <p>This is intended to stop people who join a list or lists and
+               then use a bot to send many spam messages in a short interval.
+
+               <p>Be careful when using this setting.  If it is set too low,
+               this can be triggered by a single post cross-posted to
+               multiple lists or by a single post to an umbrella list.""")),
+
+            ('member_verbosity_interval', mm_cfg.Number, 5, 0,
+             _("""Number of seconds to keep posts to this list to determine
+               member_verbosity_threshold for automatic moderation of a
+               member."""),
+
+             _("""If a member's total posts to all lists in this installation
+               with member_verbosity_threshold enabled exceeds this list's
+               member_verbosity_threshold, the member is automatically
+               moderated on this list.
+
+               <p>Posts which are counted towards this list's
+               member_verbosity_threshold are all posts to any list with
+               member_verbosity_threshold enabled that arrived within that
+               list's member_verbosity_interval.""")),
+
             ('member_moderation_action', mm_cfg.Radio,
              (_('Hold'), _('Reject'), _('Discard')), 0,
              _("""Action to take when a moderated member posts to the
@@ -484,8 +515,8 @@
             ('max_num_recipients', mm_cfg.Number, 5, 0,
              _('Ceiling on acceptable number of recipients for a posting.'),
 
-             _('''If a posting has this number, or more, of recipients, it is
-             held for admin approval.  Use 0 for no ceiling.''')),
+             _("""If a posting has this number, or more, of recipients, it is
+             held for admin approval.  Use 0 for no ceiling.""")),
             ]
 
         spam_rtn = [

=== modified file 'Mailman/Handlers/SpamDetect.py'
--- Mailman/Handlers/SpamDetect.py	2015-03-02 21:50:24 +0000
+++ Mailman/Handlers/SpamDetect.py	2016-01-18 23:56:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 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
@@ -122,6 +122,21 @@
                     raise Errors.RejectMessage, text
                 elif mlist.dmarc_moderation_action == 4:
                     raise Errors.DiscardMessage
+
+        # Get member address if any.
+        for sender in msg.get_senders():
+            if mlist.isMember(sender):
+                break
+        else:
+            sender = msg.get_sender()
+        if (mlist.member_verbosity_threshold > 0 and
+            Utils.IsVerboseMember(mlist, sender)
+           ):
+             mlist.setMemberOption(sender, mm_cfg.Moderate, 1)
+             syslog('vette',
+                    '%s: Automatically Moderated %s for verbose postings.',
+                     mlist.real_name, sender) 
+
     if msgdata.get('approved'):
         return
     # First do site hard coded header spam checks

=== modified file 'Mailman/Utils.py'
--- Mailman/Utils.py	2015-09-17 00:39:34 +0000
+++ Mailman/Utils.py	2016-01-18 23:56:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 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
@@ -1246,6 +1246,53 @@
     return False
 
 
+# Check a known list in order to auto-moderate verbose members
+# dictionary to remember recent posts.
+recentMemberPostings = {}
+# counter of times through
+clean_count = 0
+def IsVerboseMember(mlist, email):
+    """For lists that request it, we keep track of recent posts by address.
+A message from an address to a list, if the list requests it, is remembered
+for a specified time whether or not the address is a list member, and if the
+address is a member and the member is over the threshold for the list, that
+fact is returned."""
+
+    global clean_count
+
+    if mlist.member_verbosity_threshold == 0:
+        return False
+
+    email = email.lower()
+
+    now = time.time()
+    recentMemberPostings.setdefault(email,[]).append(now +
+                                       float(mlist.member_verbosity_interval)
+                                   )
+    x = range(len(recentMemberPostings[email]))
+    x.reverse()
+    for i in x:
+        if recentMemberPostings[email][i] < now:
+            del recentMemberPostings[email][i]
+
+    clean_count += 1
+    if clean_count >= mm_cfg.VERBOSE_CLEAN_LIMIT:
+        clean_count = 0
+        for addr in recentMemberPostings.keys():
+            x = range(len(recentMemberPostings[addr]))
+            x.reverse()
+            for i in x:
+                if recentMemberPostings[addr][i] < now:
+                    del recentMemberPostings[addr][i]
+            if not recentMemberPostings[addr]:
+                del recentMemberPostings[addr]
+    if not mlist.isMember(email):
+        return False
+    return (len(recentMemberPostings.get(email, [])) >=
+                mlist.member_verbosity_threshold
+           )
+
+
 def check_eq_domains(email, domains_list):
     """The arguments are an email address and a string representing a
     list of lists in a form like 'a,b,c;1,2' representing [['a', 'b',

=== modified file 'Mailman/Version.py'
--- Mailman/Version.py	2015-03-31 17:18:39 +0000
+++ Mailman/Version.py	2016-01-18 23:56:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 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 @@
                (REL_LEVEL << 4)  | (REL_SERIAL << 0))
 
 # config.pck schema version number
-DATA_FILE_VERSION = 108
+DATA_FILE_VERSION = 109
 
 # qfile/*.db schema version number
 QFILE_SCHEMA_VERSION = 3

=== modified file 'Mailman/versions.py'
--- Mailman/versions.py	2015-02-08 07:54:26 +0000
+++ Mailman/versions.py	2016-01-18 23:56:58 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 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
@@ -497,6 +497,10 @@
     add_only_if_missing('dmarc_moderation_notice', '')
     add_only_if_missing('dmarc_wrapped_message_text',
                        mm_cfg.DEFAULT_DMARC_WRAPPED_MESSAGE_TEXT)
+    add_only_if_missing('member_verbosity_threshold', 
+                       mm_cfg.DEFAULT_MEMBER_VERBOSITY_THRESHOLD)
+    add_only_if_missing('member_verbosity_interval',
+                       mm_cfg.DEFAULT_MEMBER_VERBOSITY_INTERVAL)
     add_only_if_missing('equivalent_domains', 
                        mm_cfg.DEFAULT_EQUIVALENT_DOMAINS)
     add_only_if_missing('new_member_options',

=== modified file 'NEWS'
--- NEWS	2016-01-18 04:44:26 +0000
+++ NEWS	2016-01-18 23:56:58 +0000
@@ -9,6 +9,13 @@
 
   New Features
 
+    - Thanks to Jim Popovitch there is now a feature to automatically turn
+      on moderation for a malicious list member who attempts to flood a list
+      with spam.  See the details for the Privacy options ... -> Sender
+      filters -> member_verbosity_threshold and member_verbosity_interval
+      settings in the web admin UI and the documentation in Defaults.py for
+      the DEFAULT_MEMBER_VERBOSITY_* and VERBOSE_CLEAN_LIMIT for information.
+
     - bin/list_members now has options to display all moderated or all
       non-moderated members.
 

_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to