ses-proxy 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/app/web_panel.rb +14 -6
- data/lib/ses_proxy/main_command.rb +21 -10
- data/lib/ses_proxy/models/bounce.rb +2 -0
- data/lib/ses_proxy/models/verified_sender.rb +30 -0
- data/lib/ses_proxy/smtp_server.rb +75 -23
- data/lib/ses_proxy/sns_endpoint.rb +8 -0
- data/ses_proxy.rb +1 -0
- data/template/ses-proxy.yml +1 -0
- metadata +111 -109
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 115da99c890fae8cdfa6177ead5517f91d95d3e5
|
4
|
+
data.tar.gz: c0781c1ebb4081ad2f6ab3fa5b94d1762f735a2f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e84e6871132cbb045753133d6171050624f9eb84b0a27dab8348aba04f6b3e705ef4ad0fc4df8b37a90116bcb7543d1e80316c252d987b16eaa68e226c1d70f9
|
7
|
+
data.tar.gz: b14a1d445e5a2d0415912e55cab0c156f30a7a5c60a8a239987d61171b05e750462488f0cae516a0eb1e72c70ebc1e7f7273edd9cd47f17e49e44afa36ec1916
|
data/app/web_panel.rb
CHANGED
@@ -2,6 +2,7 @@ require 'sinatra'
|
|
2
2
|
require 'haml'
|
3
3
|
require 'kaminari/sinatra'
|
4
4
|
require 'json'
|
5
|
+
require 'aws-sdk'
|
5
6
|
|
6
7
|
helpers do
|
7
8
|
def protected!
|
@@ -44,7 +45,7 @@ get '/' do
|
|
44
45
|
@per = params[:per] || 20
|
45
46
|
mails = mails_query
|
46
47
|
@mails = mails.page(params[:page]).per(@per)
|
47
|
-
haml :mails
|
48
|
+
haml :mails, :layout => "layout"
|
48
49
|
end
|
49
50
|
|
50
51
|
get '/mails.json' do
|
@@ -77,7 +78,7 @@ get '/bounced_mails' do
|
|
77
78
|
@per = params[:per] || 20
|
78
79
|
mails = bounced_mails_query
|
79
80
|
@mails = mails.page(params[:page]).per(@per)
|
80
|
-
haml :mails
|
81
|
+
haml :mails, :layout => "layout"
|
81
82
|
end
|
82
83
|
|
83
84
|
get '/bounced_mails.json' do
|
@@ -111,7 +112,7 @@ get '/bounces' do
|
|
111
112
|
@per = params[:per] || 20
|
112
113
|
bounces = bounces_query
|
113
114
|
@bounces = bounces.page(params[:page]).per(@per)
|
114
|
-
haml :bounces
|
115
|
+
haml :bounces, :layout => "layout"
|
115
116
|
end
|
116
117
|
|
117
118
|
get '/bounces.json' do
|
@@ -147,6 +148,13 @@ get '/bounces.json' do
|
|
147
148
|
data.to_json
|
148
149
|
end
|
149
150
|
|
151
|
+
get "/refresh_senders" do
|
152
|
+
protected!
|
153
|
+
ses = ::AWS::SimpleEmailService.new(SesProxy::Conf.get[:aws])
|
154
|
+
SesProxy::VerifiedSender.update_identities(ses.client)
|
155
|
+
204
|
156
|
+
end
|
157
|
+
|
150
158
|
private
|
151
159
|
|
152
160
|
def get_key_format(d_array)
|
@@ -175,7 +183,7 @@ def mails_query
|
|
175
183
|
if valid_date(@s) and valid_date(@e)
|
176
184
|
mails = SesProxy::Email.where(:created_at=>{'$gte' => string_to_date(@s),'$lte' => string_to_date(@e)+1.day})
|
177
185
|
else
|
178
|
-
mails = SesProxy::Email
|
186
|
+
mails = SesProxy::Email
|
179
187
|
end
|
180
188
|
else
|
181
189
|
if valid_date(@s) and valid_date(@e)
|
@@ -194,7 +202,7 @@ def bounced_mails_query
|
|
194
202
|
if valid_date(@s) and valid_date(@e)
|
195
203
|
mails = SesProxy::BouncedEmail.where(:created_at=>{'$gte' => string_to_date(@s),'$lte' => string_to_date(@e)+1.day})
|
196
204
|
else
|
197
|
-
mails = SesProxy::BouncedEmail
|
205
|
+
mails = SesProxy::BouncedEmail
|
198
206
|
end
|
199
207
|
else
|
200
208
|
if valid_date(@s) and valid_date(@e)
|
@@ -211,7 +219,7 @@ def bounces_query
|
|
211
219
|
if valid_date(@s) and valid_date(@e)
|
212
220
|
bounces = SesProxy::Bounce.where(:created_at=>{'$gte' => string_to_date(@s),'$lte' => string_to_date(@e)+1.day})
|
213
221
|
else
|
214
|
-
bounces = SesProxy::Bounce
|
222
|
+
bounces = SesProxy::Bounce
|
215
223
|
end
|
216
224
|
bounces
|
217
225
|
end
|
@@ -65,9 +65,19 @@ module SesProxy
|
|
65
65
|
|
66
66
|
@@env = environment
|
67
67
|
Mongoid.load! mongoid_config_file, @@env
|
68
|
+
if @@env.eql?"development"
|
69
|
+
Mongoid.logger.level = Logger::DEBUG
|
70
|
+
Mongo::Logger.logger.level = Logger::DEBUG if defined?(Mongo)
|
71
|
+
else
|
72
|
+
Mongoid.logger.level = Logger::INFO
|
73
|
+
Mongo::Logger.logger.level = Logger::INFO if defined?(Mongo)
|
74
|
+
end
|
75
|
+
|
76
|
+
ses = AWS::SimpleEmailService.new(SesProxy::Conf.get[:aws])
|
77
|
+
VerifiedSender.update_identities ses.client
|
68
78
|
|
69
79
|
app = Rack::Builder.new do
|
70
|
-
use Rack::Reloader, 0 if
|
80
|
+
use Rack::Reloader, 0 if @@env.eql?"development"
|
71
81
|
map "/sns_endpoint" do
|
72
82
|
run SesProxy::SnsEndpoint.new
|
73
83
|
end
|
@@ -76,27 +86,28 @@ module SesProxy
|
|
76
86
|
options = {:app=>app, :environment=>environment, :server=>"thin", :Port=>http_port, :Host=>http_address}
|
77
87
|
server = Rack::Server.new options
|
78
88
|
|
89
|
+
SesProxy::SmtpServer.parms = {:auth => :required}
|
79
90
|
if demonize?
|
80
|
-
options = {:app_name => "ses_proxy", :dir_mode=>:normal, :dir=>pid_dir
|
91
|
+
options = {:app_name => "ses_proxy", :dir_mode=>:normal, :dir=>pid_dir}
|
81
92
|
group = Daemons::ApplicationGroup.new('ses_proxy', options)
|
82
93
|
options[:mode] = :proc
|
83
94
|
options[:proc] = Proc.new { EM.run{ SesProxy::SmtpServer.start smtp_host, smtp_port } }
|
84
|
-
pid = Daemons::PidFile.new pid_dir, "
|
95
|
+
pid = Daemons::PidFile.new pid_dir, "ses_proxy_num.0"
|
85
96
|
@smtp = Daemons::Application.new(group, options, pid)
|
86
97
|
options[:proc] = Proc.new { server.start }
|
87
|
-
pid = Daemons::PidFile.new pid_dir, "
|
98
|
+
pid = Daemons::PidFile.new pid_dir, "ses_proxy_num.1"
|
88
99
|
@http = Daemons::Application.new(group, options, pid)
|
89
100
|
@smtp.start
|
90
101
|
@http.start
|
91
102
|
else
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
@http = Thread.new do
|
103
|
+
EM.run do
|
104
|
+
SesProxy::SmtpServer.start smtp_host, smtp_port
|
96
105
|
server.start
|
106
|
+
trap(:INT) {
|
107
|
+
SesProxy::SmtpServer.stop
|
108
|
+
EM.stop
|
109
|
+
}
|
97
110
|
end
|
98
|
-
@smtp.join
|
99
|
-
@http.join
|
100
111
|
end
|
101
112
|
end
|
102
113
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
module SesProxy
|
4
|
+
class VerifiedSender
|
5
|
+
include Mongoid::Document
|
6
|
+
store_in collection: "verified_senders"
|
7
|
+
|
8
|
+
field :ses_identity, type: String
|
9
|
+
field :type, type: String
|
10
|
+
field :created_at, type: DateTime
|
11
|
+
field :updated_at, type: DateTime
|
12
|
+
|
13
|
+
def self.update_identities(client)
|
14
|
+
print "Update verified identities... "
|
15
|
+
resp = client.list_identities
|
16
|
+
identities = resp.data[:identities]
|
17
|
+
resp = client.get_identity_verification_attributes :identities => identities
|
18
|
+
VerifiedSender.delete_all
|
19
|
+
_resp = {:emails => [], :domains => []}.with_indifferent_access
|
20
|
+
resp[:verification_attributes].each do |identity, attributes|
|
21
|
+
_type = identity.match('@') ? 'email' : 'domain'
|
22
|
+
next unless attributes[:verification_status].eql? "Success"
|
23
|
+
_resp[_type.pluralize] << VerifiedSender.create({:ses_identity => identity, :type => _type, :created_at => Time.now, :updated_at => Time.now})
|
24
|
+
end
|
25
|
+
_resp
|
26
|
+
puts "OK"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -6,10 +6,10 @@ if SesProxy::Conf.get[:aws][:ses] and SesProxy::Conf.get[:aws][:ses][:username]
|
|
6
6
|
Mail.defaults do
|
7
7
|
delivery_method :smtp, {
|
8
8
|
:address => 'email-smtp.us-east-1.amazonaws.com',
|
9
|
-
:port =>
|
9
|
+
:port => 587,
|
10
10
|
:user_name => SesProxy::Conf.get[:aws][:ses][:username],
|
11
11
|
:password => SesProxy::Conf.get[:aws][:ses][:password],
|
12
|
-
:authentication => :
|
12
|
+
:authentication => :login,
|
13
13
|
:enable_starttls_auto => true
|
14
14
|
}
|
15
15
|
end
|
@@ -33,15 +33,16 @@ module SesProxy
|
|
33
33
|
def receive_sender(sender)
|
34
34
|
sender = sender.gsub(/[<>]/,'')
|
35
35
|
@sender = sender
|
36
|
-
domains =
|
37
|
-
email_addresses =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
identity = ses.identities[sender.split('@').last]
|
36
|
+
domains = VerifiedSender.where({:type=>'domain'}).map(&:ses_identity)
|
37
|
+
email_addresses = VerifiedSender.where({:type=>'email'}).map(&:ses_identity)
|
38
|
+
if domains.empty? or email_addresses.empty?
|
39
|
+
resp = VerifiedSender.update_identities ses.client
|
40
|
+
domains = resp[:domains].map(&:ses_identity)
|
41
|
+
email_addresses = resp[:emails].map(&:ses_identity)
|
43
42
|
end
|
44
|
-
|
43
|
+
ok = (email_addresses.include?(sender) or domains.include?(sender.split('@').last))
|
44
|
+
puts "Invalid sender!" unless ok
|
45
|
+
return ok
|
45
46
|
end
|
46
47
|
|
47
48
|
def receive_recipient(recipient)
|
@@ -76,9 +77,8 @@ module SesProxy
|
|
76
77
|
|
77
78
|
def receive_message
|
78
79
|
return false unless verified
|
79
|
-
mail = Mail.read_from_string(message)
|
80
|
-
bounced = Bounce.where({:email=>{"$in"=>recipients}}).map(&:email)
|
81
|
-
#TODO: Define policy for retry when bounce is not permanent
|
80
|
+
mail = Mail.read_from_string(message + "\n") #https://github.com/mikel/mail/issues/612#issuecomment-35564404
|
81
|
+
bounced = Bounce.where({:email=>{"$in"=>recipients}, :count=>{"$gte"=>2}, :retry_at=>{"$gte"=>Time.now}}).map(&:email)
|
82
82
|
|
83
83
|
#Remove bounced addresses
|
84
84
|
actual_recipients = mail.to_addrs - bounced
|
@@ -88,21 +88,23 @@ module SesProxy
|
|
88
88
|
#Remove blacklisted domains
|
89
89
|
if SesProxy::Conf.get[:blacklisted_domains] and SesProxy::Conf.get[:blacklisted_domains].any?
|
90
90
|
bld = SesProxy::Conf.get[:blacklisted_domains]
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
_proc = proc {|address| address unless bld.include?(address.split('@').last)}
|
92
|
+
actual_recipients.collect!(&_proc).compact!
|
93
|
+
actual_cc_addrs.collect!(&_proc).compact!
|
94
|
+
actual_bcc_addrs.collect!(&_proc).compact!
|
94
95
|
end
|
95
96
|
|
96
97
|
#Remove blacklisted regexp
|
97
98
|
if SesProxy::Conf.get[:blacklisted_regexp] and SesProxy::Conf.get[:blacklisted_regexp].any?
|
98
99
|
blr = SesProxy::Conf.get[:blacklisted_regexp]
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
_proc = proc {|address| address unless blr.map{|regexp| Regexp.new(regexp).match(address)}.compact.any? }
|
101
|
+
actual_recipients.collect!(&_proc).compact!
|
102
|
+
actual_cc_addrs.collect!(&_proc).compact!
|
103
|
+
actual_bcc_addrs.collect!(&_proc).compact!
|
102
104
|
end
|
103
105
|
|
104
106
|
original_number = recipients.size
|
105
|
-
filtered_number = actual_recipients.size+actual_cc_addrs.size+actual_bcc_addrs.size
|
107
|
+
filtered_number = actual_recipients.size + actual_cc_addrs.size + actual_bcc_addrs.size
|
106
108
|
record = RecipientsNumber.new({
|
107
109
|
:original=>original_number,
|
108
110
|
:filtered=>filtered_number,
|
@@ -114,12 +116,13 @@ module SesProxy
|
|
114
116
|
mail.to = actual_recipients.uniq.join(",")
|
115
117
|
mail.cc = actual_cc_addrs.uniq.join(",")
|
116
118
|
mail.bcc = actual_bcc_addrs.uniq.join(",")
|
119
|
+
mail['X-SES-Proxy'] = 'True'
|
117
120
|
unless SesProxy::Conf.get[:collect_sent_mails].eql? false
|
118
121
|
record = Email.new({
|
119
122
|
:sender => sender,
|
120
123
|
:recipients => actual_recipients.uniq.join(","),
|
121
124
|
:subject => mail.subject,
|
122
|
-
:body => mail.
|
125
|
+
:body => mail.decode_body,
|
123
126
|
:system => mail['X-Sender-System']||"Unknown",
|
124
127
|
:created_at => Time.now,
|
125
128
|
:updated_at => Time.now
|
@@ -149,7 +152,7 @@ module SesProxy
|
|
149
152
|
:sender => sender,
|
150
153
|
:recipients => (recipients&bounced).uniq.join(","),
|
151
154
|
:subject => mail.subject,
|
152
|
-
:body => mail.
|
155
|
+
:body => mail.decode_body,
|
153
156
|
:system => mail['X-Sender-System']||"Unknown",
|
154
157
|
:created_at => Time.now,
|
155
158
|
:updated_at => Time.now
|
@@ -157,7 +160,11 @@ module SesProxy
|
|
157
160
|
record.save!
|
158
161
|
end
|
159
162
|
end
|
160
|
-
true
|
163
|
+
if actual_recipients.empty? and SesProxy::Conf.get[:raise_error_if_no_recipients].eql?(true)
|
164
|
+
return [550, "Message rejected because of all recipients are bounced!"]
|
165
|
+
else
|
166
|
+
return true
|
167
|
+
end
|
161
168
|
end
|
162
169
|
|
163
170
|
def receive_ehlo_domain(domain)
|
@@ -181,5 +188,50 @@ module SesProxy
|
|
181
188
|
def self.running?
|
182
189
|
!!@server
|
183
190
|
end
|
191
|
+
|
192
|
+
# Allow EM::Protocols::SmtpServer#receive_message to return an Array so that cases other than succeed/fail can be handled.
|
193
|
+
# If an Array is returned it is expected to have two elements, the response code and the message (e.g. [451, "Temp Failure"]).
|
194
|
+
# Does not break backward compatibility.
|
195
|
+
def process_data_line ln
|
196
|
+
if ln == "."
|
197
|
+
if @databuffer.length > 0
|
198
|
+
receive_data_chunk @databuffer
|
199
|
+
@databuffer.clear
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
succeeded = proc {
|
204
|
+
send_data "250 Message accepted\r\n"
|
205
|
+
reset_protocol_state
|
206
|
+
}
|
207
|
+
failed = proc {
|
208
|
+
send_data "550 Message rejected\r\n"
|
209
|
+
reset_protocol_state
|
210
|
+
}
|
211
|
+
|
212
|
+
d = receive_message
|
213
|
+
|
214
|
+
if d.respond_to?(:set_deferred_status)
|
215
|
+
d.callback(&succeeded)
|
216
|
+
d.errback(&failed)
|
217
|
+
elsif d.kind_of?(Array)
|
218
|
+
send_data d.join(' ') + "\r\n"
|
219
|
+
reset_protocol_state
|
220
|
+
else
|
221
|
+
(d ? succeeded : failed).call
|
222
|
+
end
|
223
|
+
|
224
|
+
@state.delete :data
|
225
|
+
else
|
226
|
+
# slice off leading . if any
|
227
|
+
ln.slice!(0...1) if ln[0] == ?.
|
228
|
+
@databuffer << ln
|
229
|
+
if @databuffer.length > @@parms[:chunksize]
|
230
|
+
receive_data_chunk @databuffer
|
231
|
+
@databuffer.clear
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
184
236
|
end
|
185
237
|
end
|
@@ -4,6 +4,7 @@ require 'aws-sdk'
|
|
4
4
|
require 'net/http'
|
5
5
|
require 'base64'
|
6
6
|
require 'openssl'
|
7
|
+
require 'active_support/all'
|
7
8
|
|
8
9
|
module SesProxy
|
9
10
|
class SnsEndpoint
|
@@ -43,6 +44,12 @@ module SesProxy
|
|
43
44
|
if message["notificationType"].eql? "Bounce"
|
44
45
|
message["bounce"]["bouncedRecipients"].each do |recipient|
|
45
46
|
if record = Bounce.where(:email => recipient["emailAddress"]).first
|
47
|
+
record.count ||= 0
|
48
|
+
record.count += 1
|
49
|
+
if record.count >= 2
|
50
|
+
record.retry_at ||= Time.now
|
51
|
+
record.retry_at = record.retry_at.to_time + ((2 ** (record.count - 2)) * 7).days
|
52
|
+
end
|
46
53
|
record.updated_at = Time.now
|
47
54
|
record.save!
|
48
55
|
else
|
@@ -50,6 +57,7 @@ module SesProxy
|
|
50
57
|
:email => recipient["emailAddress"],
|
51
58
|
:type => message["bounce"]["bounceType"],
|
52
59
|
:desc => recipient["diagnosticCode"],
|
60
|
+
:count => 1,
|
53
61
|
:created_at => Time.now,
|
54
62
|
:updated_at => Time.now
|
55
63
|
})
|
data/ses_proxy.rb
CHANGED
data/template/ses-proxy.yml
CHANGED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ses-proxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Massimo Maino
|
@@ -14,195 +13,199 @@ dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: clamp
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: json
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rack
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ">="
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: thin
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - ">="
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :runtime
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - ">="
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: aws-sdk
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - '='
|
84
74
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
75
|
+
version: 1.66.0
|
86
76
|
type: :runtime
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - '='
|
92
81
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
82
|
+
version: 1.66.0
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: mail
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
|
-
- -
|
87
|
+
- - ">="
|
100
88
|
- !ruby/object:Gem::Version
|
101
89
|
version: '0'
|
102
90
|
type: :runtime
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
|
-
- -
|
94
|
+
- - ">="
|
108
95
|
- !ruby/object:Gem::Version
|
109
96
|
version: '0'
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
98
|
name: eventmachine
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
|
-
- -
|
101
|
+
- - ">="
|
116
102
|
- !ruby/object:Gem::Version
|
117
103
|
version: '0'
|
118
104
|
type: :runtime
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
|
-
- -
|
108
|
+
- - ">="
|
124
109
|
- !ruby/object:Gem::Version
|
125
110
|
version: '0'
|
126
111
|
- !ruby/object:Gem::Dependency
|
127
112
|
name: mongoid
|
128
113
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
114
|
requirements:
|
131
|
-
- -
|
115
|
+
- - ">="
|
132
116
|
- !ruby/object:Gem::Version
|
133
117
|
version: '0'
|
134
118
|
type: :runtime
|
135
119
|
prerelease: false
|
136
120
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
121
|
requirements:
|
139
|
-
- -
|
122
|
+
- - ">="
|
140
123
|
- !ruby/object:Gem::Version
|
141
124
|
version: '0'
|
142
125
|
- !ruby/object:Gem::Dependency
|
143
126
|
name: haml
|
144
127
|
requirement: !ruby/object:Gem::Requirement
|
145
|
-
none: false
|
146
128
|
requirements:
|
147
|
-
- -
|
129
|
+
- - ">="
|
148
130
|
- !ruby/object:Gem::Version
|
149
131
|
version: '0'
|
150
132
|
type: :runtime
|
151
133
|
prerelease: false
|
152
134
|
version_requirements: !ruby/object:Gem::Requirement
|
153
|
-
none: false
|
154
135
|
requirements:
|
155
|
-
- -
|
136
|
+
- - ">="
|
156
137
|
- !ruby/object:Gem::Version
|
157
138
|
version: '0'
|
158
139
|
- !ruby/object:Gem::Dependency
|
159
140
|
name: sinatra
|
160
141
|
requirement: !ruby/object:Gem::Requirement
|
161
|
-
none: false
|
162
142
|
requirements:
|
163
|
-
- -
|
143
|
+
- - ">="
|
164
144
|
- !ruby/object:Gem::Version
|
165
145
|
version: '0'
|
166
146
|
type: :runtime
|
167
147
|
prerelease: false
|
168
148
|
version_requirements: !ruby/object:Gem::Requirement
|
169
|
-
none: false
|
170
149
|
requirements:
|
171
|
-
- -
|
150
|
+
- - ">="
|
172
151
|
- !ruby/object:Gem::Version
|
173
152
|
version: '0'
|
174
153
|
- !ruby/object:Gem::Dependency
|
175
154
|
name: kaminari
|
176
155
|
requirement: !ruby/object:Gem::Requirement
|
177
|
-
none: false
|
178
156
|
requirements:
|
179
|
-
- -
|
157
|
+
- - ">="
|
180
158
|
- !ruby/object:Gem::Version
|
181
159
|
version: '0'
|
182
160
|
type: :runtime
|
183
161
|
prerelease: false
|
184
162
|
version_requirements: !ruby/object:Gem::Requirement
|
185
|
-
none: false
|
186
163
|
requirements:
|
187
|
-
- -
|
164
|
+
- - ">="
|
188
165
|
- !ruby/object:Gem::Version
|
189
166
|
version: '0'
|
190
167
|
- !ruby/object:Gem::Dependency
|
191
168
|
name: padrino-helpers
|
192
169
|
requirement: !ruby/object:Gem::Requirement
|
193
|
-
none: false
|
194
170
|
requirements:
|
195
|
-
- -
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: daemons
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
196
186
|
- !ruby/object:Gem::Version
|
197
187
|
version: '0'
|
198
188
|
type: :runtime
|
199
189
|
prerelease: false
|
200
190
|
version_requirements: !ruby/object:Gem::Requirement
|
201
|
-
none: false
|
202
191
|
requirements:
|
203
|
-
- -
|
192
|
+
- - ">="
|
204
193
|
- !ruby/object:Gem::Version
|
205
194
|
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: actionpack
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - '='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 3.2.16
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - '='
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 3.2.16
|
206
209
|
description:
|
207
210
|
email: maintux@gmail.com
|
208
211
|
executables:
|
@@ -210,88 +213,87 @@ executables:
|
|
210
213
|
extensions: []
|
211
214
|
extra_rdoc_files: []
|
212
215
|
files:
|
213
|
-
-
|
214
|
-
-
|
215
|
-
-
|
216
|
-
-
|
217
|
-
-
|
218
|
-
-
|
219
|
-
-
|
220
|
-
-
|
221
|
-
-
|
222
|
-
- lib/ses_proxy/main_command.rb
|
223
|
-
- bin/ses_proxy
|
224
|
-
- app/web_panel.rb
|
225
|
-
- app/views/_search_form.haml
|
226
|
-
- app/views/bounces.haml
|
227
|
-
- app/views/kaminari/_gap.html.erb
|
228
|
-
- app/views/kaminari/_prev_page.html.erb
|
229
|
-
- app/views/kaminari/_page.html.erb
|
230
|
-
- app/views/kaminari/_next_page.html.erb
|
231
|
-
- app/views/kaminari/_paginator.html.erb
|
232
|
-
- app/views/kaminari/_first_page.html.erb
|
233
|
-
- app/views/kaminari/_last_page.html.erb
|
234
|
-
- app/views/layout.haml
|
235
|
-
- app/views/mails.haml
|
236
|
-
- app/views/_chart.haml
|
237
|
-
- app/public/datepicker/less/datepicker.less
|
216
|
+
- app/public/bootstrap/css/bootstrap-responsive.css
|
217
|
+
- app/public/bootstrap/css/bootstrap-responsive.min.css
|
218
|
+
- app/public/bootstrap/css/bootstrap.css
|
219
|
+
- app/public/bootstrap/css/bootstrap.min.css
|
220
|
+
- app/public/bootstrap/img/glyphicons-halflings-white.png
|
221
|
+
- app/public/bootstrap/img/glyphicons-halflings.png
|
222
|
+
- app/public/bootstrap/js/bootstrap.js
|
223
|
+
- app/public/bootstrap/js/bootstrap.min.js
|
224
|
+
- app/public/css/application.css
|
238
225
|
- app/public/datepicker/css/datepicker.css
|
239
226
|
- app/public/datepicker/js/bootstrap-datepicker.js
|
240
|
-
- app/public/
|
241
|
-
- app/public/
|
242
|
-
- app/public/
|
227
|
+
- app/public/datepicker/less/datepicker.less
|
228
|
+
- app/public/highcharts/adapters/mootools-adapter.js
|
229
|
+
- app/public/highcharts/adapters/mootools-adapter.src.js
|
230
|
+
- app/public/highcharts/adapters/prototype-adapter.js
|
231
|
+
- app/public/highcharts/adapters/prototype-adapter.src.js
|
232
|
+
- app/public/highcharts/highcharts-more.js
|
233
|
+
- app/public/highcharts/highcharts.js
|
234
|
+
- app/public/highcharts/highcharts.src.js
|
243
235
|
- app/public/highcharts/modules/canvas-tools.js
|
244
|
-
- app/public/highcharts/modules/
|
245
|
-
- app/public/highcharts/modules/data.src.js
|
236
|
+
- app/public/highcharts/modules/canvas-tools.src.js
|
246
237
|
- app/public/highcharts/modules/data.js
|
238
|
+
- app/public/highcharts/modules/data.src.js
|
239
|
+
- app/public/highcharts/modules/exporting.js
|
247
240
|
- app/public/highcharts/modules/exporting.src.js
|
248
|
-
- app/public/highcharts/modules/canvas-tools.src.js
|
249
|
-
- app/public/highcharts/highcharts.src.js
|
250
|
-
- app/public/highcharts/themes/gray.js
|
251
|
-
- app/public/highcharts/themes/skies.js
|
252
241
|
- app/public/highcharts/themes/dark-blue.js
|
253
|
-
- app/public/highcharts/themes/grid.js
|
254
242
|
- app/public/highcharts/themes/dark-green.js
|
255
|
-
- app/public/highcharts/
|
256
|
-
- app/public/highcharts/
|
257
|
-
- app/public/highcharts/
|
258
|
-
- app/public/
|
259
|
-
- app/public/
|
260
|
-
- app/
|
261
|
-
- app/
|
262
|
-
- app/
|
263
|
-
- app/
|
264
|
-
- app/
|
265
|
-
- app/
|
266
|
-
- app/
|
267
|
-
- app/
|
268
|
-
- app/
|
269
|
-
-
|
243
|
+
- app/public/highcharts/themes/gray.js
|
244
|
+
- app/public/highcharts/themes/grid.js
|
245
|
+
- app/public/highcharts/themes/skies.js
|
246
|
+
- app/public/images/loader.gif
|
247
|
+
- app/public/js/application.js
|
248
|
+
- app/views/_chart.haml
|
249
|
+
- app/views/_search_form.haml
|
250
|
+
- app/views/bounces.haml
|
251
|
+
- app/views/kaminari/_first_page.html.erb
|
252
|
+
- app/views/kaminari/_gap.html.erb
|
253
|
+
- app/views/kaminari/_last_page.html.erb
|
254
|
+
- app/views/kaminari/_next_page.html.erb
|
255
|
+
- app/views/kaminari/_page.html.erb
|
256
|
+
- app/views/kaminari/_paginator.html.erb
|
257
|
+
- app/views/kaminari/_prev_page.html.erb
|
258
|
+
- app/views/layout.haml
|
259
|
+
- app/views/mails.haml
|
260
|
+
- app/web_panel.rb
|
261
|
+
- bin/ses_proxy
|
262
|
+
- lib/ses_proxy/conf.rb
|
263
|
+
- lib/ses_proxy/main_command.rb
|
264
|
+
- lib/ses_proxy/models/bounce.rb
|
265
|
+
- lib/ses_proxy/models/bounced_email.rb
|
266
|
+
- lib/ses_proxy/models/complaint.rb
|
267
|
+
- lib/ses_proxy/models/email.rb
|
268
|
+
- lib/ses_proxy/models/recipients_number.rb
|
269
|
+
- lib/ses_proxy/models/verified_sender.rb
|
270
|
+
- lib/ses_proxy/smtp_server.rb
|
271
|
+
- lib/ses_proxy/sns_endpoint.rb
|
272
|
+
- ses_proxy.rb
|
270
273
|
- template/mongoid.yml
|
274
|
+
- template/ses-proxy.yml
|
271
275
|
homepage: https://github.com/maintux/ses-proxy
|
272
276
|
licenses:
|
273
277
|
- MIT
|
278
|
+
metadata: {}
|
274
279
|
post_install_message:
|
275
280
|
rdoc_options: []
|
276
281
|
require_paths:
|
277
282
|
- lib
|
278
283
|
required_ruby_version: !ruby/object:Gem::Requirement
|
279
|
-
none: false
|
280
284
|
requirements:
|
281
|
-
- -
|
285
|
+
- - ">="
|
282
286
|
- !ruby/object:Gem::Version
|
283
287
|
version: '0'
|
284
288
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
285
|
-
none: false
|
286
289
|
requirements:
|
287
|
-
- -
|
290
|
+
- - ">="
|
288
291
|
- !ruby/object:Gem::Version
|
289
292
|
version: '0'
|
290
293
|
requirements: []
|
291
294
|
rubyforge_project:
|
292
|
-
rubygems_version:
|
295
|
+
rubygems_version: 2.4.5.1
|
293
296
|
signing_key:
|
294
|
-
specification_version:
|
297
|
+
specification_version: 4
|
295
298
|
summary: SMTP Proxy for Amazon Simple Email Service with bounce and complaints support
|
296
299
|
test_files: []
|
297
|
-
has_rdoc: false
|