tangofoxtrot-tmail_bouncer 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/bouncers/base.rb CHANGED
@@ -1,21 +1,22 @@
1
1
  module TmailBouncer
2
2
  class Base
3
3
  attr_accessor :status_info, :original_message_id, :original_sender, :original_recipient, :original_subject, :handling_server, :status_part, :original_message
4
-
5
4
 
6
5
  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
6
+ @email = email
7
+ end
8
+
9
+ def self.valid_email
10
+ /^[-^!$#%&\'*+\/=?`{|}~\w] # Valid starting character
11
+ [-^!$#%&\'*+\/=?`{|}~.\w]* # Valid succeeding characters
12
+ @
13
+ [a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])* # Domain
14
+ (\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9]))* # Subdomain
15
+ (\.[a-zA-Z]([-a-zA-Z]?[a-zA-Z]))+$/x # TLD (.com, .co.uk)
15
16
  end
16
17
 
17
18
  def self.available_bouncers
18
- @@bouncers ||= [TmailBouncer::StandardBouncer]
19
+ @@bouncers ||= [TmailBouncer::StandardBouncer, TmailBouncer::BoxFullBouncer]
19
20
  end
20
21
 
21
22
 
@@ -30,9 +31,58 @@ module TmailBouncer
30
31
  def self.from_email(email)
31
32
  bounce = determine_bouncer(email)
32
33
  # lets start by trying to extract the message/delivery-status part
33
-
34
34
  bounce
35
35
  end
36
+
37
+ def determine_status_part
38
+ @status_part = @email.parts.detect { |part| part.content_type == "message/delivery-status" }
39
+ @status_part.nil? ? @email.header : parse_status_part
40
+ end
41
+
42
+ def parse_status_part
43
+ statuses = @status_part.body.gsub("\n ","").split(/\n/)
44
+ statuses.inject({}) do |hash,line|
45
+ key,value = line.split(/:/,2)
46
+ hash[key] = value.strip rescue nil
47
+ hash
48
+ end
49
+ end
50
+
51
+ def determine_original_message_part
52
+ @original_untouched = @email.parts.detect do |part|
53
+ part.content_type == "message/rfc822" || part.content_type == "text/rfc822-headers"
54
+ end
55
+ parse_original_message_part
56
+ end
57
+
58
+ def parse_original_message_part
59
+ @email.multipart? ? TMail::Mail.parse(@original_untouched.body) : @email
60
+ end
61
+
62
+ def original_message
63
+ @original_message ||= determine_original_message_part
64
+ end
65
+
66
+ def original_message_id
67
+ @original_message_id ||= self.original_message.message_id
68
+ end
69
+
70
+ def original_subject
71
+ @original_subject ||= self.original_message.subject
72
+ end
73
+
74
+ def original_sender
75
+ @original_sender ||= @email.multipart? ? self.original_message.from.to_a.first : self.status_info['x-original-to'].body
76
+ end
77
+
78
+ def original_recipient
79
+ @original_recipient ||= self.status_info["Final-Recipient"].to_s.gsub(%r%Final-Recipient: %i,"").gsub(%r%rfc822;%i,"").strip
80
+ end
81
+
82
+ def status_info
83
+ @status_info ||= determine_status_part
84
+ end
85
+
36
86
 
37
87
  def status
38
88
  case status_info['Status']
@@ -0,0 +1,23 @@
1
+ module TmailBouncer
2
+ class BoxFullBouncer < Base
3
+
4
+ def self.recognize?(email)
5
+ if email.parts.size == 0
6
+ email.body =~ /quota/
7
+ end
8
+ end
9
+
10
+ def original_recipient
11
+ if @email.multipart?
12
+ @original_recipient ||= self.status_info["Final-Recipient"].to_s.gsub(%r%Final-Recipient: %i,"").gsub(%r%rfc822;%i,"").strip
13
+ else
14
+ @original_recipient ||= self.original_message.body.split(' ').detect {|word| word.match(self.class.valid_email)}.to_s
15
+ end
16
+ end
17
+
18
+ def status
19
+ "Failure"
20
+ end
21
+
22
+ end
23
+ end
@@ -1,10 +1,6 @@
1
1
  module TmailBouncer
2
2
  class NoBouncer < Base
3
3
 
4
- def initialize
5
-
6
- end
7
-
8
4
  def status
9
5
  "Success"
10
6
  end
@@ -7,42 +7,5 @@ module TmailBouncer
7
7
  end
8
8
  end
9
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
10
  end
48
11
  end
data/lib/tmail_bouncer.rb CHANGED
@@ -2,4 +2,5 @@ require 'tmail'
2
2
  require 'bouncers/base'
3
3
  require 'bouncers/standard_bouncer'
4
4
  require 'bouncers/no_bouncer'
5
+ require 'bouncers/box_full_bouncer'
5
6
  TMail::Mail.send :include, TmailBouncer
@@ -0,0 +1,96 @@
1
+ Return-Path: <>
2
+ X-Original-To: joe@example.com
3
+ Delivered-To: postmaster_bounce@example.com
4
+ Received: from omr-d25.mx.aol.com (mail.example.com [1.1.1.1])
5
+ by mail.example.com (Postfix) with ESMTP id D03081784B5
6
+ for <joe@example.com>; Thu, 28 May 2009 16:20:16 -0700 (PDT)
7
+ Received: from omr-d25.mx.aol.com ([205.188.249.69] helo=omr-d25.mx.aol.com)
8
+ by ASSP.nospam; 28 May 2009 16:20:16 -0700
9
+ Received: from rly-dg01.mx.aol.com (rly-dg01.mx.aol.com [205.188.155.81]) by omr-d25.mx.aol.com (v117.7) with ESMTP id MAILOMRD256-7d884a1f1c39a6; Thu, 28 May 2009 19:20:25 -0400
10
+ Received: from localhost (localhost)
11
+ by rly-dg01.mx.aol.com (8.14.1/8.14.1) id n4SNKNdL013113;
12
+ Thu, 28 May 2009 19:20:25 -0400
13
+ Date: Thu, 28 May 2009 19:20:25 -0400
14
+ From: Mail Delivery Subsystem <MAILER-DAEMON@aol.com>
15
+ Message-Id: <200905282320.n4SNKNdL013113@rly-dg01.mx.aol.com>
16
+ To: <joe@example.com>
17
+ MIME-Version: 1.0
18
+ Content-Type: multipart/report; report-type=delivery-status;
19
+ boundary="n4SNKNdL013113.1243552825/rly-dg01.mx.aol.com"
20
+ Subject: Returned mail: see transcript for details
21
+ Auto-Submitted: auto-generated (failure)
22
+ X-AOL-INRLY: mail.example.com [1.1.1.1] rly-dg01
23
+ X-AOL-IP: 205.188.155.81
24
+ X-Assp-Received-RBL: pass
25
+
26
+ This is a MIME-encapsulated message
27
+
28
+ --n4SNKNdL013113.1243552825/rly-dg01.mx.aol.com
29
+
30
+ The original message was received at Thu, 28 May 2009 19:20:04 -0400
31
+ from mail.example.com [1.1.1.1]
32
+
33
+
34
+ *** ATTENTION ***
35
+
36
+ Your e-mail is being returned to you because there was a problem with its
37
+ delivery. The address which was undeliverable is listed in the section
38
+ labeled: "----- The following addresses had permanent fatal errors -----".
39
+
40
+ The reason your mail is being returned to you is listed in the section
41
+ labeled: "----- Transcript of Session Follows -----".
42
+
43
+ The line beginning with "<<<" describes the specific reason your e-mail could
44
+ not be delivered. The next line contains a second error message which is a
45
+ general translation for other e-mail servers.
46
+
47
+ Please direct further questions regarding this message to your e-mail
48
+ administrator.
49
+
50
+ --AOL Postmaster
51
+
52
+
53
+
54
+ ----- The following addresses had permanent fatal errors -----
55
+ <fred@aol.com>
56
+ (reason: 552 bigbassboris MAILBOX FULL)
57
+
58
+ ----- Transcript of session follows -----
59
+ ... while talking to air-dg06.mail.aol.com.:
60
+ >>> RCPT To:<fred@aol.com>
61
+ <<< 552 bigbassboris MAILBOX FULL
62
+ 554 5.0.0 Service unavailable
63
+
64
+ --n4SNKNdL013113.1243552825/rly-dg01.mx.aol.com
65
+ Content-Type: message/delivery-status
66
+
67
+ Reporting-MTA: dns; rly-dg01.mx.aol.com
68
+ Arrival-Date: Thu, 28 May 2009 19:20:04 -0400
69
+
70
+ Final-Recipient: RFC822; fred@aol.com
71
+ Action: failed
72
+ Status: 5.2.2
73
+ Remote-MTA: DNS; air-dg06.mail.aol.com
74
+ Diagnostic-Code: SMTP; 552 bigbassboris MAILBOX FULL
75
+ Last-Attempt-Date: Thu, 28 May 2009 19:20:25 -0400
76
+
77
+ --n4SNKNdL013113.1243552825/rly-dg01.mx.aol.com
78
+ Content-Type: text/rfc822-headers
79
+
80
+ Received: from mail.example.com (mail.example.com [1.1.1.1]) by rly-dg01.mx.aol.com (v123.4) with ESMTP id MAILRELAYINDG013-59e4a1f1c241e7; Thu, 28 May 2009 19:20:04 -0400
81
+ Received: from mail.example.com (example.com [1.1.1.1)
82
+ by mail.example.com (Postfix) with ESMTP id DF6A5178462
83
+ for <fred@aol.com>; Thu, 28 May 2009 16:19:53 -0700 (PDT)
84
+ Date: Thu, 28 May 2009 16:18:36 -0700
85
+ From: joe@example.com
86
+ To: fred@aol.com
87
+ Subject: I like turtles
88
+ Mime-Version: 1.0
89
+ Content-Type: text/html; charset=utf-8
90
+ Message-Id: <1234.1212@example.com>
91
+ X-AOL-IP: 1.1.1.1
92
+ X-AOL-SCOLL-SCORE:1:2:371949056:93952408
93
+ X-AOL-SCOLL-URL_COUNT:5
94
+
95
+ --n4SNKNdL013113.1243552825/rly-dg01.mx.aol.com--
96
+
@@ -0,0 +1,24 @@
1
+ Return-Path: <root@mail.example.com>
2
+ X-Original-To: joe@example.com
3
+ Delivered-To: postmaster_bounce@example.com
4
+ Received: from mail.example.com (mail.example.com [8.12.160.195])
5
+ by mail.example.com (Postfix) with ESMTP id DE32317849E
6
+ for <joe@example.com>; Fri, 29 May 2009 14:33:03 -0700 (PDT)
7
+ Received: from mail.example.com ([64.233.207.26] helo=mail.example.com)
8
+ by ASSP.nospam; 29 May 2009 14:33:03 -0700
9
+ Received: from mail.example.com (localhost.localdomain [127.0.0.1])
10
+ by mail.example.com (8.12.8/8.12.8) with ESMTP id n4TLXDTH003035
11
+ for <joe@example.com>; Fri, 29 May 2009 16:33:13 -0500
12
+ Received: (from root@localhost)
13
+ by mail.example.com (8.12.8/8.12.8/Submit) id n4TLXD6Y003033;
14
+ Fri, 29 May 2009 16:33:13 -0500
15
+ Date: Fri, 29 May 2009 16:33:13 -0500
16
+ Message-Id: <1234.1212@example.com>
17
+ To: joe@example.com
18
+ Subject: I like turtles
19
+ References: <20090529213302.06FB11783A7@mail.example.com>
20
+ In-Reply-To: <20090529213302.06FB11783A7@mail.example.com>
21
+ From: <MAILER-DAEMON@somewhere.com>
22
+ X-Assp-Received-RBL: pass
23
+
24
+ Your message to fred@example.com was rejected due to the user exceeding its mailbox quota
@@ -0,0 +1,147 @@
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
+ context "AOL Bounce" do
66
+ setup do
67
+ @email = read_email('aol.eml')
68
+ end
69
+
70
+ should "be a tmail object" do
71
+ assert @email.is_a?(TMail::Mail), "Whoops Im a #{@email.class}"
72
+ end
73
+
74
+ should "return StandardBouncer" do
75
+ assert @email.undeliverable_info.is_a?(TmailBouncer::StandardBouncer) , "Whoops Im a #{@email.class}"
76
+ end
77
+
78
+ should "detect bounced" do
79
+ assert_equal @email.undeliverable_info.status, "Failure"
80
+ end
81
+
82
+ should "detect original sender" do
83
+ assert_equal @email.undeliverable_info.original_sender, "joe@example.com"
84
+ end
85
+
86
+ should "detect original recipient" do
87
+ assert_equal @email.undeliverable_info.original_recipient, "fred@aol.com"
88
+ end
89
+
90
+ should "detect original subject" do
91
+ assert_equal @email.undeliverable_info.original_subject, "I like turtles"
92
+ end
93
+
94
+ should "detect original_message_id" do
95
+ assert_equal @email.undeliverable_info.original_message_id, "<1234.1212@example.com>"
96
+ end
97
+
98
+ should "return status of failure" do
99
+ assert_equal "Failure", @email.undeliverable_info.status
100
+ end
101
+ end
102
+
103
+ context "Box Full Bounce" do
104
+ setup do
105
+ @email = read_email('box_full.eml')
106
+ end
107
+
108
+ should "be a tmail object" do
109
+ assert @email.is_a?(TMail::Mail), "Whoops Im a #{@email.class}"
110
+ end
111
+
112
+ should "return BoxFullBouncer" do
113
+ assert @email.undeliverable_info.is_a?(TmailBouncer::BoxFullBouncer) , "Whoops Im a #{@email.class}"
114
+ end
115
+
116
+ should "detect bounced" do
117
+ assert_equal @email.undeliverable_info.status, "Failure"
118
+ end
119
+
120
+ should "detect original sender" do
121
+ assert_equal @email.undeliverable_info.original_sender, "joe@example.com"
122
+ end
123
+
124
+ should "detect original recipient" do
125
+ assert_equal @email.undeliverable_info.original_recipient, "fred@example.com"
126
+ end
127
+
128
+ should "detect original subject" do
129
+ assert_equal @email.undeliverable_info.original_subject, "I like turtles"
130
+ end
131
+
132
+ should "detect original_message_id" do
133
+ assert_equal @email.undeliverable_info.original_message_id, "<1234.1212@example.com>"
134
+ end
135
+
136
+ should "return status of failure" do
137
+ assert_equal "Failure", @email.undeliverable_info.status
138
+ end
139
+ end
140
+
141
+
142
+ protected
143
+
144
+ def read_email(name)
145
+ TMail::Mail.parse(File.read('test/fixtures/' + name))
146
+ end
147
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tangofoxtrot-tmail_bouncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - tangofoxtrot
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-29 00:00:00 -07:00
12
+ date: 2009-06-03 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,13 +27,16 @@ files:
27
27
  - README.rdoc
28
28
  - Rakefile
29
29
  - lib/bouncers/base.rb
30
+ - lib/bouncers/box_full_bouncer.rb
30
31
  - lib/bouncers/no_bouncer.rb
31
32
  - lib/bouncers/standard_bouncer.rb
32
33
  - lib/tmail_bouncer.rb
34
+ - test/fixtures/aol.eml
35
+ - test/fixtures/box_full.eml
33
36
  - test/fixtures/yahoo.eml
34
37
  - test/fixtures/yahoo_legit.eml
35
38
  - test/test_helper.rb
36
- - test/unit/tmail_bouncer_test.rb
39
+ - test/unit/test_tmail_bouncer.rb
37
40
  has_rdoc: true
38
41
  homepage: http://github.com/tangofoxtrot/tmail_bouncer
39
42
  post_install_message:
@@ -62,4 +65,4 @@ specification_version: 2
62
65
  summary: TODO
63
66
  test_files:
64
67
  - test/test_helper.rb
65
- - test/unit/tmail_bouncer_test.rb
68
+ - test/unit/test_tmail_bouncer.rb
@@ -1,70 +0,0 @@
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