minbox 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34bfc6208f399c85b149fb9febe81a997940696a0802d7326aa91aa55261e8d5
4
- data.tar.gz: 77d432c13608ac84e4c9dcee94c018569d52ab9bdf486b08d28f626dd3def4ab
3
+ metadata.gz: 2c500238d8a8cfc853fd4ec9a252a7a0cde7f421e1bd31a4695c312288110e23
4
+ data.tar.gz: 80b489dd81313d68e7e8e8e479060d22dd105b3b0570357eb489340d145ab0d4
5
5
  SHA512:
6
- metadata.gz: fb60ac240af6400731e5b4031ef5d86b73739761ccc82c650a0b931ed3e4e5562fe24ed97b1d5765d01ecae9c3334622dba72b1bed3aa235179bd47ba2a8d9bc
7
- data.tar.gz: 2d5c4b97db3e4b5d1d0621bc0a4af5eb461bd44ba1165362335cf9609bb173301c5498d860e94933be95e611bc7a351d1682f6fa5ac25455bf5348d4d756e5cb
6
+ metadata.gz: 7bd692169477f0e2fe78d1611ac32bdc83cdd9980a8feacf15c6574cd6d471ebe98503e6d049fba23ef991db8d5eaafc960bad31ec38d50e8c47e02efaf3b3a7
7
+ data.tar.gz: 601d89064f6f2189b097615815a6b46f13d4a66989c4138815a9ea5bfb8e56c449aa829d6df10e8ddf065d737dba0520d07b98ddbe9b9f9ac1ea088a79ebe1ce
data/Dockerfile ADDED
@@ -0,0 +1,10 @@
1
+ FROM ruby:2.6-alpine
2
+ ENV PACKAGES build-base tzdata
3
+ RUN apk update && \
4
+ apk upgrade && \
5
+ apk add $PACKAGES && \
6
+ rm -fr /var/cache/apk/* && \
7
+ apk del build-base
8
+ WORKDIR /opt/minbox
9
+ RUN gem install minbox
10
+ CMD ["minbox", "server", "localhost", "25", "--output=stdout file"]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minbox (0.1.0)
4
+ minbox (0.1.1)
5
5
  mail (~> 2.7)
6
6
  redis (~> 4.1)
7
7
  thor (~> 0.20)
@@ -9,7 +9,12 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
+ concurrent-ruby (1.1.4)
12
13
  diff-lcs (1.3)
14
+ faker (1.9.3)
15
+ i18n (>= 0.7)
16
+ i18n (1.6.0)
17
+ concurrent-ruby (~> 1.0)
13
18
  mail (2.7.1)
14
19
  mini_mime (>= 0.1.1)
15
20
  mini_mime (1.0.1)
@@ -35,6 +40,7 @@ PLATFORMS
35
40
 
36
41
  DEPENDENCIES
37
42
  bundler (~> 2.0)
43
+ faker (~> 1.9)
38
44
  minbox!
39
45
  rake (~> 10.0)
40
46
  rspec (~> 3.0)
data/bin/docker-build ADDED
@@ -0,0 +1,14 @@
1
+ #!/bin/sh
2
+
3
+ cp Dockerfile pkg/
4
+ docker pull ruby:2.6-alpine
5
+ docker image build \
6
+ -t "mokhan/minbox:latest" \
7
+ -f pkg/Dockerfile pkg/
8
+
9
+ if [[ -z "${DOCKER_PUSH}" ]]; then
10
+ echo 'skip push'
11
+ else
12
+ echo 'pushing...'
13
+ docker push "mokhan/minbox:latest"
14
+ fi
data/lib/minbox.rb CHANGED
@@ -1,6 +1,10 @@
1
+ require 'base64'
2
+ require 'logger'
1
3
  require 'socket'
2
4
 
5
+ require "minbox/core"
3
6
  require "minbox/publisher"
7
+ require "minbox/client"
4
8
  require "minbox/server"
5
9
  require "minbox/version"
6
10
 
data/lib/minbox/cli.rb CHANGED
@@ -14,18 +14,19 @@ module Minbox
14
14
  from 'Your Name <me@example.org>'
15
15
  to 'Destination Address <them@example.com>'
16
16
  subject 'test message'
17
- body 'This is a test message.'
17
+ body "#{Time.now} This is a test message."
18
18
  end
19
19
  require 'net/smtp'
20
20
  Net::SMTP.start(host, port) do |smtp|
21
- smtp.send_message(mail.to_s, 'me@example.org', 'them@example.com')
21
+ smtp.send_message(mail.to_s, 'me+1@example.org', 'them+1@example.com')
22
+ smtp.send_message(mail.to_s, 'me+2@example.org', 'them+2@example.com')
22
23
  end
23
24
  end
24
25
 
25
26
  method_option :output, type: :array, default: ['stdout']
26
27
  desc 'server <HOST> <PORT>', 'SMTP server'
27
28
  def server(host = 'localhost', port = '25')
28
- publisher = publishers_for(options[:output])
29
+ publisher = Publisher.from(options[:output])
29
30
  Server.new(host, port).listen! do |mail|
30
31
  publisher.publish(mail)
31
32
  end
@@ -35,23 +36,6 @@ module Minbox
35
36
  def version
36
37
  say Minbox::VERSION
37
38
  end
38
-
39
- private
40
-
41
- def publishers_for(output)
42
- publisher = Publisher.new
43
- output.each do |x|
44
- case x
45
- when 'stdout'
46
- publisher.add(LogPublisher.new)
47
- when 'redis'
48
- publisher.add(RedisPublisher.new)
49
- when 'file'
50
- publisher.add(FilePublisher.new)
51
- end
52
- end
53
- publisher
54
- end
55
39
  end
56
40
  end
57
41
  end
@@ -0,0 +1,135 @@
1
+ module Minbox
2
+ class Client
3
+ attr_reader :host, :socket, :logger
4
+
5
+ def initialize(host, socket, logger)
6
+ @host = host
7
+ @logger = logger
8
+ @socket = socket
9
+ end
10
+
11
+ def handle(&block)
12
+ write "220"
13
+ while connected? && (line = read)
14
+ case line
15
+ when /^EHLO/i then ehlo(line)
16
+ when /^HELO/i then helo(line)
17
+ when /^MAIL FROM/i then mail_from(line)
18
+ when /^RCPT TO/i then rcpt_to(line)
19
+ when /^DATA/i then data(line, &block)
20
+ when /^QUIT/i then quit
21
+ when /^STARTTLS/i then start_tls
22
+ when /^RSET/i then reset
23
+ when /^NOOP/i then noop
24
+ when /^AUTH PLAIN/i then auth_plain(line)
25
+ when /^AUTH LOGIN/i then auth_login(line)
26
+ else
27
+ logger.error(line)
28
+ write '502 Invalid/unsupported command'
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def quit
36
+ write "221 Bye"
37
+ close
38
+ end
39
+
40
+ def data(line, &block)
41
+ write "354 End data with <CR><LF>.<CR><LF>"
42
+ body = []
43
+ line = read
44
+ until line.nil? || line.match(/^\.\r\n$/)
45
+ body << line
46
+ line = read
47
+ end
48
+ write "250 OK"
49
+ block.call(Mail.new(body.join))
50
+ end
51
+
52
+ def rcpt_to(line)
53
+ write "250 OK"
54
+ end
55
+
56
+ def mail_from(line)
57
+ write "250 OK"
58
+ end
59
+
60
+ def ehlo(line)
61
+ _ehlo, _client_domain = line.split(" ")
62
+ write "250-#{host}"
63
+ #write "250 AUTH PLAIN LOGIN"
64
+ write "250-ENHANCEDSTATUSCODES"
65
+ write "250 OK"
66
+ end
67
+
68
+ def helo(line)
69
+ _ehlo, _client_domain = line.split(" ")
70
+ write "250 #{host}"
71
+ end
72
+
73
+ def start_tls
74
+ write "502 TLS not available"
75
+ end
76
+
77
+ def reset
78
+ write '250 OK'
79
+ end
80
+
81
+ def noop
82
+ write '250 OK'
83
+ end
84
+
85
+ def auth_plain(line)
86
+ data = line.gsub(/AUTH PLAIN ?/i, '')
87
+ if data.strip == ''
88
+ write '334'
89
+ data = read
90
+ end
91
+ parts = Base64.decode64(data).split("\0")
92
+ username, password = parts[-2], parts[-1]
93
+ logger.debug("#{username}:#{password}")
94
+ return write '535 Authenticated failed - protocol error' unless username && password
95
+ write "235 2.7.0 Authentication successful"
96
+ end
97
+
98
+ def auth_login(line)
99
+ username = line.gsub!(/AUTH LOGIN ?/i, '')
100
+ if username.strip == ''
101
+ write '334 VXNlcm5hbWU6'
102
+ username = read
103
+ write '334 UGFzc3dvcmQ6'
104
+ else
105
+ write '334 UGFzc3dvcmQ6'
106
+ end
107
+ password = Base64.decode64(read)
108
+ logger.debug("#{username}:#{password}")
109
+
110
+ return write '535 Authenticated failed - protocol error' unless username && password
111
+ write "235 2.7.0 Authentication successful"
112
+ end
113
+
114
+ def write(message)
115
+ message = "#{message}\r\n"
116
+ logger.debug("S: #{message.inspect}")
117
+ socket.puts message
118
+ end
119
+
120
+ def read
121
+ line = socket.gets
122
+ logger.debug("C: #{line.inspect}")
123
+ line
124
+ end
125
+
126
+ def close
127
+ socket&.close
128
+ @socket = nil
129
+ end
130
+
131
+ def connected?
132
+ @socket
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,9 @@
1
+ module Minbox
2
+ def self.logger
3
+ @logger ||= Logger.new(STDOUT)
4
+ end
5
+
6
+ def self.logger=(logger)
7
+ @logger = logger
8
+ end
9
+ end
@@ -13,17 +13,35 @@ module Minbox
13
13
  end
14
14
 
15
15
  def publish(mail)
16
- publishers.each { |x| x.publish(mail) }
16
+ Thread.new do
17
+ Minbox.logger.debug("Publishing: #{mail.message_id}")
18
+ publishers.each { |x| x.publish(mail) }
19
+ end
20
+ end
21
+
22
+ def self.from(outputs)
23
+ publisher = Publisher.new
24
+ outputs.each do |x|
25
+ case x
26
+ when 'stdout'
27
+ publisher.add(LogPublisher.new)
28
+ when 'redis'
29
+ publisher.add(RedisPublisher.new)
30
+ when 'file'
31
+ publisher.add(FilePublisher.new)
32
+ end
33
+ end
34
+ publisher
17
35
  end
18
36
  end
19
37
 
20
38
  class LogPublisher
21
- def initialize(logger = STDOUT)
39
+ def initialize(logger = Minbox.logger)
22
40
  @logger = logger
23
41
  end
24
42
 
25
43
  def publish(mail)
26
- @logger.puts mail.to_s
44
+ @logger.debug(mail.to_s)
27
45
  end
28
46
  end
29
47
 
data/lib/minbox/server.rb CHANGED
@@ -1,53 +1,32 @@
1
1
  module Minbox
2
2
  class Server
3
- attr_reader :host, :port
3
+ attr_reader :host, :port, :logger
4
4
 
5
- def initialize(host, port)
5
+ def initialize(host = 'localhost', port = 25, logger = Minbox.logger)
6
6
  @host = host
7
7
  @port = port
8
+ @logger = logger
8
9
  end
9
10
 
10
- def listen!
11
- server = TCPServer.new(port.to_i)
11
+ def listen!(&block)
12
+ logger.debug("Starting server on port #{port}...")
13
+ @server = TCPServer.new(port.to_i)
14
+ logger.debug("Server started!")
15
+
12
16
  loop do
13
- yield handle(server.accept)
17
+ handle(@server.accept, &block)
18
+ rescue StandardError => error
19
+ logger.error(error)
14
20
  end
15
21
  end
16
22
 
17
- def handle(client)
18
- mail = { headers: [], body: [] }
19
-
20
- client.puts "220"
21
- ehlo, _client_domain = client.gets.split(" ")
22
-
23
- if ["HELO", "EHLO"].include?(ehlo)
24
- client.puts "250-#{host}"
25
- client.puts "250 OK"
26
- else
27
- puts 'Ooops...'
28
- client.close
29
- return
30
- end
31
-
32
- data = client.gets
33
- until data.start_with?("DATA")
34
- mail[:headers] << data
35
- client.puts "250 OK"
36
- data = client.gets
37
- end
38
- client.puts "354 End data with <CR><LF>.<CR><LF>"
39
-
40
- data = client.gets
41
- until data.match(/^\.\r\n$/)
42
- mail[:body] << data
43
- data = client.gets
44
- end
45
-
46
- client.puts "250 OK"
47
- client.puts "221 Bye"
48
- client.close
23
+ def handle(socket, &block)
24
+ logger.debug("client connected: #{socket.inspect}")
25
+ Client.new(host, socket, logger).handle(&block)
26
+ end
49
27
 
50
- Mail.new(mail[:body].join)
28
+ def shutdown!
29
+ @server&.close
51
30
  end
52
31
  end
53
32
  end
@@ -1,3 +1,3 @@
1
1
  module Minbox
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/minbox.gemspec CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "redis", "~> 4.1"
39
39
  spec.add_dependency "thor", "~> 0.20"
40
40
  spec.add_development_dependency "bundler", "~> 2.0"
41
+ spec.add_development_dependency "faker", "~> 1.9"
41
42
  spec.add_development_dependency "rake", "~> 10.0"
42
43
  spec.add_development_dependency "rspec", "~> 3.0"
43
44
  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.0
4
+ version: 0.1.1
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-02-27 00:00:00.000000000 Z
11
+ date: 2019-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faker
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -105,16 +119,20 @@ files:
105
119
  - ".gitignore"
106
120
  - ".rspec"
107
121
  - ".travis.yml"
122
+ - Dockerfile
108
123
  - Gemfile
109
124
  - Gemfile.lock
110
125
  - LICENSE.txt
111
126
  - README.md
112
127
  - Rakefile
113
128
  - bin/console
129
+ - bin/docker-build
114
130
  - bin/setup
115
131
  - exe/minbox
116
132
  - lib/minbox.rb
117
133
  - lib/minbox/cli.rb
134
+ - lib/minbox/client.rb
135
+ - lib/minbox/core.rb
118
136
  - lib/minbox/publisher.rb
119
137
  - lib/minbox/server.rb
120
138
  - lib/minbox/version.rb