minbox 0.1.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c500238d8a8cfc853fd4ec9a252a7a0cde7f421e1bd31a4695c312288110e23
4
- data.tar.gz: 80b489dd81313d68e7e8e8e479060d22dd105b3b0570357eb489340d145ab0d4
3
+ metadata.gz: 7aa7df98b9f03f4db8a1971c248579b5f033fef52ff74479c5bababac5ef416f
4
+ data.tar.gz: 30507d43a907f6a4eb730930b88e11b56b99bfdd0f45b63500271efcee3d8a90
5
5
  SHA512:
6
- metadata.gz: 7bd692169477f0e2fe78d1611ac32bdc83cdd9980a8feacf15c6574cd6d471ebe98503e6d049fba23ef991db8d5eaafc960bad31ec38d50e8c47e02efaf3b3a7
7
- data.tar.gz: 601d89064f6f2189b097615815a6b46f13d4a66989c4138815a9ea5bfb8e56c449aa829d6df10e8ddf065d737dba0520d07b98ddbe9b9f9ac1ea088a79ebe1ce
6
+ metadata.gz: ea55e3fd8bd032f1666bdbcf7a6892456502379cbabeac6de95a3572e66a8386f25e2f3ef6027b04ae806710b1359d46b7d149d14af836aa627a3f4e18777865
7
+ data.tar.gz: 0c2ad2a2d4803945869874e5bbd2cd1e6aed03944cdf1a18b774d173569c2a61481a7e0b1a21c385f4e4343346c5ea7f213331314a95e021694ec002a7d41ae3
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ server.pem
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minbox (0.1.1)
4
+ minbox (0.1.2)
5
5
  mail (~> 2.7)
6
6
  redis (~> 4.1)
7
7
  thor (~> 0.20)
data/lib/minbox/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
- require 'thor'
2
1
  require 'mail'
2
+ require 'net/smtp'
3
+ require 'openssl'
4
+ require 'thor'
3
5
 
4
6
  require 'minbox'
5
7
 
@@ -16,18 +18,19 @@ module Minbox
16
18
  subject 'test message'
17
19
  body "#{Time.now} This is a test message."
18
20
  end
19
- require 'net/smtp'
20
21
  Net::SMTP.start(host, port) do |smtp|
22
+ smtp.debug_output= Minbox.logger
21
23
  smtp.send_message(mail.to_s, 'me+1@example.org', 'them+1@example.com')
22
24
  smtp.send_message(mail.to_s, 'me+2@example.org', 'them+2@example.com')
23
25
  end
24
26
  end
25
27
 
26
28
  method_option :output, type: :array, default: ['stdout']
29
+ method_option :tls, type: :boolean, default: false
27
30
  desc 'server <HOST> <PORT>', 'SMTP server'
28
31
  def server(host = 'localhost', port = '25')
29
32
  publisher = Publisher.from(options[:output])
30
- Server.new(host, port).listen! do |mail|
33
+ Server.new(host, port, options[:tls]).listen! do |mail|
31
34
  publisher.publish(mail)
32
35
  end
33
36
  end
data/lib/minbox/client.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  module Minbox
2
2
  class Client
3
- attr_reader :host, :socket, :logger
3
+ attr_reader :server, :socket, :logger
4
4
 
5
- def initialize(host, socket, logger)
6
- @host = host
5
+ def initialize(server, socket, logger)
6
+ @server = server
7
7
  @logger = logger
8
8
  @socket = socket
9
9
  end
10
10
 
11
11
  def handle(&block)
12
- write "220"
12
+ write "220 #{server.host} ESMTP"
13
13
  while connected? && (line = read)
14
14
  case line
15
15
  when /^EHLO/i then ehlo(line)
@@ -28,6 +28,10 @@ module Minbox
28
28
  write '502 Invalid/unsupported command'
29
29
  end
30
30
  end
31
+ close
32
+ rescue Errno::ECONNRESET, Errno::EPIPE => error
33
+ logger.error(error)
34
+ close
31
35
  end
32
36
 
33
37
  private
@@ -46,7 +50,7 @@ module Minbox
46
50
  line = read
47
51
  end
48
52
  write "250 OK"
49
- block.call(Mail.new(body.join))
53
+ block.call(Mail.new(body.join)) unless body.empty?
50
54
  end
51
55
 
52
56
  def rcpt_to(line)
@@ -59,19 +63,25 @@ module Minbox
59
63
 
60
64
  def ehlo(line)
61
65
  _ehlo, _client_domain = line.split(" ")
62
- write "250-#{host}"
63
- #write "250 AUTH PLAIN LOGIN"
66
+ write "250-#{server.host} offers a warm hug of welcome"
67
+ write "250-8BITMIME"
64
68
  write "250-ENHANCEDSTATUSCODES"
69
+ #write "250-STARTTLS"# if server.tls?
70
+ #write "250 AUTH PLAIN LOGIN"
65
71
  write "250 OK"
66
72
  end
67
73
 
68
74
  def helo(line)
69
75
  _ehlo, _client_domain = line.split(" ")
70
- write "250 #{host}"
76
+ write "250 #{server.host}"
71
77
  end
72
78
 
73
79
  def start_tls
74
- write "502 TLS not available"
80
+ write "220 Ready to start TLS"
81
+
82
+ socket = OpenSSL::SSL::SSLSocket.new(@socket, server.ssl_context)
83
+ socket.sync_close = true
84
+ @socket = socket.accept
75
85
  end
76
86
 
77
87
  def reset
data/lib/minbox/server.rb CHANGED
@@ -1,16 +1,23 @@
1
1
  module Minbox
2
2
  class Server
3
- attr_reader :host, :port, :logger
3
+ attr_reader :host, :port, :logger, :key
4
4
 
5
- def initialize(host = 'localhost', port = 25, logger = Minbox.logger)
5
+ def initialize(host = 'localhost', port = 25, tls = false, logger = Minbox.logger)
6
6
  @host = host
7
7
  @port = port
8
8
  @logger = logger
9
+ @tls = tls
10
+ @key = OpenSSL::PKey::RSA.new(2048)
11
+ end
12
+
13
+ def tls?
14
+ @tls
9
15
  end
10
16
 
11
17
  def listen!(&block)
12
18
  logger.debug("Starting server on port #{port}...")
13
19
  @server = TCPServer.new(port.to_i)
20
+ @server = upgrade(@server) if tls?
14
21
  logger.debug("Server started!")
15
22
 
16
23
  loop do
@@ -22,11 +29,56 @@ module Minbox
22
29
 
23
30
  def handle(socket, &block)
24
31
  logger.debug("client connected: #{socket.inspect}")
25
- Client.new(host, socket, logger).handle(&block)
32
+ Client.new(self, socket, logger).handle(&block)
26
33
  end
27
34
 
28
35
  def shutdown!
29
36
  @server&.close
30
37
  end
38
+
39
+ def ssl_context
40
+ @ssl_context ||=
41
+ begin
42
+ ssl_context = OpenSSL::SSL::SSLContext.new
43
+ ssl_context.cert = certificate_for(key)
44
+ ssl_context.key = key
45
+ ssl_context.ssl_version = :TLSv1_2
46
+ ssl_context
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def upgrade(tcp_server)
53
+ server = OpenSSL::SSL::SSLServer.new(tcp_server, ssl_context)
54
+ server.start_immediately = true
55
+ server
56
+ end
57
+
58
+ def certificate_for(private_key)
59
+ certificate = OpenSSL::X509::Certificate.new
60
+ subject = '/C=CA/ST=AB/L=Calgary/O=minbox/OU=development/CN=minbox'
61
+ certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(subject)
62
+ certificate.not_before = Time.now
63
+ certificate.not_after = certificate.not_before + 30 * 24 * 60 * 60 # 30 days
64
+ certificate.public_key = private_key.public_key
65
+ certificate.serial = 1
66
+ certificate.version = 2
67
+ apply_ski_extension_to(certificate)
68
+ certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
69
+ certificate
70
+ end
71
+
72
+ def apply_ski_extension_to(certificate)
73
+ extensions = OpenSSL::X509::ExtensionFactory.new
74
+ extensions.subject_certificate = certificate
75
+ extensions.issuer_certificate = certificate
76
+ certificate.add_extension(
77
+ extensions.create_extension('subjectKeyIdentifier', 'hash', false)
78
+ )
79
+ certificate.add_extension(
80
+ extensions.create_extension('keyUsage', 'keyEncipherment,digitalSignature', true)
81
+ )
82
+ end
31
83
  end
32
84
  end
@@ -1,3 +1,3 @@
1
1
  module Minbox
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-08 00:00:00.000000000 Z
11
+ date: 2019-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -118,6 +118,7 @@ extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
120
120
  - ".rspec"
121
+ - ".ruby-version"
121
122
  - ".travis.yml"
122
123
  - Dockerfile
123
124
  - Gemfile