tangofoxtrot-tmail_bouncer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 tangofoxtrot
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ = tmail_bouncer
2
+
3
+ Additional functionality to Tmail to handle and parse bounce back emails.
4
+
5
+ Original code came from http://github.com/dmarkow/tmail_bounce_parser/tree/master
6
+
7
+ == Copyright
8
+
9
+ Copyright (c) 2009 tangofoxtrot. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "tmail_bouncer"
8
+ gem.summary = %Q{TODO}
9
+ gem.email = "richard.luther@gmail.com"
10
+ gem.homepage = "http://github.com/tangofoxtrot/tmail_bouncer"
11
+ gem.authors = ["tangofoxtrot"]
12
+
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/*_test.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/*_test.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+
40
+ task :default => :test
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ if File.exist?('VERSION.yml')
45
+ config = YAML.load(File.read('VERSION.yml'))
46
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
47
+ else
48
+ version = ""
49
+ end
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "tmail_bouncer #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
56
+
@@ -0,0 +1,69 @@
1
+ module TmailBouncer
2
+ class Base
3
+ attr_accessor :status_info, :original_message_id, :original_sender, :original_recipient, :original_subject, :handling_server, :status_part, :original_message
4
+
5
+
6
+ def initialize(email)
7
+ @email = email
8
+ determine_status_part
9
+ determine_original_message_part
10
+ parse_status_part
11
+ parse_original_message_part
12
+ parse_original_recipient
13
+ parse_original_sender
14
+ parse_original_subject
15
+ end
16
+
17
+ def self.available_bouncers
18
+ @@bouncers ||= [TmailBouncer::StandardBouncer]
19
+ end
20
+
21
+
22
+ def self.determine_bouncer(email)
23
+ if klass = available_bouncers.find {|bouncer| bouncer.recognize? email}
24
+ klass.new(email)
25
+ else
26
+ TmailBouncer::NoBouncer.new
27
+ end
28
+ end
29
+
30
+ def self.from_email(email)
31
+ bounce = determine_bouncer(email)
32
+ # lets start by trying to extract the message/delivery-status part
33
+
34
+ bounce
35
+ end
36
+
37
+ def status
38
+ case status_info['Status']
39
+ when /^5/
40
+ 'Failure'
41
+ when /^4/
42
+ 'Temporary Failure'
43
+ when /^2/
44
+ 'Success'
45
+ end
46
+ end
47
+
48
+
49
+ # def determine_status_part
50
+ # end
51
+ #
52
+ # def determine_original_message_part
53
+ # end
54
+ #
55
+ # def parse_status_part
56
+ # end
57
+ #
58
+ # def parse_original_message_part
59
+ #
60
+ # end
61
+
62
+
63
+ end
64
+
65
+ def undeliverable_info
66
+ TmailBouncer::Base.from_email(self)
67
+ end
68
+
69
+ end
@@ -0,0 +1,12 @@
1
+ module TmailBouncer
2
+ class NoBouncer < Base
3
+
4
+ def initialize
5
+
6
+ end
7
+
8
+ def status
9
+ "Success"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ module TmailBouncer
2
+ class StandardBouncer < Base
3
+
4
+ def self.recognize?(email)
5
+ email.parts.detect do |part|
6
+ part.content_type == "message/delivery-status"
7
+ end
8
+ end
9
+
10
+ def determine_status_part
11
+ self.status_part = @email.parts.detect { |part| part.content_type == "message/delivery-status" }
12
+ end
13
+
14
+ def determine_original_message_part
15
+ self.original_message = @email.parts.detect do |part|
16
+ part.content_type == "message/rfc822"
17
+ end
18
+ end
19
+
20
+
21
+ def parse_status_part
22
+ statuses = self.status_part.body.gsub("\n ","").split(/\n/)
23
+ self.status_info = statuses.inject({}) do |hash,line|
24
+ key,value = line.split(/:/,2)
25
+ hash[key] = value.strip rescue nil
26
+ hash
27
+ end
28
+ end
29
+
30
+ def parse_original_message_part
31
+ self.original_message = TMail::Mail.parse(self.original_message.body)
32
+ self.original_message_id = self.original_message.message_id
33
+ end
34
+
35
+ def parse_original_recipient
36
+ self.original_recipient = self.status_info["Final-Recipient"].to_s.gsub("Final-Recipient: ","").gsub("rfc822;","").strip
37
+ end
38
+
39
+ def parse_original_sender
40
+ self.original_sender = self.original_message.from.to_a.first
41
+ end
42
+
43
+ def parse_original_subject
44
+ self.original_subject = self.original_message.subject
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ require 'tmail'
2
+ require 'bouncers/base'
3
+ require 'bouncers/standard_bouncer'
4
+ require 'bouncers/no_bouncer'
5
+ TMail::Mail.send :include, TmailBouncer
@@ -0,0 +1,72 @@
1
+ Return-Path: <>
2
+ X-Original-To: joe@example.com
3
+ Delivered-To: joe@example.com
4
+ Received: by mail.example.com (Postfix)
5
+ id CD6DE1780F5; Thu, 28 May 2009 14:03:11 -0700 (PDT)
6
+ Date: Thu, 28 May 2009 14:03:11 -0700 (PDT)
7
+ From: MAILER-DAEMON@example.com (Mail Delivery System)
8
+ Subject: Undelivered Mail Returned to Sender
9
+ To: joe@example.com
10
+ MIME-Version: 1.0
11
+ Content-Type: multipart/report; report-type=delivery-status;
12
+ boundary="A6CFB1785AB.1243544591/mail.example.com"
13
+ Message-Id: <20090528210311.CD6DE1780F5@mail.example.com>
14
+
15
+ This is a MIME-encapsulated message.
16
+
17
+ --A6CFB1785AB.1243544591/mail.example.com
18
+ Content-Description: Notification
19
+ Content-Type: text/plain
20
+
21
+ This is the Postfix program at host mail.example.com.
22
+
23
+ I'm sorry to have to inform you that your message could not
24
+ be delivered to one or more recipients. It's attached below.
25
+
26
+ For further assistance, please send mail to <postmaster>
27
+
28
+ If you do so, please include this problem report. You can
29
+ delete your own text from the attached returned message.
30
+
31
+ The Postfix program
32
+
33
+ <fred@somewhere.com>: host mail.somewhere.com[1.1.1.1] said: 554
34
+ delivery error: dd Sorry your message to fred@somewhere.com cannot be
35
+ delivered. This account has been disabled or discontinued [#102]. -
36
+ mail.somewhere.com (in reply to end of DATA command)
37
+
38
+ --A6CFB1785AB.1243544591/mail.example.com
39
+ Content-Description: Delivery report
40
+ Content-Type: message/delivery-status
41
+
42
+ Reporting-MTA: dns; mail.example.com
43
+ X-Postfix-Queue-ID: A6CFB1785AB
44
+ X-Postfix-Sender: rfc822; joe@example.com
45
+ Arrival-Date: Thu, 28 May 2009 13:26:56 -0700 (PDT)
46
+
47
+ Final-Recipient: rfc822; fred@somewhere.com
48
+ Action: failed
49
+ Status: 5.0.0
50
+ Diagnostic-Code: X-Postfix; host mail.somewhere.com[1.1.1.1] said: 554
51
+ delivery error: dd Sorry your message to fred@somewhere.com cannot be
52
+ delivered. This account has been disabled or discontinued [#102]. -
53
+ mail.somewhere.com (in reply to end of DATA command)
54
+
55
+ --A6CFB1785AB.1243544591/mail.example.com
56
+ Content-Description: Undelivered Message
57
+ Content-Type: message/rfc822
58
+
59
+ Received: from mail.example.com (example.com [2.2.2.2])
60
+ by mail.example.com (Postfix) with ESMTP id A6CFB1785AB
61
+ for <fred@somewhere.com>; Thu, 28 May 2009 13:26:56 -0700 (PDT)
62
+ Date: Thu, 28 May 2009 13:26:06 -0700
63
+ From: joe@example.com
64
+ To: fred@somewhere.com
65
+ Subject: I like turtles
66
+ Mime-Version: 1.0
67
+ Content-Type: text/html; charset=utf-8
68
+ Message-Id: <1234.1212@example.com>
69
+
70
+ Hi
71
+
72
+ --A6CFB1785AB.1243544591/mail.example.com--
@@ -0,0 +1,42 @@
1
+ Return-Path: <joe@example.com>
2
+ X-Original-To: fred@somewhere.com
3
+ Delivered-To: fred@somewhere.com
4
+ Received: from mail.mud.yahoo.com (mail.pnn.com [8.12.160.195])
5
+ by mail.somewhere.com (Postfix) with SMTP id 618E317804A
6
+ for <fred@somewhere.com>; Thu, 28 May 2009 23:13:11 -0700 (PDT)
7
+ Received: from mail.mud.yahoo.com ([68.142.200.144]
8
+ helo=mail.mud.yahoo.com) by ASSP.nospam; 28 May 2009
9
+ 23:13:11 -0700
10
+ Received: (qmail 3737 invoked by uid 60001); 29 May 2009 06:06:40 -0000
11
+ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024; t=1243577200; bh=jq9T7juj3eU1mLVzXoF9iug+sOAeLdokzpK7l6424R0=; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; b=DFynmvLugBnhvJRnSgOKYtXDUQHVYBQbu4ZIaw8HrtBcfVXdunZUAXfalf2wL22pQpBI4jd70OVucAWhAi7g8IqpdDYCZ5EMApRQP8yOlelrCULXnvv8hqbPO8IL/nenNq1W+2gOY9zTgHNubWBvOd8pAxF3odwlPJqqx7XgQb4=
12
+ DomainKey-Signature:a=rsa-sha1; q=dns; c=nofws;
13
+ s=s1024; d=yahoo.com;
14
+ h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type;
15
+ b=0PXdCXj2w/uWB5Cvl1Aau1YBGeITW4Ep333GHD8NkGFt+87rTzn8Kz2Zxup//fqCjfUv5rU3mLt35ALNmzJ1pwX9odVsz2lpGQxZS0ExDizP+WBLlI51/1q11ufv6EJ7dlTo0E8Ia9x2pc9MdrOImKkEbUTzdzLTVzdGR34cXrs=;
16
+ Message-ID: <381499.2516.qm@web30801.mail.mud.yahoo.com>
17
+ X-YMail-OSG: hjm_1hIVM1k3pRgaY.b.u55HvX..XUf4Kr8T.rVbhY8FeitCmjki3l2l70Zr4q3KCc0MXf0.2Oo6Mg80Tj.gwa.yZ_sS79Ecp_JSaummtL54K.3FM4vtPI.bBy6O9pqXU5mesTKuK8tUMU5LQFkIK1XLhcCwpu96BXag9EMFXW0Ttvt_2KojLGAv14ul0_28Xy883D8s_AYimofrN..ywdsTSir3_3W1fKiyFIo.EXgo16Qqt8OcwEqE0qIBpkSaEmC4a6qxg8JuqPiIAKNvp_hkn8pmoOEa4yW.614Y
18
+ Received: from [75.62.232.23] by web30801.mail.mud.yahoo.com via HTTP; Thu, 28 May 2009 23:06:40 PDT
19
+ X-Mailer: YahooMailClassic/5.3.9 YahooMailWebService/0.7.289.10
20
+ Date: Thu, 28 May 2009 23:06:40 -0700 (PDT)
21
+ From: Richard L <joe@example.com>
22
+ Subject: Hi
23
+ To: Richard <fred@somewhere.com>
24
+ MIME-Version: 1.0
25
+ Content-Type: multipart/alternative; boundary="0-1875523946-1243577200=:2516"
26
+ X-Assp-Received-RBL: pass
27
+
28
+
29
+ --0-1875523946-1243577200=:2516
30
+ Content-Type: text/plain; charset=us-ascii
31
+
32
+ Hello
33
+
34
+
35
+
36
+ --0-1875523946-1243577200=:2516
37
+ Content-Type: text/html; charset=us-ascii
38
+
39
+ <table cellspacing="0" cellpadding="0" border="0" ><tr><td valign="top" style="font: inherit;">Hello</td></tr></table><br>
40
+
41
+
42
+ --0-1875523946-1243577200=:2516--
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'tmail_bouncer'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+
3
+ class TmailBouncerTest < Test::Unit::TestCase
4
+
5
+ context "Yahoo Bounce" do
6
+ setup do
7
+ @email = read_email('yahoo.eml')
8
+ end
9
+
10
+ should "be a tmail object" do
11
+ assert @email.is_a?(TMail::Mail), "Whoops Im a #{@email.class}"
12
+ end
13
+
14
+ should "return StandardBouncer" do
15
+ assert @email.undeliverable_info.is_a?(TmailBouncer::StandardBouncer) , "Whoops Im a #{@email.class}"
16
+ end
17
+
18
+ should "detect bounced" do
19
+ assert_equal @email.undeliverable_info.status, "Failure"
20
+ end
21
+
22
+ should "detect original sender" do
23
+ assert_equal @email.undeliverable_info.original_sender, "joe@example.com"
24
+ end
25
+
26
+ should "detect original recipient" do
27
+ assert_equal @email.undeliverable_info.original_recipient, "fred@somewhere.com"
28
+ end
29
+
30
+ should "detect original subject" do
31
+ assert_equal @email.undeliverable_info.original_subject, "I like turtles"
32
+ end
33
+
34
+ should "detect original_message_id" do
35
+ assert_equal @email.undeliverable_info.original_message_id, "<1234.1212@example.com>"
36
+ end
37
+
38
+ should "return status of failure" do
39
+ assert_equal "Failure", @email.undeliverable_info.status
40
+ end
41
+ end
42
+
43
+ context "Yahoo Legit Email" do
44
+
45
+ setup do
46
+ @email = read_email('yahoo_legit.eml')
47
+ end
48
+
49
+
50
+ should "be a tmail object" do
51
+ assert @email.is_a?(TMail::Mail), "Whoops Im a #{@email.class}"
52
+ end
53
+
54
+ should "return NoBouce" do
55
+ assert @email.undeliverable_info.is_a?(TmailBouncer::NoBouncer) , "Whoops Im a #{@email.class}"
56
+ end
57
+
58
+ should "return status of Success" do
59
+ assert_equal "Success", @email.undeliverable_info.status
60
+ end
61
+
62
+ end
63
+
64
+
65
+ protected
66
+
67
+ def read_email(name)
68
+ TMail::Mail.parse(File.read('test/fixtures/' + name))
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tangofoxtrot-tmail_bouncer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - tangofoxtrot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-29 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: richard.luther@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - LICENSE
27
+ - README.rdoc
28
+ - Rakefile
29
+ - lib/bouncers/base.rb
30
+ - lib/bouncers/no_bouncer.rb
31
+ - lib/bouncers/standard_bouncer.rb
32
+ - lib/tmail_bouncer.rb
33
+ - test/fixtures/yahoo.eml
34
+ - test/fixtures/yahoo_legit.eml
35
+ - test/test_helper.rb
36
+ - test/unit/tmail_bouncer_test.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/tangofoxtrot/tmail_bouncer
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: TODO
63
+ test_files:
64
+ - test/test_helper.rb
65
+ - test/unit/tmail_bouncer_test.rb