mailcatcher 0.7.0 → 0.8.0.beta4
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +40 -12
- data/bin/catchmail +1 -0
- data/bin/mailcatcher +1 -0
- data/lib/mail_catcher.rb +21 -29
- data/lib/mail_catcher/bus.rb +7 -0
- data/lib/mail_catcher/mail.rb +25 -8
- data/lib/mail_catcher/smtp.rb +15 -2
- data/lib/mail_catcher/version.rb +3 -1
- data/lib/mail_catcher/web.rb +2 -0
- data/lib/mail_catcher/web/application.rb +6 -6
- data/lib/mailcatcher.rb +2 -0
- data/public/assets/logo.png +0 -0
- data/public/assets/logo_2x.png +0 -0
- data/public/assets/logo_large.png +0 -0
- data/public/assets/mailcatcher.css +1 -1
- data/public/assets/mailcatcher.js +5 -5
- data/public/favicon.ico +0 -0
- data/views/index.erb +8 -1
- metadata +54 -25
- metadata.gz.sig +0 -0
- data/lib/mail_catcher/events.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ccfb98c689953785fe9c132ac154e90f2fcf5ead0c433de7611b64c1f7f89b98
|
4
|
+
data.tar.gz: 0c87d50eece53958f858966c9f607bef6bb885dbf0a4df9938bf4b5f5616900d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1b0fae5875ae31b39700d0df1fd08c04b87ea2cd20cd7c45799c456af7cc2e4c9f4a2fc5e366b1f9ff24e0e35355752b7349fc8c991cebd8f1a664b159f2ddc
|
7
|
+
data.tar.gz: 4f051455ddc9718c69450318b8420bd74f7d7acea1d20fb42be18751b3c537f73b38550f807a6a0ead01544b9d84b288a7a7a77e2856ad0e782337dfa21706fc
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/README.md
CHANGED
@@ -26,7 +26,41 @@ MailCatcher runs a super simple SMTP server which catches any message sent to it
|
|
26
26
|
3. Go to http://127.0.0.1:1080/
|
27
27
|
4. Send mail through smtp://127.0.0.1:1025
|
28
28
|
|
29
|
-
|
29
|
+
### Command Line Options
|
30
|
+
|
31
|
+
Use `mailcatcher --help` to see the command line options.
|
32
|
+
|
33
|
+
```
|
34
|
+
Usage: mailcatcher [options]
|
35
|
+
--ip IP Set the ip address of both servers
|
36
|
+
--smtp-ip IP Set the ip address of the smtp server
|
37
|
+
--smtp-port PORT Set the port of the smtp server
|
38
|
+
--http-ip IP Set the ip address of the http server
|
39
|
+
--http-port PORT Set the port address of the http server
|
40
|
+
--http-path PATH Add a prefix to all HTTP paths
|
41
|
+
--no-quit Don't allow quitting the process
|
42
|
+
-f, --foreground Run in the foreground
|
43
|
+
-b, --browse Open web browser
|
44
|
+
-v, --verbose Be more verbose
|
45
|
+
-h, --help Display this help information
|
46
|
+
```
|
47
|
+
|
48
|
+
### Ruby
|
49
|
+
|
50
|
+
If you have trouble with the setup commands, make sure you have [Ruby installed](https://www.ruby-lang.org/en/documentation/installation/):
|
51
|
+
|
52
|
+
```
|
53
|
+
ruby -v
|
54
|
+
gem environment
|
55
|
+
```
|
56
|
+
|
57
|
+
You might need to install build tools for some of the gem dependencies. On Debian or Ubuntu, `apt install build-essential`. On macOS, `xcode-select --install`.
|
58
|
+
|
59
|
+
If you encounter issues installing [thin](https://rubygems.org/gems/thin), try:
|
60
|
+
|
61
|
+
```
|
62
|
+
gem install thin -v 1.5.1 -- --with-cflags="-Wno-error=implicit-function-declaration"
|
63
|
+
```
|
30
64
|
|
31
65
|
### Bundler
|
32
66
|
|
@@ -51,11 +85,11 @@ To set up your rails app, I recommend adding this to your `environments/developm
|
|
51
85
|
|
52
86
|
### PHP
|
53
87
|
|
54
|
-
For projects using PHP, or PHP frameworks and application platforms like Drupal, you can set [PHP's mail configuration](
|
88
|
+
For projects using PHP, or PHP frameworks and application platforms like Drupal, you can set [PHP's mail configuration](https://www.php.net/manual/en/mail.configuration.php) in your [php.ini](https://www.php.net/manual/en/configuration.file.php) to send via MailCatcher with:
|
55
89
|
|
56
90
|
sendmail_path = /usr/bin/env catchmail -f some@from.address
|
57
91
|
|
58
|
-
You can do this in your [Apache configuration](
|
92
|
+
You can do this in your [Apache configuration](https://www.php.net/manual/en/configuration.changes.php) like so:
|
59
93
|
|
60
94
|
php_admin_value sendmail_path "/usr/bin/env catchmail -f some@from.address"
|
61
95
|
|
@@ -85,13 +119,7 @@ A fairly RESTful URL schema means you can download a list of messages in JSON fr
|
|
85
119
|
## Caveats
|
86
120
|
|
87
121
|
* Mail processing is fairly basic but easily modified. If something doesn't work for you, fork and fix it or [file an issue][mailcatcher-issues] and let me know. Include the whole message you're having problems with.
|
88
|
-
*
|
89
|
-
|
90
|
-
## TODO
|
91
|
-
|
92
|
-
* Add mail delivery on request, optionally multiple times.
|
93
|
-
* Compatibility testing against CampaignMonitor's [design guidelines](http://www.campaignmonitor.com/design-guidelines/) and [CSS support matrix](http://www.campaignmonitor.com/css/).
|
94
|
-
* Forward mail to rendering service, maybe CampaignMonitor?
|
122
|
+
* Encodings are difficult. MailCatcher does not completely support utf-8 straight over the wire, you must use a mail library which encodes things properly based on SMTP server capabilities.
|
95
123
|
|
96
124
|
## Thanks
|
97
125
|
|
@@ -103,10 +131,10 @@ I work on MailCatcher mostly in my own spare time. If you've found Mailcatcher u
|
|
103
131
|
|
104
132
|
## License
|
105
133
|
|
106
|
-
Copyright © 2010-
|
134
|
+
Copyright © 2010-2019 Samuel Cochran (sj26@sj26.com). Released under the MIT License, see [LICENSE][license] for details.
|
107
135
|
|
108
136
|
[donate]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=522WUPLRWUSKE
|
109
137
|
[license]: https://github.com/sj26/mailcatcher/blob/master/LICENSE
|
110
138
|
[mailcatcher-github]: https://github.com/sj26/mailcatcher
|
111
139
|
[mailcatcher-issues]: https://github.com/sj26/mailcatcher/issues
|
112
|
-
[websockets]:
|
140
|
+
[websockets]: https://tools.ietf.org/html/rfc6455
|
data/bin/catchmail
CHANGED
data/bin/mailcatcher
CHANGED
data/lib/mail_catcher.rb
CHANGED
@@ -1,12 +1,4 @@
|
|
1
|
-
#
|
2
|
-
# repeat the invention of Bundler all over again.
|
3
|
-
gem "eventmachine", "1.0.9.1"
|
4
|
-
gem "mail", "~> 2.3"
|
5
|
-
gem "rack", "~> 1.5"
|
6
|
-
gem "sinatra", "~> 1.2"
|
7
|
-
gem "sqlite3", "~> 1.3"
|
8
|
-
gem "thin", "~> 1.5.0"
|
9
|
-
gem "skinny", "~> 0.2.3"
|
1
|
+
# frozen_string_literal: true
|
10
2
|
|
11
3
|
require "open3"
|
12
4
|
require "optparse"
|
@@ -26,7 +18,7 @@ end
|
|
26
18
|
require "mail_catcher/version"
|
27
19
|
|
28
20
|
module MailCatcher extend self
|
29
|
-
autoload :
|
21
|
+
autoload :Bus, "mail_catcher/bus"
|
30
22
|
autoload :Mail, "mail_catcher/mail"
|
31
23
|
autoload :Smtp, "mail_catcher/smtp"
|
32
24
|
autoload :Web, "mail_catcher/web"
|
@@ -45,18 +37,10 @@ module MailCatcher extend self
|
|
45
37
|
end
|
46
38
|
end
|
47
39
|
|
48
|
-
def mac?
|
49
|
-
RbConfig::CONFIG["host_os"] =~ /darwin/
|
50
|
-
end
|
51
|
-
|
52
40
|
def windows?
|
53
41
|
RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
54
42
|
end
|
55
43
|
|
56
|
-
def macruby?
|
57
|
-
mac? and const_defined? :MACRUBY_VERSION
|
58
|
-
end
|
59
|
-
|
60
44
|
def browseable?
|
61
45
|
windows? or which? "open"
|
62
46
|
end
|
@@ -77,7 +61,7 @@ module MailCatcher extend self
|
|
77
61
|
puts "*** #{message}: #{context.inspect}"
|
78
62
|
puts " Exception: #{exception}"
|
79
63
|
puts " Backtrace:", *exception.backtrace.map { |line| " #{line.sub(gems_regexp, gems_replace)}" }
|
80
|
-
puts " Please submit this as an issue at
|
64
|
+
puts " Please submit this as an issue at https://github.com/sj26/mailcatcher/issues"
|
81
65
|
end
|
82
66
|
|
83
67
|
@@defaults = {
|
@@ -86,6 +70,7 @@ module MailCatcher extend self
|
|
86
70
|
:http_ip => "127.0.0.1",
|
87
71
|
:http_port => "1080",
|
88
72
|
:http_path => "/",
|
73
|
+
:messages_limit => nil,
|
89
74
|
:verbose => false,
|
90
75
|
:daemon => !windows?,
|
91
76
|
:browse => false,
|
@@ -105,6 +90,9 @@ module MailCatcher extend self
|
|
105
90
|
OptionParser.new do |parser|
|
106
91
|
parser.banner = "Usage: mailcatcher [options]"
|
107
92
|
parser.version = VERSION
|
93
|
+
parser.separator ""
|
94
|
+
parser.separator "MailCatcher v#{VERSION}"
|
95
|
+
parser.separator ""
|
108
96
|
|
109
97
|
parser.on("--ip IP", "Set the ip address of both servers") do |ip|
|
110
98
|
options[:smtp_ip] = options[:http_ip] = ip
|
@@ -126,6 +114,10 @@ module MailCatcher extend self
|
|
126
114
|
options[:http_port] = port
|
127
115
|
end
|
128
116
|
|
117
|
+
parser.on("--messages-limit COUNT", Integer, "Only keep up to COUNT most recent messages") do |count|
|
118
|
+
options[:messages_limit] = count
|
119
|
+
end
|
120
|
+
|
129
121
|
parser.on("--http-path PATH", String, "Add a prefix to all HTTP paths") do |path|
|
130
122
|
clean_path = Rack::Utils.clean_path_info("/#{path}")
|
131
123
|
|
@@ -136,13 +128,6 @@ module MailCatcher extend self
|
|
136
128
|
options[:quit] = false
|
137
129
|
end
|
138
130
|
|
139
|
-
if mac?
|
140
|
-
parser.on("--[no-]growl") do |growl|
|
141
|
-
puts "Growl is no longer supported"
|
142
|
-
exit -2
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
131
|
unless windows?
|
147
132
|
parser.on("-f", "--foreground", "Run in the foreground") do
|
148
133
|
options[:daemon] = false
|
@@ -159,10 +144,15 @@ module MailCatcher extend self
|
|
159
144
|
options[:verbose] = true
|
160
145
|
end
|
161
146
|
|
162
|
-
parser.
|
147
|
+
parser.on_tail("-h", "--help", "Display this help information") do
|
163
148
|
puts parser
|
164
149
|
exit
|
165
150
|
end
|
151
|
+
|
152
|
+
parser.on_tail("--version", "Display the current version") do
|
153
|
+
puts "MailCatcher v#{VERSION}"
|
154
|
+
exit
|
155
|
+
end
|
166
156
|
end.parse!
|
167
157
|
end
|
168
158
|
end
|
@@ -181,7 +171,7 @@ module MailCatcher extend self
|
|
181
171
|
$stdout.sync = $stderr.sync = true
|
182
172
|
end
|
183
173
|
|
184
|
-
puts "Starting MailCatcher"
|
174
|
+
puts "Starting MailCatcher v#{VERSION}"
|
185
175
|
|
186
176
|
Thin::Logging.debug = development?
|
187
177
|
Thin::Logging.silent = !development?
|
@@ -223,6 +213,8 @@ module MailCatcher extend self
|
|
223
213
|
end
|
224
214
|
|
225
215
|
def quit!
|
216
|
+
MailCatcher::Bus.push(type: "quit")
|
217
|
+
|
226
218
|
EventMachine.next_tick { EventMachine.stop_event_loop }
|
227
219
|
end
|
228
220
|
|
@@ -233,7 +225,7 @@ protected
|
|
233
225
|
end
|
234
226
|
|
235
227
|
def http_url
|
236
|
-
"http://#{@@options[:http_ip]}:#{@@options[:http_port]}#{@@options[:http_path]}"
|
228
|
+
"http://#{@@options[:http_ip]}:#{@@options[:http_port]}#{@@options[:http_path]}".chomp("/")
|
237
229
|
end
|
238
230
|
|
239
231
|
def rescue_port port
|
data/lib/mail_catcher/mail.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "eventmachine"
|
2
4
|
require "json"
|
3
5
|
require "mail"
|
@@ -30,9 +32,11 @@ module MailCatcher::Mail extend self
|
|
30
32
|
charset TEXT,
|
31
33
|
body BLOB,
|
32
34
|
size INTEGER,
|
33
|
-
created_at DATETIME DEFAULT CURRENT_DATETIME
|
35
|
+
created_at DATETIME DEFAULT CURRENT_DATETIME,
|
36
|
+
FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE
|
34
37
|
)
|
35
38
|
SQL
|
39
|
+
db.foreign_keys = true
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
@@ -54,7 +58,7 @@ module MailCatcher::Mail extend self
|
|
54
58
|
|
55
59
|
EventMachine.next_tick do
|
56
60
|
message = MailCatcher::Mail.message message_id
|
57
|
-
MailCatcher::
|
61
|
+
MailCatcher::Bus.push(type: "add", message: message)
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
@@ -151,16 +155,29 @@ module MailCatcher::Mail extend self
|
|
151
155
|
|
152
156
|
def delete!
|
153
157
|
@delete_all_messages_query ||= db.prepare "DELETE FROM message"
|
154
|
-
@
|
158
|
+
@delete_all_messages_query.execute
|
155
159
|
|
156
|
-
|
157
|
-
|
160
|
+
EventMachine.next_tick do
|
161
|
+
MailCatcher::Bus.push(type: "clear")
|
162
|
+
end
|
158
163
|
end
|
159
164
|
|
160
165
|
def delete_message!(message_id)
|
161
166
|
@delete_messages_query ||= db.prepare "DELETE FROM message WHERE id = ?"
|
162
|
-
@
|
163
|
-
|
164
|
-
|
167
|
+
@delete_messages_query.execute(message_id)
|
168
|
+
|
169
|
+
EventMachine.next_tick do
|
170
|
+
MailCatcher::Bus.push(type: "remove", id: message_id)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def delete_older_messages!(count = MailCatcher.options[:messages_limit])
|
175
|
+
return if count.nil?
|
176
|
+
@older_messages_query ||= db.prepare "SELECT id FROM message WHERE id NOT IN (SELECT id FROM message ORDER BY created_at DESC LIMIT ?)"
|
177
|
+
@older_messages_query.execute(count).map do |row|
|
178
|
+
Hash[row.fields.zip(row)]
|
179
|
+
end.each do |message|
|
180
|
+
delete_message!(message["id"])
|
181
|
+
end
|
165
182
|
end
|
166
183
|
end
|
data/lib/mail_catcher/smtp.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "eventmachine"
|
2
4
|
|
3
5
|
require "mail_catcher/mail"
|
4
6
|
|
5
7
|
class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
6
8
|
# We override EM's mail from processing to allow multiple mail-from commands
|
7
|
-
# per [RFC 2821](
|
9
|
+
# per [RFC 2821](https://tools.ietf.org/html/rfc2821#section-4.1.1.2)
|
8
10
|
def process_mail_from sender
|
9
11
|
if @state.include? :mail_from
|
10
12
|
@state -= [:mail_from, :rcpt, :data]
|
13
|
+
|
11
14
|
receive_reset
|
12
15
|
end
|
13
16
|
|
@@ -20,30 +23,40 @@ class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
|
20
23
|
|
21
24
|
def receive_reset
|
22
25
|
@current_message = nil
|
26
|
+
|
23
27
|
true
|
24
28
|
end
|
25
29
|
|
26
30
|
def receive_sender(sender)
|
31
|
+
# EventMachine SMTP advertises size extensions [https://tools.ietf.org/html/rfc1870]
|
32
|
+
# so strip potential " SIZE=..." suffixes from senders
|
33
|
+
sender = $` if sender =~ / SIZE=\d+\z/
|
34
|
+
|
27
35
|
current_message[:sender] = sender
|
36
|
+
|
28
37
|
true
|
29
38
|
end
|
30
39
|
|
31
40
|
def receive_recipient(recipient)
|
32
41
|
current_message[:recipients] ||= []
|
33
42
|
current_message[:recipients] << recipient
|
43
|
+
|
34
44
|
true
|
35
45
|
end
|
36
46
|
|
37
47
|
def receive_data_chunk(lines)
|
38
|
-
current_message[:source] ||= ""
|
48
|
+
current_message[:source] ||= +""
|
49
|
+
|
39
50
|
lines.each do |line|
|
40
51
|
current_message[:source] << line << "\r\n"
|
41
52
|
end
|
53
|
+
|
42
54
|
true
|
43
55
|
end
|
44
56
|
|
45
57
|
def receive_message
|
46
58
|
MailCatcher::Mail.add_message current_message
|
59
|
+
MailCatcher::Mail.delete_older_messages!
|
47
60
|
puts "==> SMTP: Received message from '#{current_message[:sender]}' (#{current_message[:source].length} bytes)"
|
48
61
|
true
|
49
62
|
rescue => exception
|
data/lib/mail_catcher/version.rb
CHANGED
data/lib/mail_catcher/web.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pathname"
|
2
4
|
require "net/http"
|
3
5
|
require "uri"
|
@@ -5,7 +7,7 @@ require "uri"
|
|
5
7
|
require "sinatra"
|
6
8
|
require "skinny"
|
7
9
|
|
8
|
-
require "mail_catcher/
|
10
|
+
require "mail_catcher/bus"
|
9
11
|
require "mail_catcher/mail"
|
10
12
|
|
11
13
|
class Sinatra::Request
|
@@ -62,7 +64,7 @@ module MailCatcher
|
|
62
64
|
if request.websocket?
|
63
65
|
request.websocket!(
|
64
66
|
:on_start => proc do |websocket|
|
65
|
-
|
67
|
+
bus_subscription = MailCatcher::Bus.subscribe do |message|
|
66
68
|
begin
|
67
69
|
websocket.send_message(JSON.generate(message))
|
68
70
|
rescue => exception
|
@@ -71,7 +73,7 @@ module MailCatcher
|
|
71
73
|
end
|
72
74
|
|
73
75
|
websocket.on_close do |*|
|
74
|
-
|
76
|
+
MailCatcher::Bus.unsubscribe bus_subscription
|
75
77
|
end
|
76
78
|
end)
|
77
79
|
else
|
@@ -95,9 +97,7 @@ module MailCatcher
|
|
95
97
|
("html" if Mail.message_has_html? id),
|
96
98
|
("plain" if Mail.message_has_plain? id)
|
97
99
|
].compact,
|
98
|
-
"attachments" => Mail.message_attachments(id)
|
99
|
-
attachment.merge({"href" => "/messages/#{escape(id)}/parts/#{escape(attachment["cid"])}"})
|
100
|
-
end,
|
100
|
+
"attachments" => Mail.message_attachments(id),
|
101
101
|
}))
|
102
102
|
else
|
103
103
|
not_found
|
data/lib/mailcatcher.rb
CHANGED
data/public/assets/logo.png
CHANGED
Binary file
|