diff options
| author | mailman | 1998-03-27 05:21:48 +0000 |
|---|---|---|
| committer | mailman | 1998-03-27 05:21:48 +0000 |
| commit | f0b02da4f34105feba873cc7ef6810b47c14775a (patch) | |
| tree | 082decc9559011131ed6e56dd48260dbd0759ba9 /modules/mm_bouncer.py | |
| parent | 51de1dab23dbd2e7f38ecde9216177191483bbd9 (diff) | |
| download | mailman-f0b02da4f34105feba873cc7ef6810b47c14775a.tar.gz mailman-f0b02da4f34105feba873cc7ef6810b47c14775a.tar.zst mailman-f0b02da4f34105feba873cc7ef6810b47c14775a.zip | |
Finally fix bounce handling so (1) there is an option for users to be
disabled, rather than removed, and (2) that option can be accompanied
by email to the admin, or not, but the removal option always entails
email to the admin. (Email to the admin was previously not
implemented, though there were options that were supposed to do it.)
Mostly implemented in new method, .HandleBouncingAddress(), which
dispatches to RemoveBouncingAddress() or DisableBouncingAddress().
Improve logging - more terse messages, but more comprehensive
coverage.
Diffstat (limited to 'modules/mm_bouncer.py')
| -rw-r--r-- | modules/mm_bouncer.py | 122 |
1 files changed, 96 insertions, 26 deletions
diff --git a/modules/mm_bouncer.py b/modules/mm_bouncer.py index 4ab2ec89e..5e3ebfe2a 100644 --- a/modules/mm_bouncer.py +++ b/modules/mm_bouncer.py @@ -27,7 +27,8 @@ class Bouncer: ('bounce_processing', mm_cfg.Toggle, ('No', 'Yes'), 0, 'Try to figure out error messages automatically? '), ('minimum_removal_date', mm_cfg.Number, 3, 0, - 'Minimum number of days an address has been bad before we consider nuking it'), + 'Minimum number of days an address has been non-fatally ' + 'bad before we take action'), ('minimum_post_count_before_bounce_action', mm_cfg.Number, 3, 0, 'Minimum number of posts to the list since members first ' 'bounce before we consider removing them from the list'), @@ -35,9 +36,11 @@ class Bouncer: "Maximum number of messages your list gets in an hour. " "(Yes, bounce detection finds this info useful)"), ('automatic_bounce_action', mm_cfg.Radio, - ("Do nothing", "Disable and notify me ", "Remove and notify me", - "Remove and don't notify me"), - 0, "Automatically remove addresses considered for removal, or alert you?") + ("Do nothing", + "Disable and notify me", + "Disable and DON'T notify me", + "Remove and notify me"), + 0, "Action when fatal or excessive bounces are detected.") ] def ClearBounceInfo(self, email): email = string.lower(email) @@ -54,7 +57,7 @@ class Bouncer: # What the last post ID was that we saw a bounce. self.bounce_info[string.lower(email)] = [now, self.post_id, self.post_id] - self.LogMsg("bounce", report + "first bounce") + self.LogMsg("bounce", report + "first") self.Save() return @@ -67,15 +70,15 @@ class Bouncer: # Should maybe keep track in see if people become stale entries # often... self.LogMsg("bounce", - report + "first fresh bounce on a stale addr") + report + "first fresh") self.bounce_info[addr] = [now, self.post_id, self.post_id] return self.bounce_info[addr][2] = self.post_id if ((self.post_id - inf[1] > self.minimum_post_count_before_bounce_action) and difference > self.minimum_removal_date * 24 * 60 * 60): - self.LogMsg("bounce", report + "they're Out of here...") - self.RemoveBouncingAddress(addr) + self.LogMsg("bounce", report + "exceeded limits") + self.HandleBouncingAddress(addr) return else: post_count = (self.minimum_post_count_before_bounce_action - @@ -93,40 +96,108 @@ class Bouncer: elif len(mm_utils.FindMatchingAddresses(addr, self.digest_members)): if self.volume > inf[1]: self.LogMsg("bounce", - "%s: First fresh bounce on a stale addr (D).", + "%s: first fresh (D)", self._internal_name) self.bounce_info[addr] = [now, self.volume, self.volume] return if difference > self.minimum_removal_date * 24 * 60 * 60: - self.LogMsg("bounce", "Seeya, digest-ee...") - self.RemoveBouncingAddress(addr) + self.LogMsg("bounce", "exceeded limits (D)") + self.HandleBouncingAddress(addr) return self.LogMsg("bounce", - "digester lucked out, he's still got time!") + "digester lucked out") else: self.LogMsg("bounce", - "Address %s wasn't a member of %s.", - addr, - self._internal_name) + "%s: address %s not a member.", + self._internal_name, + addr) - + def HandleBouncingAddress(self, addr): + """Disable or remove addr according to bounce_action setting.""" + if self.automatic_bounce_action == 0: + return + elif self.automatic_bounce_action == 1: + succeeded = self.DisableBouncingAddress(addr) + did = "disabled" + send = 1 + elif self.automatic_bounce_action == 2: + succeeded = self.DisableBouncingAddress(addr) + did = "disabled" + send = 0 + elif self.automatic_bounce_action == 3: + succeeded = self.RemoveBouncingAddress(addr) + did = "removed" + send = 1 + if send: + if succeeded != 1: + negative="not " + recipient = mm_cfg.MAILMAN_OWNER + else: + negative="" + recipient = self.GetAdminEmail() + text = ("This is a mailman maillist administrator notice.\n" + "\n\tMaillist:\t%s\n" + "\tMember:\t\t%s\n" + "\tAction:\t\tSubscription %s%s.\n" + "\tReason:\t\tExcessive or fatal bounces.\n" + % (self.real_name, addr, negative, did)) + if succeeded != 1: + text = text + "\tFailure reason:\t%s\n\n" % succeeded + else: + text = text + "\n" + if did == "disabled" and succeeded == 1: + text = text + ( + "You can reenable their subscription by visiting " + "their options page\n" + "(via %s) and using your\n" + "list admin password to authorize the option change.\n\n" + % self.GetScriptURL('listinfo')) + text = text + ("Questions? Contact the mailman site admin,\n%s" + % mm_cfg.MAILMAN_OWNER) + if negative: + negative = string.upper(negative) + self.SendTextToUser(subject = ("%s member %s %s%s due to bounces" + % (self.real_name, addr, + negative, did)), + recipient = recipient, + sender = mm_cfg.MAILMAN_OWNER, + errorsto = mm_cfg.MAILMAN_OWNER, + text = text) def DisableBouncingAddress(self, addr): + if not self.IsMember(addr): + reason = "User not found." + self.LogMsg("bounce", "%s: NOT disabled %s: %s", + self.real_name, addr, reason) + return reason try: self.SetUserOption(addr, mm_cfg.DisableDelivery, 1) - self.LogMsg("bounce", "%s: inhibited %s", self.real_name, addr) - # Send mail to the user... but we can't! + self.LogMsg("bounce", "%s: disabled %s", self.real_name, addr) + self.Save() + return 1 except mm_err.MMNoSuchUserError: + self.LogMsg("bounce", "%s: NOT disabled %s: %s", + self.real_name, addr, mm_err.MMNoSuchUserError) self.ClearBounceInfo(addr) - self.Save() + self.Save() + return mm_err.MMNoSuchUserError def RemoveBouncingAddress(self, addr): + if not self.IsMember(addr): + reason = "User not found." + self.LogMsg("bounce", "%s: NOT removed %s: %s", + self.real_name, addr, reason) + return reason try: self.DeleteMember(addr) self.LogMsg("bounce", "%s: removed %s", self.real_name, addr) - # Send mail to the user... but we can't! + self.Save() + return 1 except mm_err.MMNoSuchUserError: + self.LogMsg("bounce", "%s: NOT removed %s: %s", + self.real_name, addr, mm_err.MMNoSuchUserError) self.ClearBounceInfo(addr) - self.Save() + self.Save() + return mm_err.MMNoSuchUserError # Return 0 if we couldn't make any sense of it, 1 if we handled it. def ScanMessage(self, msg): @@ -191,14 +262,13 @@ class Bouncer: emails = string.split(email,',') for email_addr in emails: if email_addr not in did: - self.RemoveBouncingAddress( + self.HandleBouncingAddress( string.strip(email_addr)) did.append(email_addr) message_groked = 1 continue elif action == BOUNCE: emails = string.split(email,',') - print emails for email_addr in emails: self.RegisterBounce(email_addr) message_groked = 1 @@ -215,17 +285,17 @@ class Bouncer: continue if messy_pattern_3.match(line) <> -1 or messy_pattern_4.match(line) <> -1 or messy_pattern_5.match(line) <> -1: username = string.split(line)[1] - self.RemoveBouncingAddress('%s@%s' % (username, remote_host)) + self.HandleBouncingAddress('%s@%s' % (username, remote_host)) message_groked = 1 continue if messy_pattern_6.match(line) <> -1: username = string.split(string.strip(line))[0][:-1] - self.RemoveBouncingAddress('%s@%s' % (username, remote_host)) + self.HandleBouncingAddress('%s@%s' % (username, remote_host)) message_groked = 1 continue if messy_pattern_7.match(line) <> -1: username = string.split(string.strip(line))[0] - self.RemoveBouncingAddress('%s@%s' % (username, remote_host)) + self.HandleBouncingAddress('%s@%s' % (username, remote_host)) message_groked = 1 continue |
