mailcatcher 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.2
@@ -55,6 +55,7 @@ module_function
55
55
  @@defaults.dup.tap do |options|
56
56
  OptionParser.new do |parser|
57
57
  parser.banner = "Usage: mailcatcher [options]"
58
+ parser.version = File.read(File.expand_path("../../VERSION", __FILE__))
58
59
 
59
60
  parser.on("--ip IP", "Set the ip address of both servers") do |ip|
60
61
  options[:smtp_ip] = options[:http_ip] = ip
@@ -1,7 +1,5 @@
1
1
  require 'eventmachine'
2
2
 
3
- module MailCatcher
4
- module Events
5
- MessageAdded = EventMachine::Channel.new
6
- end
3
+ module MailCatcher::Events
4
+ MessageAdded = EventMachine::Channel.new
7
5
  end
@@ -6,7 +6,8 @@ module MailCatcher
6
6
  end
7
7
 
8
8
  def notify message
9
- system "growlnotify", "--name", "MailCatcher", "--message", "Message received:\n#{message["subject"]}"
9
+ image_path = File.expand_path(File.join(__FILE__, '..', '..', '..', 'public', 'images', 'logo_large.png'))
10
+ system "growlnotify", "--image", image_path, "--name", "MailCatcher", "--message", "Message received:\n#{message["subject"]}"
10
11
  end
11
12
 
12
13
  # TODO: Native support on MacRuby with click backs
@@ -3,152 +3,152 @@ require 'sqlite3'
3
3
  require 'eventmachine'
4
4
 
5
5
  module MailCatcher::Mail
6
- class << self
7
- def db
8
- @@__db ||= begin
9
- SQLite3::Database.new(':memory:', :type_translation => true).tap do |db|
10
- db.execute(<<-SQL)
11
- CREATE TABLE message (
12
- id INTEGER PRIMARY KEY ASC,
13
- sender TEXT,
14
- recipients TEXT,
15
- subject TEXT,
16
- source BLOB,
17
- size TEXT,
18
- type TEXT,
19
- created_at DATETIME DEFAULT CURRENT_DATETIME
20
- )
21
- SQL
22
- db.execute(<<-SQL)
23
- CREATE TABLE message_part (
24
- id INTEGER PRIMARY KEY ASC,
25
- message_id INTEGER NOT NULL,
26
- cid TEXT,
27
- type TEXT,
28
- is_attachment INTEGER,
29
- filename TEXT,
30
- charset TEXT,
31
- body BLOB,
32
- size INTEGER,
33
- created_at DATETIME DEFAULT CURRENT_DATETIME
34
- )
35
- SQL
36
- end
6
+ module_function
7
+
8
+ def db
9
+ @@__db ||= begin
10
+ SQLite3::Database.new(':memory:', :type_translation => true).tap do |db|
11
+ db.execute(<<-SQL)
12
+ CREATE TABLE message (
13
+ id INTEGER PRIMARY KEY ASC,
14
+ sender TEXT,
15
+ recipients TEXT,
16
+ subject TEXT,
17
+ source BLOB,
18
+ size TEXT,
19
+ type TEXT,
20
+ created_at DATETIME DEFAULT CURRENT_DATETIME
21
+ )
22
+ SQL
23
+ db.execute(<<-SQL)
24
+ CREATE TABLE message_part (
25
+ id INTEGER PRIMARY KEY ASC,
26
+ message_id INTEGER NOT NULL,
27
+ cid TEXT,
28
+ type TEXT,
29
+ is_attachment INTEGER,
30
+ filename TEXT,
31
+ charset TEXT,
32
+ body BLOB,
33
+ size INTEGER,
34
+ created_at DATETIME DEFAULT CURRENT_DATETIME
35
+ )
36
+ SQL
37
37
  end
38
38
  end
39
+ end
39
40
 
40
- def add_message(message)
41
- @@add_message_query ||= db.prepare("INSERT INTO message (sender, recipients, subject, source, type, size, created_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))")
42
-
43
- mail = Mail.new(message[:source])
44
- result = @@add_message_query.execute(message[:sender], message[:recipients].to_json, mail.subject, message[:source], mail.mime_type || 'text/plain', message[:source].length)
45
- message_id = db.last_insert_row_id
46
- parts = mail.all_parts
47
- parts = [mail] if parts.empty?
48
- parts.each do |part|
49
- body = part.body.to_s
50
- # Only parts have CIDs, not mail
51
- cid = part.cid if part.respond_to? :cid
52
- add_message_part(message_id, cid, part.mime_type || 'text/plain', part.attachment? ? 1 : 0, part.filename, part.charset, body, body.length)
53
- end
41
+ def add_message(message)
42
+ @@add_message_query ||= db.prepare("INSERT INTO message (sender, recipients, subject, source, type, size, created_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))")
54
43
 
55
- EventMachine.next_tick do
56
- message = MailCatcher::Mail.message message_id
57
- MailCatcher::Events::MessageAdded.push message
58
- end
44
+ mail = Mail.new(message[:source])
45
+ result = @@add_message_query.execute(message[:sender], message[:recipients].to_json, mail.subject, message[:source], mail.mime_type || 'text/plain', message[:source].length)
46
+ message_id = db.last_insert_row_id
47
+ parts = mail.all_parts
48
+ parts = [mail] if parts.empty?
49
+ parts.each do |part|
50
+ body = part.body.to_s
51
+ # Only parts have CIDs, not mail
52
+ cid = part.cid if part.respond_to? :cid
53
+ add_message_part(message_id, cid, part.mime_type || 'text/plain', part.attachment? ? 1 : 0, part.filename, part.charset, body, body.length)
59
54
  end
60
55
 
61
- def add_message_part(*args)
62
- @@add_message_part_query ||= db.prepare "INSERT INTO message_part (message_id, cid, type, is_attachment, filename, charset, body, size, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))"
63
- @@add_message_part_query.execute(*args)
56
+ EventMachine.next_tick do
57
+ message = MailCatcher::Mail.message message_id
58
+ MailCatcher::Events::MessageAdded.push message
64
59
  end
60
+ end
65
61
 
66
- def latest_created_at
67
- @@latest_created_at_query ||= db.prepare "SELECT created_at FROM message ORDER BY created_at DESC LIMIT 1"
68
- @@latest_created_at_query.execute.next
69
- end
62
+ def add_message_part(*args)
63
+ @@add_message_part_query ||= db.prepare "INSERT INTO message_part (message_id, cid, type, is_attachment, filename, charset, body, size, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))"
64
+ @@add_message_part_query.execute(*args)
65
+ end
70
66
 
71
- def messages
72
- @@messages_query ||= db.prepare "SELECT id, sender, recipients, subject, size, created_at FROM message ORDER BY created_at ASC"
73
- @@messages_query.execute.map do |row|
74
- Hash[row.fields.zip(row)].tap do |message|
75
- message["recipients"] &&= ActiveSupport::JSON.decode message["recipients"]
76
- end
77
- end
78
- end
67
+ def latest_created_at
68
+ @@latest_created_at_query ||= db.prepare "SELECT created_at FROM message ORDER BY created_at DESC LIMIT 1"
69
+ @@latest_created_at_query.execute.next
70
+ end
79
71
 
80
- def message(id)
81
- @@message_query ||= db.prepare "SELECT * FROM message WHERE id = ? LIMIT 1"
82
- row = @@message_query.execute(id).next
83
- row && Hash[row.fields.zip(row)].tap do |message|
72
+ def messages
73
+ @@messages_query ||= db.prepare "SELECT id, sender, recipients, subject, size, created_at FROM message ORDER BY created_at ASC"
74
+ @@messages_query.execute.map do |row|
75
+ Hash[row.fields.zip(row)].tap do |message|
84
76
  message["recipients"] &&= ActiveSupport::JSON.decode message["recipients"]
85
77
  end
86
78
  end
79
+ end
87
80
 
88
- def message_has_html?(id)
89
- @@message_has_html_query ||= db.prepare "SELECT 1 FROM message_part WHERE message_id = ? AND is_attachment = 0 AND type IN ('application/xhtml+xml', 'text/html') LIMIT 1"
90
- (!!@@message_has_html_query.execute(id).next) || ['text/html', 'application/xhtml+xml'].include?(message(id)["type"])
81
+ def message(id)
82
+ @@message_query ||= db.prepare "SELECT * FROM message WHERE id = ? LIMIT 1"
83
+ row = @@message_query.execute(id).next
84
+ row && Hash[row.fields.zip(row)].tap do |message|
85
+ message["recipients"] &&= ActiveSupport::JSON.decode message["recipients"]
91
86
  end
87
+ end
92
88
 
93
- def message_has_plain?(id)
94
- @@message_has_plain_query ||= db.prepare "SELECT 1 FROM message_part WHERE message_id = ? AND is_attachment = 0 AND type = 'text/plain' LIMIT 1"
95
- (!!@@message_has_plain_query.execute(id).next) || message(id)["type"] == "text/plain"
96
- end
89
+ def message_has_html?(id)
90
+ @@message_has_html_query ||= db.prepare "SELECT 1 FROM message_part WHERE message_id = ? AND is_attachment = 0 AND type IN ('application/xhtml+xml', 'text/html') LIMIT 1"
91
+ (!!@@message_has_html_query.execute(id).next) || ['text/html', 'application/xhtml+xml'].include?(message(id)["type"])
92
+ end
97
93
 
98
- def message_parts(id)
99
- @@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? ORDER BY filename ASC"
100
- @@message_parts_query.execute(id).map do |row|
101
- Hash[row.fields.zip(row)]
102
- end
103
- end
94
+ def message_has_plain?(id)
95
+ @@message_has_plain_query ||= db.prepare "SELECT 1 FROM message_part WHERE message_id = ? AND is_attachment = 0 AND type = 'text/plain' LIMIT 1"
96
+ (!!@@message_has_plain_query.execute(id).next) || message(id)["type"] == "text/plain"
97
+ end
104
98
 
105
- def message_attachments(id)
106
- @@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? AND is_attachment = 1 ORDER BY filename ASC"
107
- @@message_parts_query.execute(id).map do |row|
108
- Hash[row.fields.zip(row)]
109
- end
99
+ def message_parts(id)
100
+ @@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? ORDER BY filename ASC"
101
+ @@message_parts_query.execute(id).map do |row|
102
+ Hash[row.fields.zip(row)]
110
103
  end
104
+ end
111
105
 
112
- def message_part(message_id, part_id)
113
- @@message_part_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND id = ? LIMIT 1"
114
- row = @@message_part_query.execute(message_id, part_id).next
115
- row && Hash[row.fields.zip(row)]
106
+ def message_attachments(id)
107
+ @@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? AND is_attachment = 1 ORDER BY filename ASC"
108
+ @@message_parts_query.execute(id).map do |row|
109
+ Hash[row.fields.zip(row)]
116
110
  end
111
+ end
117
112
 
118
- def message_part_type(message_id, part_type)
119
- @@message_part_type_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND type = ? AND is_attachment = 0 LIMIT 1"
120
- row = @@message_part_type_query.execute(message_id, part_type).next
121
- row && Hash[row.fields.zip(row)]
122
- end
113
+ def message_part(message_id, part_id)
114
+ @@message_part_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND id = ? LIMIT 1"
115
+ row = @@message_part_query.execute(message_id, part_id).next
116
+ row && Hash[row.fields.zip(row)]
117
+ end
123
118
 
124
- def message_part_html(message_id)
125
- part = message_part_type(message_id, "text/html")
126
- part ||= message_part_type(message_id, "application/xhtml+xml")
127
- part ||= begin
128
- message = message(message_id)
129
- message if message.present? and ['text/html', 'application/xhtml+xml'].include? message["type"]
130
- end
131
- end
119
+ def message_part_type(message_id, part_type)
120
+ @@message_part_type_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND type = ? AND is_attachment = 0 LIMIT 1"
121
+ row = @@message_part_type_query.execute(message_id, part_type).next
122
+ row && Hash[row.fields.zip(row)]
123
+ end
132
124
 
133
- def message_part_plain(message_id)
134
- message_part_type message_id, "text/plain"
125
+ def message_part_html(message_id)
126
+ part = message_part_type(message_id, "text/html")
127
+ part ||= message_part_type(message_id, "application/xhtml+xml")
128
+ part ||= begin
129
+ message = message(message_id)
130
+ message if message.present? and ['text/html', 'application/xhtml+xml'].include? message["type"]
135
131
  end
132
+ end
136
133
 
137
- def message_part_cid(message_id, cid)
138
- @@message_part_cid_query ||= db.prepare 'SELECT * FROM message_part WHERE message_id = ?'
139
- @@message_part_cid_query.execute(message_id).map do |row|
140
- part = Hash[row.fields.zip(row)]
141
- end.find do |part|
142
- part["cid"] == cid
143
- end
134
+ def message_part_plain(message_id)
135
+ message_part_type message_id, "text/plain"
136
+ end
137
+
138
+ def message_part_cid(message_id, cid)
139
+ @@message_part_cid_query ||= db.prepare 'SELECT * FROM message_part WHERE message_id = ?'
140
+ @@message_part_cid_query.execute(message_id).map do |row|
141
+ part = Hash[row.fields.zip(row)]
142
+ end.find do |part|
143
+ part["cid"] == cid
144
144
  end
145
+ end
145
146
 
146
- def delete!
147
- @@delete_messages_query ||= db.prepare 'DELETE FROM message'
148
- @@delete_message_parts_query ||= db.prepare 'DELETE FROM message_part'
147
+ def delete!
148
+ @@delete_messages_query ||= db.prepare 'DELETE FROM message'
149
+ @@delete_message_parts_query ||= db.prepare 'DELETE FROM message_part'
149
150
 
150
- @@delete_messages_query.execute and
151
- @@delete_message_parts_query.execute
152
- end
151
+ @@delete_messages_query.execute and
152
+ @@delete_message_parts_query.execute
153
153
  end
154
154
  end
@@ -1,48 +1,46 @@
1
1
  require 'eventmachine'
2
2
 
3
- module MailCatcher
4
- class Smtp < EventMachine::Protocols::SmtpServer
5
- def current_message
6
- @current_message ||= {}
7
- end
3
+ class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
4
+ def current_message
5
+ @current_message ||= {}
6
+ end
8
7
 
9
- def receive_reset
10
- @current_message = nil
11
- true
12
- end
8
+ def receive_reset
9
+ @current_message = nil
10
+ true
11
+ end
13
12
 
14
- def receive_sender(sender)
15
- current_message[:sender] = sender
16
- true
17
- end
13
+ def receive_sender(sender)
14
+ current_message[:sender] = sender
15
+ true
16
+ end
18
17
 
19
- def receive_recipient(recipient)
20
- current_message[:recipients] ||= []
21
- current_message[:recipients] << recipient
22
- true
23
- end
18
+ def receive_recipient(recipient)
19
+ current_message[:recipients] ||= []
20
+ current_message[:recipients] << recipient
21
+ true
22
+ end
24
23
 
25
- def receive_data_chunk(lines)
26
- current_message[:source] ||= ""
27
- current_message[:source] += lines.join("\n")
28
- true
29
- end
24
+ def receive_data_chunk(lines)
25
+ current_message[:source] ||= ""
26
+ current_message[:source] += lines.join("\n")
27
+ true
28
+ end
30
29
 
31
- def receive_message
32
- MailCatcher::Mail.add_message current_message
33
- puts "==> SMTP: Received message from '#{current_message[:sender]}' (#{current_message[:source].length} bytes)"
34
- true
35
- rescue
36
- puts "*** Error receiving message: #{current_message.inspect}"
37
- puts " Exception: #{$!}"
38
- puts " Backtrace:"
39
- $!.backtrace.each do |line|
40
- puts " #{line}"
41
- end
42
- puts " Please submit this as an issue at http://github.com/sj26/mailcatcher/issues"
43
- false
44
- ensure
45
- @current_message = nil
30
+ def receive_message
31
+ MailCatcher::Mail.add_message current_message
32
+ puts "==> SMTP: Received message from '#{current_message[:sender]}' (#{current_message[:source].length} bytes)"
33
+ true
34
+ rescue
35
+ puts "*** Error receiving message: #{current_message.inspect}"
36
+ puts " Exception: #{$!}"
37
+ puts " Backtrace:"
38
+ $!.backtrace.each do |line|
39
+ puts " #{line}"
46
40
  end
41
+ puts " Please submit this as an issue at http://github.com/sj26/mailcatcher/issues"
42
+ false
43
+ ensure
44
+ @current_message = nil
47
45
  end
48
46
  end
@@ -1,5 +1,7 @@
1
1
  require 'sinatra'
2
2
  require 'pathname'
3
+ require 'net/http'
4
+ require 'uri'
3
5
 
4
6
  require 'skinny'
5
7
 
@@ -7,120 +9,132 @@ class Sinatra::Request
7
9
  include Skinny::Helpers
8
10
  end
9
11
 
10
- module MailCatcher
11
- class Web < Sinatra::Base
12
- set :root, Pathname.new(__FILE__).dirname.parent.parent
13
- set :haml, :format => :html5
12
+ class MailCatcher::Web < Sinatra::Base
13
+ set :root, Pathname.new(__FILE__).dirname.parent.parent
14
+ set :haml, :format => :html5
14
15
 
15
- get '/' do
16
- haml :index
17
- end
16
+ get '/' do
17
+ haml :index
18
+ end
18
19
 
19
- delete '/' do
20
- MailCatcher.quit!
21
- status 204
22
- end
20
+ delete '/' do
21
+ MailCatcher.quit!
22
+ status 204
23
+ end
23
24
 
24
- get '/messages' do
25
- if request.websocket?
26
- request.websocket!(
27
- :protocol => "MailCatcher 0.2 Message Push",
28
- :on_start => proc do |websocket|
29
- subscription = MailCatcher::Events::MessageAdded.subscribe { |message| websocket.send_message message.to_json }
30
- websocket.on_close do |websocket|
31
- MailCatcher::Events::MessageAdded.unsubscribe subscription
32
- end
33
- end)
34
- else
35
- MailCatcher::Mail.messages.to_json
36
- end
25
+ get '/messages' do
26
+ if request.websocket?
27
+ puts "Gots a websocket"
28
+ request.websocket!(
29
+ :protocol => "MailCatcher 0.2 Message Push",
30
+ :on_start => proc do |websocket|
31
+ subscription = MailCatcher::Events::MessageAdded.subscribe { |message| websocket.send_message message.to_json }
32
+ websocket.on_close do |websocket|
33
+ MailCatcher::Events::MessageAdded.unsubscribe subscription
34
+ end
35
+ end)
36
+ else
37
+ MailCatcher::Mail.messages.to_json
37
38
  end
39
+ end
38
40
 
39
- delete '/messages' do
40
- MailCatcher::Mail.delete!
41
- status 204
42
- end
41
+ delete '/messages' do
42
+ MailCatcher::Mail.delete!
43
+ status 204
44
+ end
43
45
 
44
- get '/messages/:id.json' do
45
- id = params[:id].to_i
46
- if message = MailCatcher::Mail.message(id)
47
- message.merge({
48
- "formats" => [
49
- "source",
50
- ("html" if MailCatcher::Mail.message_has_html? id),
51
- ("plain" if MailCatcher::Mail.message_has_plain? id),
52
- ].compact,
53
- "attachments" => MailCatcher::Mail.message_attachments(id).map do |attachment|
54
- attachment.merge({"href" => "/messages/#{escape(id)}/#{escape(attachment['cid'])}"})
55
- end,
56
- }).to_json
57
- else
58
- not_found
59
- end
46
+ get '/messages/:id.json' do
47
+ id = params[:id].to_i
48
+ if message = MailCatcher::Mail.message(id)
49
+ message.merge({
50
+ "formats" => [
51
+ "source",
52
+ ("html" if MailCatcher::Mail.message_has_html? id),
53
+ ("plain" if MailCatcher::Mail.message_has_plain? id),
54
+ ].compact,
55
+ "attachments" => MailCatcher::Mail.message_attachments(id).map do |attachment|
56
+ attachment.merge({"href" => "/messages/#{escape(id)}/#{escape(attachment['cid'])}"})
57
+ end,
58
+ }).to_json
59
+ else
60
+ not_found
60
61
  end
62
+ end
61
63
 
62
- get '/messages/:id.html' do
63
- id = params[:id].to_i
64
- if part = MailCatcher::Mail.message_part_html(id)
65
- content_type part["type"], :charset => (part["charset"] || "utf8")
64
+ get '/messages/:id.html' do
65
+ id = params[:id].to_i
66
+ if part = MailCatcher::Mail.message_part_html(id)
67
+ content_type part["type"], :charset => (part["charset"] || "utf8")
66
68
 
67
- body = part["body"]
69
+ body = part["body"]
68
70
 
69
- # Rewrite body to link to embedded attachments served by cid
70
- body.gsub! /cid:([^'"> ]+)/, "#{id}/\\1"
71
+ # Rewrite body to link to embedded attachments served by cid
72
+ body.gsub! /cid:([^'"> ]+)/, "#{id}/parts/\\1"
71
73
 
72
- # Rewrite body to open links in a new window
73
- body.gsub! /<a\s+/, '<a target="_blank" '
74
+ # Rewrite body to open links in a new window
75
+ body.gsub! /<a\s+/, '<a target="_blank" '
74
76
 
75
- body
76
- else
77
- not_found
78
- end
77
+ body
78
+ else
79
+ not_found
79
80
  end
81
+ end
80
82
 
81
- get "/messages/:id.plain" do
82
- id = params[:id].to_i
83
- if part = MailCatcher::Mail.message_part_plain(id)
84
- content_type part["type"], :charset => (part["charset"] || "utf8")
85
- part["body"]
86
- else
87
- not_found
88
- end
83
+ get "/messages/:id.plain" do
84
+ id = params[:id].to_i
85
+ if part = MailCatcher::Mail.message_part_plain(id)
86
+ content_type part["type"], :charset => (part["charset"] || "utf8")
87
+ part["body"]
88
+ else
89
+ not_found
89
90
  end
91
+ end
90
92
 
91
- get "/messages/:id.source" do
92
- id = params[:id].to_i
93
- if message = MailCatcher::Mail.message(id)
94
- content_type "text/plain"
95
- message["source"]
96
- else
97
- not_found
98
- end
93
+ get "/messages/:id.source" do
94
+ id = params[:id].to_i
95
+ if message = MailCatcher::Mail.message(id)
96
+ content_type "text/plain"
97
+ message["source"]
98
+ else
99
+ not_found
99
100
  end
101
+ end
100
102
 
101
- get "/messages/:id.eml" do
102
- id = params[:id].to_i
103
- if message = MailCatcher::Mail.message(id)
104
- content_type "message/rfc822"
105
- message["source"]
106
- else
107
- not_found
108
- end
103
+ get "/messages/:id.eml" do
104
+ id = params[:id].to_i
105
+ if message = MailCatcher::Mail.message(id)
106
+ content_type "message/rfc822"
107
+ message["source"]
108
+ else
109
+ not_found
109
110
  end
111
+ end
110
112
 
111
- get "/messages/:id/:cid" do
112
- id = params[:id].to_i
113
- if part = MailCatcher::Mail.message_part_cid(id, params[:cid])
114
- content_type part["type"], :charset => (part["charset"] || "utf8")
115
- attachment part["filename"] if part["is_attachment"] == 1
116
- body part["body"].to_s
117
- else
118
- not_found
119
- end
113
+ get "/messages/:id/parts/:cid" do
114
+ id = params[:id].to_i
115
+ if part = MailCatcher::Mail.message_part_cid(id, params[:cid])
116
+ content_type part["type"], :charset => (part["charset"] || "utf8")
117
+ attachment part["filename"] if part["is_attachment"] == 1
118
+ body part["body"].to_s
119
+ else
120
+ not_found
120
121
  end
122
+ end
121
123
 
122
- not_found do
123
- "<html><body><h1>No Dice</h1><p>The message you were looking for does not exist, or doesn't have content of this type.</p></body></html>"
124
+ get "/messages/:id/analysis.?:format?" do
125
+ id = params[:id].to_i
126
+ if part = MailCatcher::Mail.message_part_html(id)
127
+ # TODO: Server-side cache? Make the browser cache based on message create time? Hmm.
128
+ uri = URI.parse("http://api.getfractal.com/api/v2/validate#{"/format/#{params[:format]}" if params[:format].present?}")
129
+ response = Net::HTTP.post_form(uri, api_key: "5c463877265251386f516f7428", html: part["body"])
130
+ content_type ".#{params[:format]}" if params[:format].present?
131
+ body response.body
132
+ else
133
+ not_found
124
134
  end
125
135
  end
136
+
137
+ not_found do
138
+ "<html><body><h1>No Dice</h1><p>The message you were looking for does not exist, or doesn't have content of this type.</p></body></html>"
139
+ end
126
140
  end
@@ -34,6 +34,7 @@
34
34
  }
35
35
  });
36
36
  $('nav.app .clear a').live('click', __bind(function(e) {
37
+ e.preventDefault();
37
38
  if (confirm("You will lose all your received messages.\n\nAre you sure you want to clear all messages?")) {
38
39
  return $.ajax({
39
40
  url: '/messages',
@@ -50,6 +51,7 @@
50
51
  }
51
52
  }, this));
52
53
  $('nav.app .quit a').live('click', __bind(function(e) {
54
+ e.preventDefault();
53
55
  if (confirm("You will lose all your received messages.\n\nAre you sure you want to quit?")) {
54
56
  return $.ajax({
55
57
  type: 'DELETE',
@@ -148,15 +150,22 @@
148
150
  }
149
151
  };
150
152
  MailCatcher.prototype.loadMessageAnalysis = function(id) {
151
- var $iframe;
153
+ var $form, $iframe;
152
154
  id || (id = this.selectedMessage());
153
155
  $("#message .views .analysis.tab:not(.selected)").addClass('selected');
154
156
  $("#message .views :not(.analysis).tab.selected").removeClass('selected');
155
157
  if (id != null) {
156
- $iframe = $('#message iframe').contents().children().html("<html class=\"mailcatcher\"><head>" + ($('link[rel="stylesheet"]')[0].outerHTML) + "</head><body><iframe></iframe></body></html>").find("head").append($('link[rel="stylesheet"]').clone()).end().find('iframe').contents().children().html("<html>\n<head>\n<title>Analysis</title>\n" + ($('link[rel="stylesheet"]')[0].outerHTML) + "\n</head>\n<body class=\"iframe\">\n<h1>Analyse your email with Fractal</h1>\n<p><a href=\"http://getfractal.com/\" target=\"_blank\">Fractal</a> is a really neat service that applies common email design and development knowledge from <a href=\"http://www.email-standards.org/\" target=\"_blank\">Email Standards Project</a> to your HTML email and tells you what you've done wrong or what you should do instead.</p>\n<p>Please note that this <strong>sends your email to the Fractal service</strong> for analysis. Read their <a href=\"http://getfractal.com/terms\" target=\"_blank\">terms of service</a> if you're paranoid.</p>\n<form action=\"http://getfractal.com/validate\" method=\"POST\">\n<input type=\"hidden\" name=\"html\" />\n<input type=\"submit\" value=\"Analyse\" disabled=\"disabled\" /><span class=\"loading\" style=\"color: #999\">Loading your email...</span>\n</form>\n</body>\n</html>");
157
- return $.get("/messages/" + id + ".html", function(html) {
158
- return $iframe.find('input[name="html"]').attr('value', html).end().find('.loading').hide().end().find('input[type="submit"]').attr('disabled', null).end().find('form').submit(function() {
159
- return $(this).find('input[type="submit"]').attr('disabled', 'disabled').end().find('.loading').text('Analysing...').show();
158
+ $iframe = $('#message iframe').contents().children().html("<html>\n<head>\n<title>Analysis</title>\n" + ($('link[rel="stylesheet"]')[0].outerHTML) + "\n</head>\n<body class=\"iframe\">\n<h1>Analyse your email with Fractal</h1>\n<p><a href=\"http://getfractal.com/\" target=\"_blank\">Fractal</a> is a really neat service that applies common email design and development knowledge from <a href=\"http://www.email-standards.org/\" target=\"_blank\">Email Standards Project</a> to your HTML email and tells you what you've done wrong or what you should do instead.</p>\n<p>Please note that this <strong>sends your email to the Fractal service</strong> for analysis. Read their <a href=\"http://getfractal.com/terms\" target=\"_blank\">terms of service</a> if you're paranoid.</p>\n<p>(This output is still just raw XML. Someone keen to transform this into something prettier would be greatly appreciated!)</p>\n<form>\n<input type=\"submit\" value=\"Analyse\" /><span class=\"loading\" style=\"color: #999; display: none\">Analysing&hellip;</span>\n</form>\n</body>\n</html>");
159
+ return $form = $iframe.find('form').submit(function(e) {
160
+ e.preventDefault();
161
+ $(this).find('input[type="submit"]').attr('disabled', 'disabled').end().find('.loading').show();
162
+ return $.ajax({
163
+ url: "/messages/" + id + "/analysis.xml",
164
+ dataType: "text",
165
+ success: function(data) {
166
+ $form.replaceWith('<h2>Results</h2><pre id="result"></pre>');
167
+ return $iframe.find("#result").text(data);
168
+ }
160
169
  });
161
170
  });
162
171
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailcatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-07-08 00:00:00.000000000 +08:00
13
- default_executable:
12
+ date: 2011-10-09 00:00:00.000000000Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: activesupport
17
- requirement: &70179531391440 !ruby/object:Gem::Requirement
16
+ requirement: &70205943804660 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ~>
@@ -22,10 +21,10 @@ dependencies:
22
21
  version: '3.0'
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *70179531391440
24
+ version_requirements: *70205943804660
26
25
  - !ruby/object:Gem::Dependency
27
26
  name: eventmachine
28
- requirement: &70179531390940 !ruby/object:Gem::Requirement
27
+ requirement: &70205943804020 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ~>
@@ -33,32 +32,32 @@ dependencies:
33
32
  version: '0.12'
34
33
  type: :runtime
35
34
  prerelease: false
36
- version_requirements: *70179531390940
35
+ version_requirements: *70205943804020
37
36
  - !ruby/object:Gem::Dependency
38
- name: mail
39
- requirement: &70179531390440 !ruby/object:Gem::Requirement
37
+ name: haml
38
+ requirement: &70205943803120 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ~>
43
42
  - !ruby/object:Gem::Version
44
- version: '2.3'
43
+ version: '3.1'
45
44
  type: :runtime
46
45
  prerelease: false
47
- version_requirements: *70179531390440
46
+ version_requirements: *70205943803120
48
47
  - !ruby/object:Gem::Dependency
49
- name: sqlite3
50
- requirement: &70179531389940 !ruby/object:Gem::Requirement
48
+ name: mail
49
+ requirement: &70205943802440 !ruby/object:Gem::Requirement
51
50
  none: false
52
51
  requirements:
53
52
  - - ~>
54
53
  - !ruby/object:Gem::Version
55
- version: '1.3'
54
+ version: '2.3'
56
55
  type: :runtime
57
56
  prerelease: false
58
- version_requirements: *70179531389940
57
+ version_requirements: *70205943802440
59
58
  - !ruby/object:Gem::Dependency
60
- name: thin
61
- requirement: &70179531389460 !ruby/object:Gem::Requirement
59
+ name: sinatra
60
+ requirement: &70205943801860 !ruby/object:Gem::Requirement
62
61
  none: false
63
62
  requirements:
64
63
  - - ~>
@@ -66,54 +65,54 @@ dependencies:
66
65
  version: '1.2'
67
66
  type: :runtime
68
67
  prerelease: false
69
- version_requirements: *70179531389460
68
+ version_requirements: *70205943801860
70
69
  - !ruby/object:Gem::Dependency
71
70
  name: skinny
72
- requirement: &70179531384340 !ruby/object:Gem::Requirement
71
+ requirement: &70205943790820 !ruby/object:Gem::Requirement
73
72
  none: false
74
73
  requirements:
75
74
  - - ~>
76
75
  - !ruby/object:Gem::Version
77
- version: '0.1'
76
+ version: '0.2'
78
77
  type: :runtime
79
78
  prerelease: false
80
- version_requirements: *70179531384340
79
+ version_requirements: *70205943790820
81
80
  - !ruby/object:Gem::Dependency
82
- name: sinatra
83
- requirement: &70179531383760 !ruby/object:Gem::Requirement
81
+ name: sqlite3
82
+ requirement: &70205943790260 !ruby/object:Gem::Requirement
84
83
  none: false
85
84
  requirements:
86
85
  - - ~>
87
86
  - !ruby/object:Gem::Version
88
- version: '1.2'
87
+ version: '1.3'
89
88
  type: :runtime
90
89
  prerelease: false
91
- version_requirements: *70179531383760
90
+ version_requirements: *70205943790260
92
91
  - !ruby/object:Gem::Dependency
93
- name: haml
94
- requirement: &70179531383120 !ruby/object:Gem::Requirement
92
+ name: thin
93
+ requirement: &70205943789560 !ruby/object:Gem::Requirement
95
94
  none: false
96
95
  requirements:
97
96
  - - ~>
98
97
  - !ruby/object:Gem::Version
99
- version: '3.1'
98
+ version: '1.2'
100
99
  type: :runtime
101
100
  prerelease: false
102
- version_requirements: *70179531383120
101
+ version_requirements: *70205943789560
103
102
  - !ruby/object:Gem::Dependency
104
- name: sass
105
- requirement: &70179531382540 !ruby/object:Gem::Requirement
103
+ name: coffee-script
104
+ requirement: &70205943788860 !ruby/object:Gem::Requirement
106
105
  none: false
107
106
  requirements:
108
107
  - - ~>
109
108
  - !ruby/object:Gem::Version
110
- version: '3.1'
109
+ version: '2.2'
111
110
  type: :development
112
111
  prerelease: false
113
- version_requirements: *70179531382540
112
+ version_requirements: *70205943788860
114
113
  - !ruby/object:Gem::Dependency
115
114
  name: compass
116
- requirement: &70179531381980 !ruby/object:Gem::Requirement
115
+ requirement: &70205943788140 !ruby/object:Gem::Requirement
117
116
  none: false
118
117
  requirements:
119
118
  - - ~>
@@ -121,18 +120,40 @@ dependencies:
121
120
  version: 0.11.1
122
121
  type: :development
123
122
  prerelease: false
124
- version_requirements: *70179531381980
123
+ version_requirements: *70205943788140
125
124
  - !ruby/object:Gem::Dependency
126
- name: coffee-script
127
- requirement: &70179531381400 !ruby/object:Gem::Requirement
125
+ name: rake
126
+ requirement: &70205943787700 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: *70205943787700
135
+ - !ruby/object:Gem::Dependency
136
+ name: rdoc
137
+ requirement: &70205943787160 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: *70205943787160
146
+ - !ruby/object:Gem::Dependency
147
+ name: sass
148
+ requirement: &70205943786420 !ruby/object:Gem::Requirement
128
149
  none: false
129
150
  requirements:
130
151
  - - ~>
131
152
  - !ruby/object:Gem::Version
132
- version: '2.2'
153
+ version: '3.1'
133
154
  type: :development
134
155
  prerelease: false
135
- version_requirements: *70179531381400
156
+ version_requirements: *70205943786420
136
157
  description: ! " MailCatcher runs a super simple SMTP server which catches any\n
137
158
  \ message sent to it to display in a web interface. Run\n mailcatcher, set
138
159
  your favourite app to deliver to\n smtp://127.0.0.1:1025 instead of your default
@@ -148,6 +169,7 @@ extra_rdoc_files:
148
169
  files:
149
170
  - README.md
150
171
  - LICENSE
172
+ - VERSION
151
173
  - bin/catchmail
152
174
  - bin/mailcatcher
153
175
  - lib/mail_catcher/events.rb
@@ -157,14 +179,14 @@ files:
157
179
  - lib/mail_catcher/web.rb
158
180
  - lib/mail_catcher.rb
159
181
  - public/images/logo.png
182
+ - public/images/logo_large.png
160
183
  - public/javascripts/application.js
161
184
  - public/javascripts/date.js
162
185
  - public/javascripts/jquery.js
163
186
  - public/javascripts/modernizr.js
164
187
  - public/stylesheets/application.css
165
188
  - views/index.haml
166
- has_rdoc: true
167
- homepage: http://github.com/sj26/mailcatcher
189
+ homepage: http://mailcatcher.me
168
190
  licenses: []
169
191
  post_install_message:
170
192
  rdoc_options: []
@@ -184,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
206
  version: '0'
185
207
  requirements: []
186
208
  rubyforge_project:
187
- rubygems_version: 1.6.2
209
+ rubygems_version: 1.8.7
188
210
  signing_key:
189
211
  specification_version: 3
190
212
  summary: Runs an SMTP server, catches and displays email in a web interface.