zendesk-ar_mailer 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +81 -0
- data/LICENSE.txt +28 -0
- data/Manifest.txt +15 -0
- data/README.txt +70 -0
- data/Rakefile +60 -0
- data/bin/ar_sendmail +6 -0
- data/lib/action_mailer/ar_mailer.rb +111 -0
- data/lib/action_mailer/ar_sendmail.rb +558 -0
- data/lib/smtp_tls.rb +105 -0
- data/share/bsd/ar_sendmail +30 -0
- data/share/linux/ar_sendmail +75 -0
- data/share/linux/ar_sendmail.conf +30 -0
- data/test/action_mailer.rb +185 -0
- data/test/test_armailer.rb +51 -0
- data/test/test_arsendmail.rb +672 -0
- metadata +72 -0
data/lib/smtp_tls.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Original code believed public domain from ruby-talk or ruby-core email.
|
2
|
+
# Modifications by Kyle Maxwell <kyle@kylemaxwell.com> used under MIT license.
|
3
|
+
|
4
|
+
require "openssl"
|
5
|
+
require "net/smtp"
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
|
9
|
+
class Net::SMTP
|
10
|
+
|
11
|
+
class << self
|
12
|
+
send :remove_method, :start
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.start( address, port = nil,
|
16
|
+
helo = 'localhost.localdomain',
|
17
|
+
user = nil, secret = nil, authtype = nil, use_tls = false,
|
18
|
+
&block) # :yield: smtp
|
19
|
+
new(address, port).start(helo, user, secret, authtype, use_tls, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias tls_old_start start
|
23
|
+
|
24
|
+
def start( helo = 'localhost.localdomain',
|
25
|
+
user = nil, secret = nil, authtype = nil, use_tls = false ) # :yield: smtp
|
26
|
+
start_method = use_tls ? :do_tls_start : :do_start
|
27
|
+
if block_given?
|
28
|
+
begin
|
29
|
+
send start_method, helo, user, secret, authtype
|
30
|
+
return yield(self)
|
31
|
+
ensure
|
32
|
+
do_finish
|
33
|
+
end
|
34
|
+
else
|
35
|
+
send start_method, helo, user, secret, authtype
|
36
|
+
return self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def do_tls_start(helodomain, user, secret, authtype)
|
43
|
+
raise IOError, 'SMTP session already started' if @started
|
44
|
+
check_auth_args user, secret, authtype if user or secret
|
45
|
+
|
46
|
+
sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
|
47
|
+
@socket = Net::InternetMessageIO.new(sock)
|
48
|
+
@socket.read_timeout = 60 #@read_timeout
|
49
|
+
@socket.debug_output = STDERR #@debug_output
|
50
|
+
|
51
|
+
check_response(critical { recv_response() })
|
52
|
+
do_helo(helodomain)
|
53
|
+
|
54
|
+
raise 'openssl library not installed' unless defined?(OpenSSL)
|
55
|
+
starttls
|
56
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock)
|
57
|
+
ssl.sync_close = true
|
58
|
+
ssl.connect
|
59
|
+
@socket = Net::InternetMessageIO.new(ssl)
|
60
|
+
@socket.read_timeout = 60 #@read_timeout
|
61
|
+
@socket.debug_output = STDERR #@debug_output
|
62
|
+
do_helo(helodomain)
|
63
|
+
|
64
|
+
authenticate user, secret, authtype if user
|
65
|
+
@started = true
|
66
|
+
ensure
|
67
|
+
unless @started
|
68
|
+
# authentication failed, cancel connection.
|
69
|
+
@socket.close if not @started and @socket and not @socket.closed?
|
70
|
+
@socket = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def do_helo(helodomain)
|
75
|
+
begin
|
76
|
+
if @esmtp
|
77
|
+
ehlo helodomain
|
78
|
+
else
|
79
|
+
helo helodomain
|
80
|
+
end
|
81
|
+
rescue Net::ProtocolError
|
82
|
+
if @esmtp
|
83
|
+
@esmtp = false
|
84
|
+
@error_occured = false
|
85
|
+
retry
|
86
|
+
end
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def starttls
|
92
|
+
getok('STARTTLS')
|
93
|
+
end
|
94
|
+
|
95
|
+
alias tls_old_quit quit
|
96
|
+
|
97
|
+
def quit
|
98
|
+
begin
|
99
|
+
getok('QUIT')
|
100
|
+
rescue EOFError
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end unless Net::SMTP.private_method_defined? :do_tls_start or
|
105
|
+
Net::SMTP.method_defined? :tls?
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# PROVIDE: ar_sendmail
|
3
|
+
# REQUIRE: DAEMON
|
4
|
+
# BEFORE: LOGIN
|
5
|
+
# KEYWORD: FreeBSD shutdown
|
6
|
+
|
7
|
+
#
|
8
|
+
# Add the following lines to /etc/rc.conf to enable ar_sendmail:
|
9
|
+
#
|
10
|
+
#ar_sendmail_enable="YES"
|
11
|
+
|
12
|
+
. /etc/rc.subr
|
13
|
+
|
14
|
+
name="ar_sendmail"
|
15
|
+
rcvar=`set_rcvar`
|
16
|
+
|
17
|
+
command="/usr/local/bin/ar_sendmail"
|
18
|
+
command_interpreter="/usr/local/bin/ruby18"
|
19
|
+
|
20
|
+
# set defaults
|
21
|
+
|
22
|
+
ar_sendmail_rails_env=${ar_sendmail_rails_env:-"production"}
|
23
|
+
ar_sendmail_chdir=${ar_sendmail_chdir:-"/"}
|
24
|
+
ar_sendmail_enable=${ar_sendmail_enable:-"NO"}
|
25
|
+
ar_sendmail_flags=${ar_sendmail_flags:-"-d"}
|
26
|
+
|
27
|
+
load_rc_config $name
|
28
|
+
export RAILS_ENV=$ar_sendmail_rails_env
|
29
|
+
run_rc_command "$1"
|
30
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# ar_sendmail Startup script for ar_mailer by Adam Meehan
|
4
|
+
#
|
5
|
+
# chkconfig: - 85 15
|
6
|
+
# description: ar_sendmail manages sending emails for Rails apps.
|
7
|
+
#
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
# Config file app mailers
|
11
|
+
config_file = '/etc/ar_sendmail.conf'
|
12
|
+
|
13
|
+
begin
|
14
|
+
config = YAML::load(IO.read(config_file))
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
puts "Config file not found at '#{config_file}'!"
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
default_options = {'pidfile' => './log/ar_sendmail.pid'}.merge(config.delete('defaults') || {})
|
21
|
+
|
22
|
+
command, app_name = *ARGV
|
23
|
+
|
24
|
+
def start(app, options)
|
25
|
+
switches = ""
|
26
|
+
options.each {|k, v| switches << " --#{k} #{v}"}
|
27
|
+
STDOUT.write "Starting mailer for #{app} in #{options['environment']} mode ... "
|
28
|
+
status = system("ar_sendmail -d #{switches}") ? "started" : "failed"
|
29
|
+
puts status
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop(app, options)
|
33
|
+
path = options['chdir']
|
34
|
+
pid_file = File.expand_path(options['pidfile'], path)
|
35
|
+
if File.exist? pid_file
|
36
|
+
begin
|
37
|
+
pid = open(pid_file).read.to_i
|
38
|
+
STDOUT.write "Stopping mailer for #{app}... "
|
39
|
+
Process.kill('TERM', pid)
|
40
|
+
puts "stopped"
|
41
|
+
rescue Errno::ESRCH
|
42
|
+
puts "Mailer process does not exist. Is not running."
|
43
|
+
end
|
44
|
+
else
|
45
|
+
puts "Skipping mailer for #{app}, no pid file."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def restart(app, options)
|
50
|
+
puts "Restarting mailer for #{app} ..."
|
51
|
+
stop app, options
|
52
|
+
start app, options
|
53
|
+
end
|
54
|
+
|
55
|
+
def command_error(msg)
|
56
|
+
puts msg
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
if ['start', 'stop', 'restart'].include?(command)
|
61
|
+
apps = config
|
62
|
+
if app_name
|
63
|
+
command_error "No such app defined in ar_sendmail config" unless config.include?(app_name)
|
64
|
+
app_options = config[app_name]
|
65
|
+
command_error "Must specify chdir for app in ar_sendmail config" if app_options['chdir'].nil?
|
66
|
+
apps = {app_name => app_options}
|
67
|
+
end
|
68
|
+
|
69
|
+
apps.each do |app, options|
|
70
|
+
options = default_options.merge(options)
|
71
|
+
send(command, app, options)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
command_error "Usage: ar_sendmail {start|stop|restart} [optional app_name]"
|
75
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# This is a configuration file for ar_sendmail daemon init.d script
|
2
|
+
#
|
3
|
+
# Define the settings for each app which has a mailer that you want start
|
4
|
+
# automatically on startup.
|
5
|
+
#
|
6
|
+
# You can define an optional defaults section which will apply to all
|
7
|
+
# applications unless the setting is specified under the applications specific
|
8
|
+
# config.
|
9
|
+
#
|
10
|
+
# Settings not specified in either the defaults or app config section will
|
11
|
+
# be implied by the gem binary defaults. The option names are the same as the
|
12
|
+
# long format binary option switches. Run 'ar_sendmail -h' to see option switches
|
13
|
+
# and default values.
|
14
|
+
#
|
15
|
+
# Copy this file to /etc/ar_sendmail.conf and it will be read by the init.d
|
16
|
+
# script.
|
17
|
+
#
|
18
|
+
|
19
|
+
## Demo app config
|
20
|
+
#
|
21
|
+
#defaults:
|
22
|
+
# batch-size: 10
|
23
|
+
# max-age: 0
|
24
|
+
# delay: 60
|
25
|
+
#
|
26
|
+
#app_name:
|
27
|
+
# chdir: /var/www/apps/app_name
|
28
|
+
# environment: production
|
29
|
+
# pidfile: ./log/ar_sendmail.pid
|
30
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
require 'smtp_tls'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
class Net::SMTP
|
6
|
+
|
7
|
+
@reset_called = 0
|
8
|
+
|
9
|
+
@deliveries = []
|
10
|
+
|
11
|
+
@send_message_block = nil
|
12
|
+
|
13
|
+
@start_block = nil
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
attr_reader :deliveries
|
18
|
+
attr_reader :send_message_block
|
19
|
+
attr_accessor :reset_called
|
20
|
+
|
21
|
+
send :remove_method, :start
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.start(*args)
|
26
|
+
@start_block.call if @start_block
|
27
|
+
yield new(nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.on_send_message(&block)
|
31
|
+
@send_message_block = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.on_start(&block)
|
35
|
+
@start_block = block
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.reset
|
39
|
+
deliveries.clear
|
40
|
+
on_start
|
41
|
+
on_send_message
|
42
|
+
@reset_called = 0
|
43
|
+
end
|
44
|
+
|
45
|
+
alias test_old_reset reset if instance_methods.include? 'reset'
|
46
|
+
|
47
|
+
def reset
|
48
|
+
self.class.reset_called += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
alias test_old_send_message send_message
|
52
|
+
|
53
|
+
def send_message(mail, to, from)
|
54
|
+
return self.class.send_message_block.call(mail, to, from) unless
|
55
|
+
self.class.send_message_block.nil?
|
56
|
+
self.class.deliveries << [mail, to, from]
|
57
|
+
return "queued"
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Stub for ActionMailer::Base
|
64
|
+
|
65
|
+
module ActionMailer; end
|
66
|
+
|
67
|
+
class ActionMailer::Base
|
68
|
+
|
69
|
+
@server_settings = {}
|
70
|
+
|
71
|
+
def self.logger
|
72
|
+
o = Object.new
|
73
|
+
def o.info(arg) end
|
74
|
+
return o
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.method_missing(meth, *args)
|
78
|
+
meth.to_s =~ /deliver_(.*)/
|
79
|
+
super unless $1
|
80
|
+
new($1, *args).deliver!
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.reset
|
84
|
+
server_settings.clear
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.server_settings
|
88
|
+
@server_settings
|
89
|
+
end
|
90
|
+
|
91
|
+
def initialize(meth = nil)
|
92
|
+
send meth if meth
|
93
|
+
end
|
94
|
+
|
95
|
+
def deliver!
|
96
|
+
perform_delivery_activerecord @mail
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Stub for an ActiveRecord model
|
103
|
+
|
104
|
+
class Email
|
105
|
+
|
106
|
+
START = Time.parse 'Thu Aug 10 2006 11:19:48'
|
107
|
+
|
108
|
+
attr_accessor :from, :to, :mail, :last_send_attempt, :created_on, :id
|
109
|
+
|
110
|
+
@records = []
|
111
|
+
@id = 0
|
112
|
+
|
113
|
+
class << self; attr_accessor :records, :id; end
|
114
|
+
|
115
|
+
def self.create(record)
|
116
|
+
record = new record[:from], record[:to], record[:mail],
|
117
|
+
record[:last_send_attempt]
|
118
|
+
records << record
|
119
|
+
return record
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.destroy_all(conditions)
|
123
|
+
timeout = conditions.last
|
124
|
+
found = []
|
125
|
+
|
126
|
+
records.each do |record|
|
127
|
+
next if record.last_send_attempt == 0
|
128
|
+
next if record.created_on == 0
|
129
|
+
next unless record.created_on < timeout
|
130
|
+
record.destroy
|
131
|
+
found << record
|
132
|
+
end
|
133
|
+
|
134
|
+
found
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.find(_, conditions = nil)
|
138
|
+
return records if conditions.nil?
|
139
|
+
now = Time.now.to_i - 300
|
140
|
+
return records.select do |r|
|
141
|
+
r.last_send_attempt < now
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.reset
|
146
|
+
@id = 0
|
147
|
+
records.clear
|
148
|
+
end
|
149
|
+
|
150
|
+
def initialize(from, to, mail, last_send_attempt = nil)
|
151
|
+
@from = from
|
152
|
+
@to = to
|
153
|
+
@mail = mail
|
154
|
+
@id = self.class.id += 1
|
155
|
+
@created_on = START + @id
|
156
|
+
@last_send_attempt = last_send_attempt || 0
|
157
|
+
end
|
158
|
+
|
159
|
+
def destroy
|
160
|
+
self.class.records.delete self
|
161
|
+
self.freeze
|
162
|
+
end
|
163
|
+
|
164
|
+
def ==(other)
|
165
|
+
other.id == id
|
166
|
+
end
|
167
|
+
|
168
|
+
def save
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
Mail = Email
|
174
|
+
|
175
|
+
class String
|
176
|
+
def classify
|
177
|
+
self
|
178
|
+
end
|
179
|
+
|
180
|
+
def tableize
|
181
|
+
self.downcase
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'action_mailer'
|
3
|
+
require 'action_mailer/ar_mailer'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Pretend mailer
|
7
|
+
|
8
|
+
class Mailer < ActionMailer::ARMailer
|
9
|
+
|
10
|
+
def mail
|
11
|
+
@mail = Object.new
|
12
|
+
def @mail.encoded() 'email' end
|
13
|
+
def @mail.from() ['nobody@example.com'] end
|
14
|
+
def @mail.destinations() %w[user1@example.com user2@example.com] end
|
15
|
+
def @mail.key?(header_name) end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestARMailer < Test::Unit::TestCase
|
21
|
+
|
22
|
+
def setup
|
23
|
+
Mailer.email_class = Email
|
24
|
+
|
25
|
+
Email.records.clear
|
26
|
+
Mail.records.clear
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_self_email_class_equals
|
30
|
+
Mailer.email_class = Mail
|
31
|
+
|
32
|
+
Mailer.deliver_mail
|
33
|
+
|
34
|
+
assert_equal 2, Mail.records.length
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_perform_delivery_activerecord
|
38
|
+
Mailer.deliver_mail
|
39
|
+
|
40
|
+
assert_equal 2, Email.records.length
|
41
|
+
|
42
|
+
record = Email.records.first
|
43
|
+
assert_equal 'email', record.mail
|
44
|
+
assert_equal 'user1@example.com', record.to
|
45
|
+
assert_equal 'nobody@example.com', record.from
|
46
|
+
|
47
|
+
assert_equal 'user2@example.com', Email.records.last.to
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|