# Copyright (C) 2001-2018 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 # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from Mailman import mm_cfg from Mailman.i18n import _ from Mailman.mm_cfg import days from Mailman.Gui.GUIBase import GUIBase class Bounce(GUIBase): def GetConfigCategory(self): return 'bounce', _('Bounce processing') def GetConfigInfo(self, mlist, category, subcat=None): if category <> 'bounce': return None return [ _("""These policies control the automatic bounce processing system in Mailman. Here's an overview of how it works.

When a bounce is received, Mailman tries to extract two pieces of information from the message: the address of the member the message was intended for, and the severity of the problem causing the bounce. The severity can be either hard or soft meaning either a fatal error occurred, or a transient error occurred. When in doubt, a hard severity is used.

If no member address can be extracted from the bounce, then the bounce is usually discarded. Otherwise, each member is assigned a bounce score and every time we encounter a bounce from this member we increment the score. Hard bounces increment by 1 while soft bounces increment by 0.5. We only increment the bounce score once per day, so even if we receive ten hard bounces from a member per day, their score will increase by only 1 for that day.

When a member's bounce score is greater than the bounce score threshold, the subscription is disabled. Once disabled, the member will not receive any postings from the list until their membership is explicitly re-enabled (either by the list administrator or the user). However, they will receive occasional reminders that their membership has been disabled, and these reminders will include information about how to re-enable their membership.

You can control both the number of reminders the member will receive and the frequency with which these reminders are sent.

There is one other important configuration variable; after a certain period of time -- during which no bounces from the member are received -- the bounce information is considered stale and discarded. Thus by adjusting this value, and the score threshold, you can control how quickly bouncing members are disabled. You should tune both of these to the frequency and traffic volume of your list."""), _('Bounce detection sensitivity'), ('bounce_processing', mm_cfg.Toggle, (_('No'), _('Yes')), 0, _('Should Mailman perform automatic bounce processing?'), _("""By setting this value to No, you disable all automatic bounce processing for this list, however bounce messages will still be discarded so that the list administrator isn't inundated with them.""")), ('bounce_score_threshold', mm_cfg.Number, 5, 0, _("""The maximum member bounce score before the member's subscription is disabled. This value can be a floating point number."""), _("""Each subscriber is assigned a bounce score, as a floating point number. Whenever Mailman receives a bounce from a list member, that member's score is incremented. Hard bounces (fatal errors) increase the score by 1, while soft bounces (temporary errors) increase the score by 0.5. Only one bounce per day counts against a member's score, so even if 10 bounces are received for a member on the same day, their score will increase by just 1. This variable describes the upper limit for a member's bounce score, above which they are automatically disabled, but not removed from the mailing list.""")), ('bounce_info_stale_after', mm_cfg.Number, 5, 0, _("""The number of days after which a member's bounce information is discarded, if no new bounces have been received in the interim. This value must be an integer.""")), ('bounce_you_are_disabled_warnings', mm_cfg.Number, 5, 0, _("""How many Your Membership Is Disabled warnings a disabled member should get before their address is removed from the mailing list. Set to 0 to immediately remove an address from the list once their bounce score exceeds the threshold. This value must be an integer.""")), ('bounce_you_are_disabled_warnings_interval', mm_cfg.Number, 5, 0, _("""The number of days between sending the Your Membership Is Disabled warnings. This value must be an integer.""")), _('Notifications'), ('bounce_unrecognized_goes_to_list_owner', mm_cfg.Toggle, (_('No'), _('Yes')), 0, _('''Should Mailman send you, the list owner, any bounce messages that failed to be detected by the bounce processor? Yes is recommended.'''), _("""While Mailman's bounce detector is fairly robust, it's impossible to detect every bounce format in the world. You should keep this variable set to Yes for two reasons: 1) If this really is a permanent bounce from one of your members, you should probably manually remove them from your list, and 2) you might want to send the message on to the Mailman developers so that this new format can be added to its known set.

If you really can't be bothered, then set this variable to No and all non-detected bounces will be discarded without further processing.

Note: This setting will also affect all messages sent to your list's -admin address. This address is deprecated and should never be used, but some people may still send mail to this address. If this happens, and this variable is set to No those messages too will get discarded. You may want to set up an autoresponse message for email to the -owner and -admin address.""")), ('bounce_notify_owner_on_bounce_increment', mm_cfg.Toggle, (_('No'), _('Yes')), 0, _("""Should Mailman notify you, the list owner, when bounces cause a member's bounce score to be incremented?"""), _("""Setting this value to Yes will cause Mailman to send a notice including a copy of the bounce message to the list owners whenever a bounce increments a member's bounce score but doesn't cause a disable or a probe to be sent.""")), ('bounce_notify_owner_on_disable', mm_cfg.Toggle, (_('No'), _('Yes')), 0, _("""Should Mailman notify you, the list owner, when bounces cause a member's subscription to be disabled?"""), _("""By setting this value to No, you turn off notification messages that are normally sent to the list owners when a member's delivery is disabled due to excessive bounces. An attempt to notify the member will always be made.""")), ('bounce_notify_owner_on_removal', mm_cfg.Toggle, (_('No'), _('Yes')), 0, _("""Should Mailman notify you, the list owner, when bounces cause a member to be unsubscribed?"""), _("""By setting this value to No, you turn off notification messages that are normally sent to the list owners when a member is unsubscribed due to excessive bounces. An attempt to notify the member will always be made.""")), ] def _setValue(self, mlist, property, val, doc): # Do value conversion from web representation to internal # representation. try: if property == 'bounce_processing': val = int(val) elif property == 'bounce_score_threshold': val = float(val) elif property == 'bounce_info_stale_after': val = days(int(val)) elif property == 'bounce_you_are_disabled_warnings': val = int(val) elif property == 'bounce_you_are_disabled_warnings_interval': val = days(int(val)) elif property == 'bounce_notify_owner_on_disable': val = int(val) elif property == 'bounce_notify_owner_on_removal': val = int(val) except ValueError: doc.addError( _("""Bad value for %(property)s: %(val)s"""), tag = _('Error: ')) return GUIBase._setValue(self, mlist, property, val, doc) def getValue(self, mlist, kind, varname, params): if varname not in ('bounce_info_stale_after', 'bounce_you_are_disabled_warnings_interval'): return None return int(getattr(mlist, varname) / days(1))