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.
@@ -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
@@ -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.all
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.all
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.all
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 @@env.eql?"development"
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, :multiple=>true}
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, "ses_proxy_smtp"
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, "ses_proxy_http"
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
- @smtp = Thread.new do
93
- EM.run{ SesProxy::SmtpServer.start smtp_host, smtp_port }
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
 
@@ -8,6 +8,8 @@ module SesProxy
8
8
  field :email, type: String
9
9
  field :type, type: String
10
10
  field :desc, type: String
11
+ field :count, type: Integer
12
+ field :retry_at, type: DateTime
11
13
  field :created_at, type: DateTime
12
14
  field :updated_at, type: DateTime
13
15
  end
@@ -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 => '587',
9
+ :port => 587,
10
10
  :user_name => SesProxy::Conf.get[:aws][:ses][:username],
11
11
  :password => SesProxy::Conf.get[:aws][:ses][:password],
12
- :authentication => :plain,
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 = ses.identities.domains.map(&:identity)
37
- email_addresses = ses.identities.email_addresses.map(&:identity)
38
- identity = nil
39
- if email_addresses.include?(sender)
40
- identity = ses.identities[sender]
41
- elsif domains.include?(sender.split('@').last)
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
- identity&&identity.verified?
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
- actual_recipients.collect!{|address| address unless bld.include?(address.split('@').last)}.compact!
92
- actual_cc_addrs.collect!{|address| address unless bld.include?(address.split('@').last)}.compact!
93
- actual_bcc_addrs.collect!{|address| address unless bld.include?(address.split('@').last)}.compact!
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
- actual_recipients.collect!{|address| address unless blr.map{|regexp| Regexp.new(regexp).match(address)}.compact.any? }.compact!
100
- actual_cc_addrs.collect!{|address| address unless blr.map{|regexp| Regexp.new(regexp).match(address)}.compact.any? }.compact!
101
- actual_bcc_addrs.collect!{|address| address unless blr.map{|regexp| Regexp.new(regexp).match(address)}.compact.any? }.compact!
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.body.decoded,
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.body.decoded,
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
  })
@@ -9,4 +9,5 @@ module SesProxy
9
9
  autoload :Email, 'ses_proxy/models/email'
10
10
  autoload :BouncedEmail, 'ses_proxy/models/bounced_email'
11
11
  autoload :RecipientsNumber, 'ses_proxy/models/recipients_number'
12
+ autoload :VerifiedSender, 'ses_proxy/models/verified_sender'
12
13
  end
@@ -1,5 +1,6 @@
1
1
  :collect_sent_mails: true
2
2
  :collect_bounced_mails: true
3
+ :raise_error_if_no_recipients: false
3
4
  :blacklisted_domains:
4
5
  - domain1.ltd
5
6
  - domain2.ltd
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.1
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: '0'
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: '0'
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
- - ses_proxy.rb
214
- - lib/ses_proxy/sns_endpoint.rb
215
- - lib/ses_proxy/models/recipients_number.rb
216
- - lib/ses_proxy/models/bounced_email.rb
217
- - lib/ses_proxy/models/email.rb
218
- - lib/ses_proxy/models/complaint.rb
219
- - lib/ses_proxy/models/bounce.rb
220
- - lib/ses_proxy/conf.rb
221
- - lib/ses_proxy/smtp_server.rb
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/images/loader.gif
241
- - app/public/css/application.css
242
- - app/public/js/application.js
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/exporting.js
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/adapters/prototype-adapter.js
256
- - app/public/highcharts/adapters/prototype-adapter.src.js
257
- - app/public/highcharts/adapters/mootools-adapter.src.js
258
- - app/public/highcharts/adapters/mootools-adapter.js
259
- - app/public/highcharts/highcharts-more.js
260
- - app/public/highcharts/highcharts.js
261
- - app/public/bootstrap/img/glyphicons-halflings-white.png
262
- - app/public/bootstrap/img/glyphicons-halflings.png
263
- - app/public/bootstrap/css/bootstrap.min.css
264
- - app/public/bootstrap/css/bootstrap-responsive.css
265
- - app/public/bootstrap/css/bootstrap-responsive.min.css
266
- - app/public/bootstrap/css/bootstrap.css
267
- - app/public/bootstrap/js/bootstrap.min.js
268
- - app/public/bootstrap/js/bootstrap.js
269
- - template/ses-proxy.yml
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: 1.8.25
295
+ rubygems_version: 2.4.5.1
293
296
  signing_key:
294
- specification_version: 3
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