actionmailer 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionmailer might be problematic. Click here for more details.
- data/CHANGELOG +3 -0
- data/MIT-LICENSE +21 -0
- data/README +102 -0
- data/install.rb +61 -0
- data/lib/action_mailer.rb +44 -0
- data/lib/action_mailer/base.rb +111 -0
- data/lib/action_mailer/mail_helper.rb +17 -0
- data/lib/action_mailer/vendor/text/format.rb +1447 -0
- data/lib/action_mailer/vendor/tmail.rb +4 -0
- data/lib/action_mailer/vendor/tmail/address.rb +223 -0
- data/lib/action_mailer/vendor/tmail/base64.rb +52 -0
- data/lib/action_mailer/vendor/tmail/config.rb +50 -0
- data/lib/action_mailer/vendor/tmail/encode.rb +447 -0
- data/lib/action_mailer/vendor/tmail/facade.rb +531 -0
- data/lib/action_mailer/vendor/tmail/header.rb +893 -0
- data/lib/action_mailer/vendor/tmail/info.rb +16 -0
- data/lib/action_mailer/vendor/tmail/loader.rb +1 -0
- data/lib/action_mailer/vendor/tmail/mail.rb +420 -0
- data/lib/action_mailer/vendor/tmail/mailbox.rb +414 -0
- data/lib/action_mailer/vendor/tmail/mbox.rb +1 -0
- data/lib/action_mailer/vendor/tmail/net.rb +261 -0
- data/lib/action_mailer/vendor/tmail/obsolete.rb +116 -0
- data/lib/action_mailer/vendor/tmail/parser.rb +1503 -0
- data/lib/action_mailer/vendor/tmail/port.rb +358 -0
- data/lib/action_mailer/vendor/tmail/scanner.rb +22 -0
- data/lib/action_mailer/vendor/tmail/scanner_r.rb +244 -0
- data/lib/action_mailer/vendor/tmail/stringio.rb +260 -0
- data/lib/action_mailer/vendor/tmail/tmail.rb +1 -0
- data/lib/action_mailer/vendor/tmail/utils.rb +215 -0
- data/rakefile +99 -0
- data/test/fixtures/templates/signed_up.rhtml +3 -0
- data/test/fixtures/test_mailer/signed_up.rhtml +3 -0
- data/test/mail_service_test.rb +53 -0
- metadata +86 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
#
|
2
|
+
# stringio.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 1999-2003 Minero Aoki <aamine@loveruby.net>
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU Lesser General Public License version 2 or later.
|
9
|
+
#
|
10
|
+
# Id: stringio.rb,v 1.10 2003/04/27 22:02:14 aamine Exp
|
11
|
+
#
|
12
|
+
|
13
|
+
class StringInput#:nodoc:
|
14
|
+
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def new( str )
|
20
|
+
if block_given?
|
21
|
+
begin
|
22
|
+
f = super
|
23
|
+
yield f
|
24
|
+
ensure
|
25
|
+
f.close if f
|
26
|
+
end
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
alias open new
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize( str )
|
37
|
+
@src = str
|
38
|
+
@pos = 0
|
39
|
+
@closed = false
|
40
|
+
@lineno = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :lineno
|
44
|
+
|
45
|
+
def string
|
46
|
+
@src
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
def close
|
54
|
+
stream_check!
|
55
|
+
@pos = nil
|
56
|
+
@closed = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def closed?
|
60
|
+
@closed
|
61
|
+
end
|
62
|
+
|
63
|
+
def pos
|
64
|
+
stream_check!
|
65
|
+
[@pos, @src.size].min
|
66
|
+
end
|
67
|
+
|
68
|
+
alias tell pos
|
69
|
+
|
70
|
+
def seek( offset, whence = IO::SEEK_SET )
|
71
|
+
stream_check!
|
72
|
+
case whence
|
73
|
+
when IO::SEEK_SET
|
74
|
+
@pos = offset
|
75
|
+
when IO::SEEK_CUR
|
76
|
+
@pos += offset
|
77
|
+
when IO::SEEK_END
|
78
|
+
@pos = @src.size - offset
|
79
|
+
else
|
80
|
+
raise ArgumentError, "unknown seek flag: #{whence}"
|
81
|
+
end
|
82
|
+
@pos = 0 if @pos < 0
|
83
|
+
@pos = [@pos, @src.size + 1].min
|
84
|
+
offset
|
85
|
+
end
|
86
|
+
|
87
|
+
def rewind
|
88
|
+
stream_check!
|
89
|
+
@pos = 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def eof?
|
93
|
+
stream_check!
|
94
|
+
@pos > @src.size
|
95
|
+
end
|
96
|
+
|
97
|
+
def each( &block )
|
98
|
+
stream_check!
|
99
|
+
begin
|
100
|
+
@src.each(&block)
|
101
|
+
ensure
|
102
|
+
@pos = 0
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def gets
|
107
|
+
stream_check!
|
108
|
+
if idx = @src.index(?\n, @pos)
|
109
|
+
idx += 1 # "\n".size
|
110
|
+
line = @src[ @pos ... idx ]
|
111
|
+
@pos = idx
|
112
|
+
@pos += 1 if @pos == @src.size
|
113
|
+
else
|
114
|
+
line = @src[ @pos .. -1 ]
|
115
|
+
@pos = @src.size + 1
|
116
|
+
end
|
117
|
+
@lineno += 1
|
118
|
+
|
119
|
+
line
|
120
|
+
end
|
121
|
+
|
122
|
+
def getc
|
123
|
+
stream_check!
|
124
|
+
ch = @src[@pos]
|
125
|
+
@pos += 1
|
126
|
+
@pos += 1 if @pos == @src.size
|
127
|
+
ch
|
128
|
+
end
|
129
|
+
|
130
|
+
def read( len = nil )
|
131
|
+
stream_check!
|
132
|
+
return read_all unless len
|
133
|
+
str = @src[@pos, len]
|
134
|
+
@pos += len
|
135
|
+
@pos += 1 if @pos == @src.size
|
136
|
+
str
|
137
|
+
end
|
138
|
+
|
139
|
+
alias sysread read
|
140
|
+
|
141
|
+
def read_all
|
142
|
+
stream_check!
|
143
|
+
return nil if eof?
|
144
|
+
rest = @src[@pos ... @src.size]
|
145
|
+
@pos = @src.size + 1
|
146
|
+
rest
|
147
|
+
end
|
148
|
+
|
149
|
+
def stream_check!
|
150
|
+
@closed and raise IOError, 'closed stream'
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
class StringOutput#:nodoc:
|
157
|
+
|
158
|
+
class << self
|
159
|
+
|
160
|
+
def new( str = '' )
|
161
|
+
if block_given?
|
162
|
+
begin
|
163
|
+
f = super
|
164
|
+
yield f
|
165
|
+
ensure
|
166
|
+
f.close if f
|
167
|
+
end
|
168
|
+
else
|
169
|
+
super
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
alias open new
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
def initialize( str = '' )
|
178
|
+
@dest = str
|
179
|
+
@closed = false
|
180
|
+
end
|
181
|
+
|
182
|
+
def close
|
183
|
+
@closed = true
|
184
|
+
end
|
185
|
+
|
186
|
+
def closed?
|
187
|
+
@closed
|
188
|
+
end
|
189
|
+
|
190
|
+
def string
|
191
|
+
@dest
|
192
|
+
end
|
193
|
+
|
194
|
+
alias value string
|
195
|
+
alias to_str string
|
196
|
+
|
197
|
+
def size
|
198
|
+
@dest.size
|
199
|
+
end
|
200
|
+
|
201
|
+
alias pos size
|
202
|
+
|
203
|
+
def inspect
|
204
|
+
"#<#{self.class}:#{@dest ? 'open' : 'closed'},#{id}>"
|
205
|
+
end
|
206
|
+
|
207
|
+
def print( *args )
|
208
|
+
stream_check!
|
209
|
+
raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
|
210
|
+
args.each do |s|
|
211
|
+
raise ArgumentError, 'nil not allowed' if s.nil?
|
212
|
+
@dest << s.to_s
|
213
|
+
end
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
|
217
|
+
def puts( *args )
|
218
|
+
stream_check!
|
219
|
+
args.each do |str|
|
220
|
+
@dest << (s = str.to_s)
|
221
|
+
@dest << "\n" unless s[-1] == ?\n
|
222
|
+
end
|
223
|
+
@dest << "\n" if args.empty?
|
224
|
+
nil
|
225
|
+
end
|
226
|
+
|
227
|
+
def putc( ch )
|
228
|
+
stream_check!
|
229
|
+
@dest << ch.chr
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
def printf( *args )
|
234
|
+
stream_check!
|
235
|
+
@dest << sprintf(*args)
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
|
239
|
+
def write( str )
|
240
|
+
stream_check!
|
241
|
+
s = str.to_s
|
242
|
+
@dest << s
|
243
|
+
s.size
|
244
|
+
end
|
245
|
+
|
246
|
+
alias syswrite write
|
247
|
+
|
248
|
+
def <<( str )
|
249
|
+
stream_check!
|
250
|
+
@dest << str.to_s
|
251
|
+
self
|
252
|
+
end
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
def stream_check!
|
257
|
+
@closed and raise IOError, 'closed stream'
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'tmail'
|
@@ -0,0 +1,215 @@
|
|
1
|
+
#
|
2
|
+
# utils.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU Lesser General Public License version 2 or later.
|
9
|
+
#
|
10
|
+
|
11
|
+
module TMail
|
12
|
+
|
13
|
+
class SyntaxError < StandardError; end
|
14
|
+
|
15
|
+
|
16
|
+
def TMail.new_boundary
|
17
|
+
'mimepart_' + random_tag
|
18
|
+
end
|
19
|
+
|
20
|
+
def TMail.new_message_id( fqdn = nil )
|
21
|
+
fqdn ||= ::Socket.gethostname
|
22
|
+
"<#{random_tag()}@#{fqdn}.tmail>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def TMail.random_tag
|
26
|
+
@uniq += 1
|
27
|
+
t = Time.now
|
28
|
+
sprintf('%x%x_%x%x%d%x',
|
29
|
+
t.to_i, t.tv_usec,
|
30
|
+
$$, Thread.current.id, @uniq, rand(255))
|
31
|
+
end
|
32
|
+
private_class_method :random_tag
|
33
|
+
|
34
|
+
@uniq = 0
|
35
|
+
|
36
|
+
|
37
|
+
module TextUtils
|
38
|
+
|
39
|
+
aspecial = '()<>[]:;.@\\,"'
|
40
|
+
tspecial = '()<>[];:@\\,"/?='
|
41
|
+
lwsp = " \t\r\n"
|
42
|
+
control = '\x00-\x1f\x7f-\xff'
|
43
|
+
|
44
|
+
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
|
45
|
+
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
46
|
+
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
|
47
|
+
CONTROL_CHAR = /[#{control}]/n
|
48
|
+
|
49
|
+
def atom_safe?( str )
|
50
|
+
not ATOM_UNSAFE === str
|
51
|
+
end
|
52
|
+
|
53
|
+
def quote_atom( str )
|
54
|
+
(ATOM_UNSAFE === str) ? dquote(str) : str
|
55
|
+
end
|
56
|
+
|
57
|
+
def quote_phrase( str )
|
58
|
+
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
59
|
+
end
|
60
|
+
|
61
|
+
def token_safe?( str )
|
62
|
+
not TOKEN_UNSAFE === str
|
63
|
+
end
|
64
|
+
|
65
|
+
def quote_token( str )
|
66
|
+
(TOKEN_UNSAFE === str) ? dquote(str) : str
|
67
|
+
end
|
68
|
+
|
69
|
+
def dquote( str )
|
70
|
+
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
|
71
|
+
end
|
72
|
+
private :dquote
|
73
|
+
|
74
|
+
|
75
|
+
def join_domain( arr )
|
76
|
+
arr.map {|i|
|
77
|
+
if /\A\[.*\]\z/ === i
|
78
|
+
i
|
79
|
+
else
|
80
|
+
quote_atom(i)
|
81
|
+
end
|
82
|
+
}.join('.')
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
ZONESTR_TABLE = {
|
87
|
+
'jst' => 9 * 60,
|
88
|
+
'eet' => 2 * 60,
|
89
|
+
'bst' => 1 * 60,
|
90
|
+
'met' => 1 * 60,
|
91
|
+
'gmt' => 0,
|
92
|
+
'utc' => 0,
|
93
|
+
'ut' => 0,
|
94
|
+
'nst' => -(3 * 60 + 30),
|
95
|
+
'ast' => -4 * 60,
|
96
|
+
'edt' => -4 * 60,
|
97
|
+
'est' => -5 * 60,
|
98
|
+
'cdt' => -5 * 60,
|
99
|
+
'cst' => -6 * 60,
|
100
|
+
'mdt' => -6 * 60,
|
101
|
+
'mst' => -7 * 60,
|
102
|
+
'pdt' => -7 * 60,
|
103
|
+
'pst' => -8 * 60,
|
104
|
+
'a' => -1 * 60,
|
105
|
+
'b' => -2 * 60,
|
106
|
+
'c' => -3 * 60,
|
107
|
+
'd' => -4 * 60,
|
108
|
+
'e' => -5 * 60,
|
109
|
+
'f' => -6 * 60,
|
110
|
+
'g' => -7 * 60,
|
111
|
+
'h' => -8 * 60,
|
112
|
+
'i' => -9 * 60,
|
113
|
+
# j not use
|
114
|
+
'k' => -10 * 60,
|
115
|
+
'l' => -11 * 60,
|
116
|
+
'm' => -12 * 60,
|
117
|
+
'n' => 1 * 60,
|
118
|
+
'o' => 2 * 60,
|
119
|
+
'p' => 3 * 60,
|
120
|
+
'q' => 4 * 60,
|
121
|
+
'r' => 5 * 60,
|
122
|
+
's' => 6 * 60,
|
123
|
+
't' => 7 * 60,
|
124
|
+
'u' => 8 * 60,
|
125
|
+
'v' => 9 * 60,
|
126
|
+
'w' => 10 * 60,
|
127
|
+
'x' => 11 * 60,
|
128
|
+
'y' => 12 * 60,
|
129
|
+
'z' => 0 * 60
|
130
|
+
}
|
131
|
+
|
132
|
+
def timezone_string_to_unixtime( str )
|
133
|
+
if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
|
134
|
+
sec = (m[2].to_i * 60 + m[3].to_i) * 60
|
135
|
+
m[1] == '-' ? -sec : sec
|
136
|
+
else
|
137
|
+
min = ZONESTR_TABLE[str.downcase] or
|
138
|
+
raise SyntaxError, "wrong timezone format '#{str}'"
|
139
|
+
min * 60
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
|
145
|
+
MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
|
146
|
+
Jul Aug Sep Oct Nov Dec TMailBUG )
|
147
|
+
|
148
|
+
def time2str( tm )
|
149
|
+
# [ruby-list:7928]
|
150
|
+
gmt = Time.at(tm.to_i)
|
151
|
+
gmt.gmtime
|
152
|
+
offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
|
153
|
+
|
154
|
+
# DO NOT USE strftime: setlocale() breaks it
|
155
|
+
sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
|
156
|
+
WDAY[tm.wday], tm.mday, MONTH[tm.month],
|
157
|
+
tm.year, tm.hour, tm.min, tm.sec,
|
158
|
+
*(offset / 60).divmod(60)
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
|
163
|
+
|
164
|
+
def message_id?( str )
|
165
|
+
MESSAGE_ID === str
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
|
170
|
+
|
171
|
+
def mime_encoded?( str )
|
172
|
+
MIME_ENCODED === str
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def decode_params( hash )
|
177
|
+
new = Hash.new
|
178
|
+
encoded = nil
|
179
|
+
hash.each do |key, value|
|
180
|
+
if m = /\*(?:(\d+)\*)?\z/.match(key)
|
181
|
+
((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
|
182
|
+
else
|
183
|
+
new[key] = to_kcode(value)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
if encoded
|
187
|
+
encoded.each do |key, strings|
|
188
|
+
new[key] = decode_RFC2231(strings.join(''))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
new
|
193
|
+
end
|
194
|
+
|
195
|
+
NKF_FLAGS = {
|
196
|
+
'EUC' => '-e -m',
|
197
|
+
'SJIS' => '-s -m'
|
198
|
+
}
|
199
|
+
|
200
|
+
def to_kcode( str )
|
201
|
+
flag = NKF_FLAGS[$KCODE] or return str
|
202
|
+
NKF.nkf(flag, str)
|
203
|
+
end
|
204
|
+
|
205
|
+
RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
|
206
|
+
|
207
|
+
def decode_RFC2231( str )
|
208
|
+
m = RFC2231_ENCODED.match(str) or return str
|
209
|
+
NKF.nkf(NKF_FLAGS[$KCODE],
|
210
|
+
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|