diff options
author | Robin Houston <robin.houston@gmail.com> | 2012-06-03 14:20:23 +0100 |
---|---|---|
committer | Robin Houston <robin.houston@gmail.com> | 2012-06-03 14:20:23 +0100 |
commit | 775e122cb4824c9734f50db0dd2967779636080e (patch) | |
tree | 05861e51eb8da822e561e6e02960f0761fd97664 /script/handle-mail-replies | |
parent | b357d29826a67ad527c63d823290226ae2d0c871 (diff) |
Make handle-mail-replies bundler-aware too
Diffstat (limited to 'script/handle-mail-replies')
-rwxr-xr-x | script/handle-mail-replies | 182 |
1 files changed, 3 insertions, 179 deletions
diff --git a/script/handle-mail-replies b/script/handle-mail-replies index 7590f5848..ad4b3719e 100755 --- a/script/handle-mail-replies +++ b/script/handle-mail-replies @@ -1,180 +1,4 @@ -#!/usr/bin/env ruby -# -*- coding: utf-8 -*- +#!/bin/bash -# Handle email responses sent to us. -# -# This script is invoked as a pipe command, i.e. with the raw email message on stdin. -# - If a message is identified as a permanent bounce, the user is marked as having a -# bounced address, and will not be sent any more messages. -# - If a message is identified as an out-of-office autoreply, it is discarded. -# - Any other messages are forwarded to config.get("FORWARD_NONBOUNCE_RESPONSES_TO") - - -# We want to avoid loading rails unless we need it, so we start by just loading the -# config file ourselves. -$alaveteli_dir = File.join(File.dirname(__FILE__), '..') -$:.push(File.join($alaveteli_dir, "commonlib", "rblib")) -load "config.rb" -MySociety::Config.set_file(File.join($alaveteli_dir, 'config', 'general'), true) -MySociety::Config.load_default - -require 'action_mailer' - -def main(in_test_mode) - Dir.chdir($alaveteli_dir) do - raw_message = $stdin.read - begin - message = TMail::Mail.parse(raw_message) - rescue - # Error parsing message. Just pass it on, to be on the safe side. - forward_on(raw_message) unless in_test_mode - return 0 - end - - pfas = permanently_failed_addresses(message) - if !pfas.empty? - if in_test_mode - puts pfas - else - pfas.each do |pfa| - record_bounce(pfa, raw_message) - end - end - return 1 - end - - # If we are still here, there are no permanent failures, - # so if the message is a multipart/report then it must be - # reporting a temporary failure. In this case we discard it - if message.content_type == "multipart/report" - return 1 - end - - # Another style of temporary failure message - subject = message.header_string("Subject") - if message.content_type == "multipart/mixed" && subject == "Delivery Status Notification (Delay)" - return 1 - end - - # Discard out-of-office messages - if is_oof?(message) - return 2 # Use a different return code, to distinguish OOFs from bounces - end - - # Otherwise forward the message on - forward_on(raw_message) unless in_test_mode - return 0 - end -end - -def permanently_failed_addresses(message) - if message.header_string("Return-Path") == "<>" - # Some sort of auto-response - - # Check for Exim’s X-Failed-Recipients header - failed_recipients = message.header_string("X-Failed-Recipients") - if !failed_recipients.nil? - # The X-Failed-Recipients header contains the email address that failed - # Check for the words "This is a permanent error." in the body, to indicate - # a permanent failure - if message.body =~ /This is a permanent error./ - return failed_recipients.split(/,\s*/) - end - end - - # Next, look for multipart/report - if message.content_type == "multipart/report" - permanently_failed_recipients = [] - message.parts.each do |part| - if part.content_type == "message/delivery-status" - sections = part.body.split(/\r?\n\r?\n/) - # The first section is a generic header; subsequent sections - # represent a particular recipient. Since we - sections[1..-1].each do |section| - if section !~ /^Status: (\d)/ || $1 != '5' - # Either we couldn’t find the Status field, or it was a transient failure - break - end - if section =~ /^Final-Recipient: rfc822;(.+)/ - permanently_failed_recipients.push($1) - end - end - end - end - if !permanently_failed_recipients.empty? - return permanently_failed_recipients - end - end - end - - subject = message.header_string("Subject") - # Then look for the style we’ve seen in WebShield bounces - # (These do not have a return path of <> in the cases I have seen.) - if subject == "Returned Mail: Error During Delivery" - if message.body =~ /^\s*---- Failed Recipients ----\s*((?:<[^>]+>\n)+)/ - return $1.scan(/<([^>]+)>/).flatten - end - end - - return [] -end - -def is_oof?(message) - # Check for out-of-office - - if message.header_string("X-POST-MessageClass") == "9; Autoresponder" - return true - end - - subject = message.header_string("Subject").downcase - if message.header_string("Return-Path") == "<>" - if subject.start_with? "out of office: " - return true - end - if subject.start_with? "automatic reply: " - return true - end - end - - if message.header_string("Auto-Submitted") == "auto-generated" - if subject =~ /out of( the)? office/ - return true - end - end - - if subject.start_with? "out of office autoreply:" - return true - end - if subject == "out of office" - return true - end - if subject == "out of office reply" - return true - end - if subject.end_with? "is out of the office" - return true - end - return false -end - -def forward_on(raw_message) - forward_non_bounces_to = MySociety::Config.get("FORWARD_NONBOUNCE_RESPONSES_TO", "user-support@localhost") - IO.popen("/usr/sbin/sendmail -i #{forward_non_bounces_to}", "w") do |f| - f.write(raw_message); - f.close; - end -end - -def load_rails - require File.join('config', 'boot') - require Rails.root + '/config/environment' -end - -def record_bounce(email_address, bounce_message) - load_rails - User.record_bounce_for_email(email_address, bounce_message) -end - -in_test_mode = (ARGV[0] == "--test") -status = main(in_test_mode) -exit(status) if in_test_mode +cd "`dirname "$0"`" +exec bundle exec ./handle-mail-replies.rb |