tuktuk 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +14 -1
- data/lib/tuktuk/tuktuk.rb +28 -23
- data/lib/tuktuk/version.rb +2 -2
- metadata +5 -5
data/README.md
CHANGED
@@ -19,10 +19,12 @@ To enable DKIM:
|
|
19
19
|
````
|
20
20
|
require 'tuktuk'
|
21
21
|
|
22
|
-
Tuktuk.
|
22
|
+
Tuktuk.options = {
|
23
|
+
:dkim => {
|
23
24
|
:domain => 'yoursite.com',
|
24
25
|
:selector => 'mailer',
|
25
26
|
:private_key => IO.read('ssl/yoursite.com.key')
|
27
|
+
}
|
26
28
|
}
|
27
29
|
|
28
30
|
email = {
|
@@ -35,6 +37,17 @@ To enable DKIM:
|
|
35
37
|
Tuktuk.deliver(email)
|
36
38
|
````
|
37
39
|
|
40
|
+
Additional options:
|
41
|
+
|
42
|
+
````
|
43
|
+
Tuktuk.options = {
|
44
|
+
:log_to => 'log/mailer.log',
|
45
|
+
:max_attempts => 5,
|
46
|
+
:retry_sleep => 10,
|
47
|
+
:dkim => { ... }
|
48
|
+
}
|
49
|
+
````
|
50
|
+
|
38
51
|
That's all.
|
39
52
|
|
40
53
|
--
|
data/lib/tuktuk/tuktuk.rb
CHANGED
@@ -7,19 +7,22 @@ require 'tuktuk/package'
|
|
7
7
|
DEFAULTS = {
|
8
8
|
:retry_sleep => 10,
|
9
9
|
:max_attempts => 3,
|
10
|
-
:
|
10
|
+
:log_to => nil # $stdout
|
11
11
|
}
|
12
12
|
|
13
13
|
module Tuktuk
|
14
14
|
|
15
|
+
class DNSError < RuntimeError; end
|
16
|
+
class MissingFieldsError < ArgumentError; end
|
17
|
+
|
15
18
|
class << self
|
16
19
|
|
17
20
|
def deliver(message, opts = {})
|
18
21
|
options = opts
|
19
22
|
mail = Package.new(message)
|
20
|
-
mail['X-Mailer'] =
|
21
|
-
lookup_and_deliver(mail)
|
22
|
-
mail
|
23
|
+
mail['X-Mailer'] = "Tuktuk SMTP v#{VERSION}"
|
24
|
+
response = lookup_and_deliver(mail)
|
25
|
+
return response, mail
|
23
26
|
end
|
24
27
|
|
25
28
|
def options=(hash)
|
@@ -30,7 +33,6 @@ module Tuktuk
|
|
30
33
|
end
|
31
34
|
|
32
35
|
def dkim=(dkim_opts)
|
33
|
-
# logger.info "Enabling DKIM for domain #{dkim_opts[:domain]}..."
|
34
36
|
Dkim::domain = dkim_opts[:domain]
|
35
37
|
Dkim::selector = dkim_opts[:selector]
|
36
38
|
Dkim::private_key = dkim_opts[:private_key]
|
@@ -47,11 +49,11 @@ module Tuktuk
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def logger
|
50
|
-
@logger ||= Logger.new(config[:
|
52
|
+
@logger ||= Logger.new(config[:log_to])
|
51
53
|
end
|
52
54
|
|
53
55
|
def get_domain(email_address)
|
54
|
-
email_address && email_address[/@([a-z0-9\._-]+)/i, 1]
|
56
|
+
email_address && email_address.to_s[/@([a-z0-9\._-]+)/i, 1]
|
55
57
|
end
|
56
58
|
|
57
59
|
def success(destination)
|
@@ -64,9 +66,8 @@ module Tuktuk
|
|
64
66
|
sleep config[:retry_sleep]
|
65
67
|
lookup_and_deliver(mail, attempt+1)
|
66
68
|
else
|
67
|
-
|
68
|
-
|
69
|
-
raise "Unable to send to #{to}: #{error_message}"
|
69
|
+
logger.error("#{to} - Unable to send after #{attempt} attempts: #{error.message} [#{error.class.name}]")
|
70
|
+
raise error
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
@@ -75,51 +76,55 @@ module Tuktuk
|
|
75
76
|
if mx = res.mx(domain)
|
76
77
|
mx.sort {|x,y| x.preference <=> y.preference}.map {|rr| rr.exchange}
|
77
78
|
else
|
78
|
-
raise
|
79
|
+
raise DNSError, "Could not locate MX records for domain #{domain}."
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
82
83
|
def lookup_and_deliver(mail, attempt = 1)
|
83
|
-
raise "No destinations found!
|
84
|
+
raise MissingFieldsError, "No destinations found! You need to pass a :to field." if mail.destinations.empty?
|
84
85
|
|
85
|
-
|
86
|
+
response = nil
|
87
|
+
mail.destinations.each do |to|
|
86
88
|
|
87
|
-
domain = get_domain(
|
89
|
+
domain = get_domain(to)
|
88
90
|
servers = smtp_servers_for_domain(domain)
|
89
|
-
error(mail,
|
91
|
+
error(mail, to, DNSError.new("Unknown host: #{domain}")) && next if servers.empty?
|
90
92
|
|
91
93
|
last_error = nil
|
92
94
|
servers.each do |server|
|
93
95
|
begin
|
94
|
-
send_now(mail, server,
|
96
|
+
response = send_now(mail, server, to)
|
95
97
|
break
|
96
98
|
rescue => e
|
97
99
|
last_error = e
|
98
100
|
end
|
99
101
|
end
|
100
|
-
error(mail,
|
102
|
+
error(mail, to, last_error, attempt) if last_error
|
101
103
|
end
|
104
|
+
response
|
102
105
|
end
|
103
106
|
|
104
107
|
def send_now(mail, server, to)
|
105
|
-
|
108
|
+
logger.info "#{to} - Delivering email at #{server}..."
|
106
109
|
|
110
|
+
raw_mail = use_dkim? ? Dkim.sign(mail.to_s).to_s : mail.to_s
|
107
111
|
from = mail.return_path || mail.sender || mail.from_addrs.first
|
108
|
-
|
112
|
+
helo_domain = Dkim::domain || config[:helo_domain] || get_domain(from)
|
109
113
|
|
110
114
|
context = OpenSSL::SSL::SSLContext.new
|
111
115
|
context.verify_mode = OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER
|
112
116
|
|
113
|
-
helo_domain = config[:helo_domain] || Dkim::domain || get_domain(mail[:from])
|
114
|
-
|
115
117
|
smtp = Net::SMTP.new(server, nil)
|
116
118
|
smtp.enable_starttls_auto(context)
|
119
|
+
|
120
|
+
response = nil
|
117
121
|
smtp.start(helo_domain, nil, nil, nil) do |smtp|
|
118
|
-
|
119
|
-
logger.info "#{to} - #{
|
122
|
+
response = smtp.send_message(raw_mail, from, to)
|
123
|
+
logger.info "#{to} - #{response.message.strip}"
|
120
124
|
end
|
121
125
|
|
122
126
|
success(to)
|
127
|
+
response
|
123
128
|
end
|
124
129
|
|
125
130
|
end
|
data/lib/tuktuk/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tuktuk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Tom\xC3\xA1s Pollak"
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-06-
|
18
|
+
date: 2012-06-02 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: bundler
|