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 +1 -0
- data/lib/mail_catcher.rb +1 -0
- data/lib/mail_catcher/events.rb +2 -4
- data/lib/mail_catcher/growl.rb +2 -1
- data/lib/mail_catcher/mail.rb +118 -118
- data/lib/mail_catcher/smtp.rb +36 -38
- data/lib/mail_catcher/web.rb +106 -92
- data/public/images/logo_large.png +0 -0
- data/public/javascripts/application.js +14 -5
- metadata +64 -42
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.2
|
data/lib/mail_catcher.rb
CHANGED
@@ -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
|
data/lib/mail_catcher/events.rb
CHANGED
data/lib/mail_catcher/growl.rb
CHANGED
@@ -6,7 +6,8 @@ module MailCatcher
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def notify message
|
9
|
-
|
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
|
data/lib/mail_catcher/mail.rb
CHANGED
@@ -3,152 +3,152 @@ require 'sqlite3'
|
|
3
3
|
require 'eventmachine'
|
4
4
|
|
5
5
|
module MailCatcher::Mail
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
134
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
151
|
-
|
152
|
-
end
|
151
|
+
@@delete_messages_query.execute and
|
152
|
+
@@delete_message_parts_query.execute
|
153
153
|
end
|
154
154
|
end
|
data/lib/mail_catcher/smtp.rb
CHANGED
@@ -1,48 +1,46 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
4
|
+
def current_message
|
5
|
+
@current_message ||= {}
|
6
|
+
end
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def receive_reset
|
9
|
+
@current_message = nil
|
10
|
+
true
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
def receive_sender(sender)
|
14
|
+
current_message[:sender] = sender
|
15
|
+
true
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def receive_recipient(recipient)
|
19
|
+
current_message[:recipients] ||= []
|
20
|
+
current_message[:recipients] << recipient
|
21
|
+
true
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def receive_data_chunk(lines)
|
25
|
+
current_message[:source] ||= ""
|
26
|
+
current_message[:source] += lines.join("\n")
|
27
|
+
true
|
28
|
+
end
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
data/lib/mail_catcher/web.rb
CHANGED
@@ -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
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
16
|
+
get '/' do
|
17
|
+
haml :index
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
delete '/' do
|
21
|
+
MailCatcher.quit!
|
22
|
+
status 204
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
delete '/messages' do
|
42
|
+
MailCatcher::Mail.delete!
|
43
|
+
status 204
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
69
|
+
body = part["body"]
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
+
# Rewrite body to link to embedded attachments served by cid
|
72
|
+
body.gsub! /cid:([^'"> ]+)/, "#{id}/parts/\\1"
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
+
# Rewrite body to open links in a new window
|
75
|
+
body.gsub! /<a\s+/, '<a target="_blank" '
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
77
|
+
body
|
78
|
+
else
|
79
|
+
not_found
|
79
80
|
end
|
81
|
+
end
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
123
|
-
|
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
|
Binary file
|
@@ -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
|
157
|
-
return
|
158
|
-
|
159
|
-
|
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…</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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *70205943804660
|
26
25
|
- !ruby/object:Gem::Dependency
|
27
26
|
name: eventmachine
|
28
|
-
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: *
|
35
|
+
version_requirements: *70205943804020
|
37
36
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
39
|
-
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: '
|
43
|
+
version: '3.1'
|
45
44
|
type: :runtime
|
46
45
|
prerelease: false
|
47
|
-
version_requirements: *
|
46
|
+
version_requirements: *70205943803120
|
48
47
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
50
|
-
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: '
|
54
|
+
version: '2.3'
|
56
55
|
type: :runtime
|
57
56
|
prerelease: false
|
58
|
-
version_requirements: *
|
57
|
+
version_requirements: *70205943802440
|
59
58
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
61
|
-
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: *
|
68
|
+
version_requirements: *70205943801860
|
70
69
|
- !ruby/object:Gem::Dependency
|
71
70
|
name: skinny
|
72
|
-
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.
|
76
|
+
version: '0.2'
|
78
77
|
type: :runtime
|
79
78
|
prerelease: false
|
80
|
-
version_requirements: *
|
79
|
+
version_requirements: *70205943790820
|
81
80
|
- !ruby/object:Gem::Dependency
|
82
|
-
name:
|
83
|
-
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.
|
87
|
+
version: '1.3'
|
89
88
|
type: :runtime
|
90
89
|
prerelease: false
|
91
|
-
version_requirements: *
|
90
|
+
version_requirements: *70205943790260
|
92
91
|
- !ruby/object:Gem::Dependency
|
93
|
-
name:
|
94
|
-
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: '
|
98
|
+
version: '1.2'
|
100
99
|
type: :runtime
|
101
100
|
prerelease: false
|
102
|
-
version_requirements: *
|
101
|
+
version_requirements: *70205943789560
|
103
102
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
105
|
-
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: '
|
109
|
+
version: '2.2'
|
111
110
|
type: :development
|
112
111
|
prerelease: false
|
113
|
-
version_requirements: *
|
112
|
+
version_requirements: *70205943788860
|
114
113
|
- !ruby/object:Gem::Dependency
|
115
114
|
name: compass
|
116
|
-
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: *
|
123
|
+
version_requirements: *70205943788140
|
125
124
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
127
|
-
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: '
|
153
|
+
version: '3.1'
|
133
154
|
type: :development
|
134
155
|
prerelease: false
|
135
|
-
version_requirements: *
|
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
|
-
|
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.
|
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.
|