malm 0.0.6 → 0.1.0

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