ses-proxy 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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