malm 0.0.6 → 0.1.0

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.
data/Gemfile CHANGED
@@ -5,9 +5,6 @@ gem "clamp", "0.2.1"
5
5
  gem "sinatra", "1.2.6"
6
6
  gem "thin", "1.2.11"
7
7
  gem "mail", "2.3.0"
8
- gem "therubyracer", "0.9.2"
9
- gem "execjs", "1.2.0"
10
- gem "coffee-script", "2.2.0"
11
8
  gem "daemons", "1.1.4"
12
9
 
13
10
  group :development do
data/Gemfile.lock CHANGED
@@ -2,15 +2,9 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  clamp (0.2.1)
5
- coffee-script (2.2.0)
6
- coffee-script-source
7
- execjs
8
- coffee-script-source (1.1.1)
9
5
  daemons (1.1.4)
10
6
  diff-lcs (1.1.2)
11
7
  eventmachine (0.12.10)
12
- execjs (1.2.0)
13
- multi_json (~> 1.0)
14
8
  git (1.2.5)
15
9
  i18n (0.6.0)
16
10
  jeweler (1.6.4)
@@ -18,13 +12,11 @@ GEM
18
12
  git (>= 1.2.5)
19
13
  rake
20
14
  json (1.5.3)
21
- libv8 (3.3.10.2)
22
15
  mail (2.3.0)
23
16
  i18n (>= 0.4.0)
24
17
  mime-types (~> 1.16)
25
18
  treetop (~> 1.4.8)
26
19
  mime-types (1.16)
27
- multi_json (1.0.3)
28
20
  polyglot (0.3.1)
29
21
  rack (1.3.1)
30
22
  rake (0.9.2)
@@ -39,9 +31,7 @@ GEM
39
31
  rspec-mocks (2.3.0)
40
32
  sinatra (1.2.6)
41
33
  rack (~> 1.1)
42
- tilt (< 2.0, >= 1.2.2)
43
- therubyracer (0.9.2)
44
- libv8 (~> 3.3.10)
34
+ tilt (>= 1.2.2, < 2.0)
45
35
  thin (1.2.11)
46
36
  daemons (>= 1.0.9)
47
37
  eventmachine (>= 0.12.6)
@@ -56,14 +46,11 @@ PLATFORMS
56
46
  DEPENDENCIES
57
47
  bundler (~> 1.0.0)
58
48
  clamp (= 0.2.1)
59
- coffee-script (= 2.2.0)
60
49
  daemons (= 1.1.4)
61
- execjs (= 1.2.0)
62
50
  jeweler (~> 1.6.2)
63
51
  json (= 1.5.3)
64
52
  mail (= 2.3.0)
65
53
  rcov
66
54
  rspec (~> 2.3.0)
67
55
  sinatra (= 1.2.6)
68
- therubyracer (= 0.9.2)
69
56
  thin (= 1.2.11)
data/Rakefile CHANGED
@@ -36,4 +36,26 @@ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
36
  spec.rcov = true
37
37
  end
38
38
 
39
- task :default => :spec
39
+ task :default => [:coffee, :spec]
40
+
41
+ task :release => ["coffee", "gemspec:release","git:release","gemcutter:release"]
42
+
43
+ desc "Clean out yucky dev stuff"
44
+ task :clean => "coffee:clean"
45
+
46
+ desc "compile coffeescript"
47
+ task :coffee => "coffee:compile"
48
+
49
+ namespace :coffee do
50
+
51
+ coffee_src_dir = File.join(File.dirname(__FILE__), "web", "src", "coffee")
52
+ coffee_out_dir = File.join(File.dirname(__FILE__), "web", "public", "js")
53
+
54
+ task :compile do
55
+ `coffee -o #{coffee_out_dir} #{coffee_src_dir}/*.coffee`
56
+ end
57
+
58
+ task :clean do
59
+ `rm #{coffee_out_dir}/*.js`
60
+ end
61
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.1.0
data/bin/malm CHANGED
@@ -19,7 +19,7 @@ class MalmCommand < Clamp::Command
19
19
 
20
20
  def execute
21
21
  malm_proc = proc{
22
- malm = Malm.new(:log => log, :smtpport => smtpport, :webport => webport)
22
+ malm = Malm::Main.new(:log => log, :smtpport => smtpport, :webport => webport)
23
23
  malm.run!
24
24
  }
25
25
 
data/lib/malm/main.rb ADDED
@@ -0,0 +1,40 @@
1
+ module Malm
2
+ class Main
3
+ attr_accessor :log, :smtpport, :webport
4
+
5
+ def initialize(options={})
6
+ options.each{|k,v| send("#{k}=", v)}
7
+ end
8
+
9
+ def run!
10
+ create_db
11
+ run_smtp!
12
+ run_web!
13
+ end
14
+
15
+ private
16
+ def create_db
17
+ @db = Malm::MessageDb.new
18
+ end
19
+
20
+ def run_smtp!
21
+ smtp_server = Malm::SMTPServer.new(@smtpport)
22
+ smtp_server.mail_log = @log
23
+ smtp_server.message_db = @db
24
+
25
+ begin
26
+ smtp_server.start
27
+ rescue Errno::EACCES
28
+ STDERR.puts("Don't have permission to start SMTP server on port #{smtpport}. Maybe run with sudo?")
29
+ exit 1
30
+ end
31
+ smtp_server
32
+ end
33
+
34
+ def run_web!
35
+ Malm::Web.set :port, @webport
36
+ Malm::Web.set :message_db, @db
37
+ Malm::Web.run!
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,87 @@
1
+ module Malm
2
+ class Message
3
+ attr_reader :state, :email_body, :mail_from, :rcpt_to
4
+
5
+ def initialize
6
+ @email_body = ""
7
+ @rcpt_to = []
8
+ @state = :init_state
9
+ end
10
+
11
+ def process_line(line=nil)
12
+ if line =~ /QUIT/
13
+ return quit!
14
+ end
15
+
16
+ # state machine pattern. Delegate to state handler for where we're at in SMTP conversation
17
+ response = send(@state, line)
18
+ return error! unless response
19
+
20
+ # transition if state handler returned a new state
21
+ new_state = response.delete(:state)
22
+ @state = new_state if new_state
23
+
24
+ response
25
+ end
26
+
27
+ def subject
28
+ if @email_body =~ /^Subject\: (.+)$/
29
+ $1.strip
30
+ end
31
+ end
32
+
33
+ private
34
+ def init_state(_ignore_line=nil)
35
+ {:output => "220 hello\r\n", :state => :helo_state}
36
+ end
37
+
38
+ def helo_state(line)
39
+ if (line =~ /^(HELO|EHLO)/)
40
+ ok!(:mail_state)
41
+ end
42
+ end
43
+
44
+ def mail_state(line)
45
+ if (line =~ /^MAIL FROM\:<(.+)>.*$/)
46
+ @mail_from = $1
47
+ ok!(:rcpt_state)
48
+ end
49
+ end
50
+
51
+ def rcpt_state(line)
52
+ if (line =~ /^RCPT TO\:<(.+)>.*$/)
53
+ @rcpt_to << $1
54
+ ok!
55
+ elsif (line =~ /^DATA/)
56
+ {:output => "354 Enter message, ending with \".\" on a line by itself\r\n", :state => :data_state}
57
+ end
58
+ end
59
+
60
+ def data_state(line)
61
+ if (line.chomp =~ /^\.$/)
62
+ ok!(:quit_state)
63
+ else
64
+ @email_body << line
65
+ {}
66
+ end
67
+ end
68
+
69
+ def quit_state(_ignore)
70
+ error!
71
+ end
72
+
73
+ def quit!
74
+ {:output => "221 bye\r\n", :state => :quit_state, :done => true}
75
+ end
76
+
77
+ def error!
78
+ {:output => "500 ERROR\r\n", :state => :quit_state, :done => true}
79
+ end
80
+
81
+ def ok!(state=nil)
82
+ {:output => "250 OK\r\n", :state => state}
83
+ end
84
+
85
+
86
+ end
87
+ end
@@ -1,6 +1,6 @@
1
1
  require 'thread'
2
2
 
3
- class Malm
3
+ module Malm
4
4
  class MessageDb
5
5
 
6
6
  def initialize
@@ -1,83 +1,36 @@
1
1
  # based on copy + paste from http://snippets.dzone.com/posts/show/5152
2
2
  require 'gserver'
3
3
 
4
- class Malm
4
+ module Malm
5
5
  class SMTPServer < GServer
6
6
 
7
7
  attr_accessor :mail_log
8
8
  attr_accessor :message_db
9
-
10
- class Session
11
- attr_accessor :data_mode, :data_mode, :email_body, :mail_from, :rcpt_to, :subject
12
- alias :data_mode? :data_mode
13
9
 
14
- def initialize
15
- @data_mode = false
16
- @email_body = ""
17
- end
18
-
19
- end
20
-
21
10
  def serve(io)
22
- session = Session.new
23
- puts "Connected"
24
- io.print "220 hello\r\n"
11
+ message = Message.new
12
+ io.print message.process_line[:output]
25
13
  loop do
26
14
  data = io.gets
27
- puts ">>" + data
28
- ok, op = process_line(data, session)
29
- puts "<<" + op
30
- io.print op
31
- break unless ok
15
+ response = message.process_line(data)
16
+ io.print response[:output] if response[:output]
17
+ break if response[:done]
32
18
  break if io.closed?
33
19
  end
34
20
  begin
35
21
  io.close
36
- db_insert(session)
22
+ db_insert(message)
37
23
  rescue => e
38
24
  log "something screwed up..."
39
25
  log e.backtrace
40
26
  end
41
27
  end
42
-
43
- def process_line(line, session)
44
- if (session.data_mode?) && (line.chomp =~ /^\.$/)
45
- session.data_mode = false
46
- return true, "250 OK\r\n"
47
- elsif session.data_mode?
48
- session.email_body += line
49
- return true, ""
50
- elsif (line =~ /^(HELO|EHLO)/)
51
- return true, "250 and..?\r\n"
52
- elsif (line =~ /^QUIT/)
53
- return false, "221 bye\r\n"
54
- elsif (line =~ /^MAIL FROM\:/)
55
- session.mail_from = (/^MAIL FROM\:<(.+)>.*$/).match(line)[1]
56
- return true, "250 OK\r\n"
57
- elsif (line =~ /^RCPT TO\:/)
58
- session.rcpt_to = (/^RCPT TO\:<(.+)>.*$/).match(line)[1]
59
- return true, "250 OK\r\n"
60
- elsif (line =~ /^DATA/)
61
- session.data_mode = true
62
- return true, "354 Enter message, ending with \".\" on a line by itself\r\n"
63
- else
64
- return true, "500 ERROR\r\n"
65
- end
66
- end
67
28
 
68
- def db_insert(session)
69
- subject_regex = /^Subject\: (.+)$/
70
-
71
- subject_match = subject_regex.match(session.email_body)
72
- subject = subject_match ? subject_match[1] : ""
73
- subject.strip!
74
-
75
- message = {:subject => subject, :from => session.mail_from, :to => session.rcpt_to, :body => session.email_body}
76
-
77
- @mail_log_fd.puts(message.inspect) if @mail_log_fd
78
- @message_db.create(message) if @message_db
29
+ def db_insert(message)
30
+ message_data = {:subject => message.subject, :from => message.mail_from, :to => message.rcpt_to, :body => message.email_body}
79
31
 
80
- log("Message received: #{message.inspect}")
32
+ @mail_log_fd.puts(message_data.inspect) if @mail_log_fd
33
+ @message_db.create(message_data) if @message_db
81
34
  end
82
35
 
83
36
  protected
data/lib/malm/web.rb CHANGED
@@ -1,18 +1,19 @@
1
1
  require 'thin'
2
2
  require 'sinatra/base'
3
3
  require 'json'
4
- require 'v8'
5
- require 'execjs'
6
- require 'coffee_script'
7
4
  require 'mail'
8
5
 
9
- class Malm
6
+ module Malm
10
7
  class Web < Sinatra::Base
11
8
 
12
- set :public, File.join(File.dirname(__FILE__), "..", "..", "web", "static")
9
+ set :public, File.join(File.dirname(__FILE__), "..", "..", "web", "public")
13
10
  set :views, File.join(File.dirname(__FILE__), "..", "..", "web", "views")
14
11
 
15
- get "/messages.json" do
12
+ get "/" do
13
+ erb :'index.html'
14
+ end
15
+
16
+ get "/messages" do
16
17
  content_type :json
17
18
  settings.message_db.find_all.map{|m|
18
19
  m = m.dup
@@ -22,7 +23,7 @@ class Malm
22
23
  }.to_json
23
24
  end
24
25
 
25
- get "/messages/:id.json" do
26
+ get "/messages/:id" do
26
27
  content_type :json
27
28
  find_message(params[:id]).to_json
28
29
  end
@@ -30,12 +31,7 @@ class Malm
30
31
  get "/messages/:id/body.:type" do
31
32
  render_message(params[:id], params[:type])
32
33
  end
33
-
34
- get "/coffee/:script.coffee" do
35
- content_type "text/javascript"
36
- coffee params[:script].to_sym
37
- end
38
-
34
+
39
35
  private
40
36
  def find_message(id)
41
37
  id = Integer(id)
@@ -53,7 +49,8 @@ class Malm
53
49
 
54
50
  msg_obj = find_message(id)
55
51
  if msg_obj
56
- Mail::Message.new(msg_obj[:body]).send("#{type}_part".to_sym).body.to_s
52
+ content_type_body = Mail::Message.new(msg_obj[:body]).send("#{type}_part".to_sym)
53
+ content_type_body ? content_type_body.body.to_s : msg_obj[:body]
57
54
  else
58
55
  status 404
59
56
  "don't know about message #{id}"
data/lib/malm.rb CHANGED
@@ -1,42 +1,5 @@
1
+ require 'malm/message'
1
2
  require 'malm/message_db'
2
3
  require 'malm/smtp_server'
3
4
  require 'malm/web'
4
-
5
- class Malm
6
- attr_accessor :log, :smtpport, :webport
7
-
8
- def initialize(options={})
9
- options.each{|k,v| send("#{k}=", v)}
10
- end
11
-
12
- def run!
13
- create_db
14
- run_smtp!
15
- run_web!
16
- end
17
-
18
- private
19
- def create_db
20
- @db = Malm::MessageDb.new
21
- end
22
-
23
- def run_smtp!
24
- smtp_server = Malm::SMTPServer.new(@smtpport)
25
- smtp_server.mail_log = @log
26
- smtp_server.message_db = @db
27
-
28
- begin
29
- smtp_server.start
30
- rescue Errno::EACCES
31
- STDERR.puts("Don't have permission to start SMTP server on port #{smtpport}. Maybe run with sudo?")
32
- exit 1
33
- end
34
- smtp_server
35
- end
36
-
37
- def run_web!
38
- Malm::Web.set :port, @webport
39
- Malm::Web.set :message_db, @db
40
- Malm::Web.run!
41
- end
42
- end
5
+ require 'malm/main'
data/malm.gemspec CHANGED
@@ -4,15 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{malm}
8
- s.version = "0.0.6"
7
+ s.name = "malm"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["madlep"]
12
- s.date = %q{2011-07-25}
13
- s.default_executable = %q{malm}
14
- s.description = %q{SMTP server with web interface for easy local development. Sets up a little mail server that you can send messages to, and provides a web front end to let you see what your app did.}
15
- s.email = %q{julian.doherty.ml@gmail.com}
12
+ s.date = "2011-10-31"
13
+ s.description = "SMTP server with web interface for easy local development. Sets up a little mail server that you can send messages to, and provides a web front end to let you see what your app did."
14
+ s.email = "julian.doherty.ml@gmail.com"
16
15
  s.executables = ["malm"]
17
16
  s.extra_rdoc_files = [
18
17
  "LICENSE.txt",
@@ -29,27 +28,29 @@ Gem::Specification.new do |s|
29
28
  "VERSION",
30
29
  "bin/malm",
31
30
  "lib/malm.rb",
31
+ "lib/malm/main.rb",
32
+ "lib/malm/message.rb",
32
33
  "lib/malm/message_db.rb",
33
34
  "lib/malm/smtp_server.rb",
34
35
  "lib/malm/web.rb",
35
36
  "malm.gemspec",
36
37
  "spec/bin/malm_test_message",
37
- "spec/help_spec.rb",
38
+ "spec/malm/message_spec.rb",
39
+ "spec/malm/smtp_server_spec.rb",
38
40
  "spec/spec_helper.rb",
39
- "web/static/css/style.css",
40
- "web/static/favicon.ico",
41
- "web/static/index.html",
42
- "web/static/js/backbone-min.js",
43
- "web/static/js/backbone.js",
44
- "web/static/js/jquery-1.6.2.js",
45
- "web/static/js/jquery-1.6.2.min.js",
46
- "web/views/hello_world.coffee"
41
+ "web/public/css/style.css",
42
+ "web/public/favicon.ico",
43
+ "web/public/js/.gitignore",
44
+ "web/public/js/lib/backbone-min.js",
45
+ "web/public/js/lib/jquery-1.6.2.min.js",
46
+ "web/src/coffee/malm.coffee",
47
+ "web/views/index.html.erb"
47
48
  ]
48
- s.homepage = %q{http://github.com/madlep/malm}
49
+ s.homepage = "http://github.com/madlep/malm"
49
50
  s.licenses = ["MIT"]
50
51
  s.require_paths = ["lib"]
51
- s.rubygems_version = %q{1.6.2}
52
- s.summary = %q{Easy SMTP server for local development}
52
+ s.rubygems_version = "1.8.10"
53
+ s.summary = "Easy SMTP server for local development"
53
54
 
54
55
  if s.respond_to? :specification_version then
55
56
  s.specification_version = 3
@@ -60,9 +61,6 @@ Gem::Specification.new do |s|
60
61
  s.add_runtime_dependency(%q<sinatra>, ["= 1.2.6"])
61
62
  s.add_runtime_dependency(%q<thin>, ["= 1.2.11"])
62
63
  s.add_runtime_dependency(%q<mail>, ["= 2.3.0"])
63
- s.add_runtime_dependency(%q<therubyracer>, ["= 0.9.2"])
64
- s.add_runtime_dependency(%q<execjs>, ["= 1.2.0"])
65
- s.add_runtime_dependency(%q<coffee-script>, ["= 2.2.0"])
66
64
  s.add_runtime_dependency(%q<daemons>, ["= 1.1.4"])
67
65
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
68
66
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -74,9 +72,6 @@ Gem::Specification.new do |s|
74
72
  s.add_dependency(%q<sinatra>, ["= 1.2.6"])
75
73
  s.add_dependency(%q<thin>, ["= 1.2.11"])
76
74
  s.add_dependency(%q<mail>, ["= 2.3.0"])
77
- s.add_dependency(%q<therubyracer>, ["= 0.9.2"])
78
- s.add_dependency(%q<execjs>, ["= 1.2.0"])
79
- s.add_dependency(%q<coffee-script>, ["= 2.2.0"])
80
75
  s.add_dependency(%q<daemons>, ["= 1.1.4"])
81
76
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
82
77
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -89,9 +84,6 @@ Gem::Specification.new do |s|
89
84
  s.add_dependency(%q<sinatra>, ["= 1.2.6"])
90
85
  s.add_dependency(%q<thin>, ["= 1.2.11"])
91
86
  s.add_dependency(%q<mail>, ["= 2.3.0"])
92
- s.add_dependency(%q<therubyracer>, ["= 0.9.2"])
93
- s.add_dependency(%q<execjs>, ["= 1.2.0"])
94
- s.add_dependency(%q<coffee-script>, ["= 2.2.0"])
95
87
  s.add_dependency(%q<daemons>, ["= 1.1.4"])
96
88
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
97
89
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -205,10 +205,40 @@ ICAgICAgICAgPC90cj4NCiAgICAgICAgPC90YWJsZT4NCiAgICA8L2JvZHk+DQo8L2h0bWw+DQo=
205
205
  --_000_4e1f79598aac0508010ee224app01groupsdevintlpotmail_--
206
206
  END_OF_MESSAGE
207
207
 
208
+ message3 = <<-END_OF_MESSAGE
209
+ Delivered-To: julian.doherty.ml@gmail.com
210
+ Received: by 10.205.65.11 with SMTP id xk11cs235015bkb;
211
+ Fri, 8 Jul 2011 04:13:29 -0700 (PDT)
212
+ Received: by 10.236.180.38 with SMTP id i26mr2405055yhm.205.1310123608196;
213
+ Fri, 08 Jul 2011 04:13:28 -0700 (PDT)
214
+ Return-Path: <qybsao@galeriestills.nl>
215
+ Received: from kiver.site5.com (kiver.site5.com [174.132.150.130])
216
+ by mx.google.com with ESMTPS id g63si36654835yhn.48.2011.07.08.04.13.26
217
+ (version=TLSv1/SSLv3 cipher=OTHER);
218
+ Fri, 08 Jul 2011 04:13:26 -0700 (PDT)
219
+ Received-SPF: neutral (google.com: 174.132.150.130 is neither permitted nor denied by best guess record for domain of qybsao@galeriestills.nl) client-ip=174.132.150.130;
220
+ Authentication-Results: mx.google.com; spf=neutral (google.com: 174.132.150.130 is neither permitted nor denied by best guess record for domain of qybsao@galeriestills.nl) smtp.mail=qybsao@galeriestills.nl
221
+ Received: from akf90.internetdsl.tpnet.pl ([83.17.9.90] helo=galeriestills.nl)
222
+ by kiver.site5.com with esmtp (Exim 4.69)
223
+ (envelope-from <qybsao@galeriestills.nl>)
224
+ id 1Qf8zt-0007aO-I9
225
+ for madlep@ubercharged.net; Fri, 08 Jul 2011 06:13:26 -0500
226
+ Message-ID: <2ac901cc3d5f$2983c8b0$3b29bc19@galeriestills.nl>
227
+ From: "Precision Crafted and Low Priced Watches" <qybsao@galeriestills.nl>
228
+ To: <madlep@ubercharged.net>
229
+ Subject: No fancy HTML...
230
+ Date: Fri, 8 Jul 2011 13:11:17 +0200
231
+ Yeah, I just got plain text sorry.
232
+ END_OF_MESSAGE
233
+
208
234
  Net::SMTP.start('localhost', 2525) do |smtp|
209
235
  smtp.send_message(message1,"francis@example.com","bill@example.com")
210
236
  end
211
237
 
212
238
  Net::SMTP.start('localhost', 2525) do |smtp|
213
239
  smtp.send_message(message2,"registration@lonelyplanet.com.au","madlep0715@devint.lpo")
240
+ end
241
+
242
+ Net::SMTP.start('localhost', 2525) do |smtp|
243
+ smtp.send_message(message3,"boring@example.com","unimpressed@example.com")
214
244
  end