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.

Files changed (34) hide show
  1. data/CHANGELOG +3 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +102 -0
  4. data/install.rb +61 -0
  5. data/lib/action_mailer.rb +44 -0
  6. data/lib/action_mailer/base.rb +111 -0
  7. data/lib/action_mailer/mail_helper.rb +17 -0
  8. data/lib/action_mailer/vendor/text/format.rb +1447 -0
  9. data/lib/action_mailer/vendor/tmail.rb +4 -0
  10. data/lib/action_mailer/vendor/tmail/address.rb +223 -0
  11. data/lib/action_mailer/vendor/tmail/base64.rb +52 -0
  12. data/lib/action_mailer/vendor/tmail/config.rb +50 -0
  13. data/lib/action_mailer/vendor/tmail/encode.rb +447 -0
  14. data/lib/action_mailer/vendor/tmail/facade.rb +531 -0
  15. data/lib/action_mailer/vendor/tmail/header.rb +893 -0
  16. data/lib/action_mailer/vendor/tmail/info.rb +16 -0
  17. data/lib/action_mailer/vendor/tmail/loader.rb +1 -0
  18. data/lib/action_mailer/vendor/tmail/mail.rb +420 -0
  19. data/lib/action_mailer/vendor/tmail/mailbox.rb +414 -0
  20. data/lib/action_mailer/vendor/tmail/mbox.rb +1 -0
  21. data/lib/action_mailer/vendor/tmail/net.rb +261 -0
  22. data/lib/action_mailer/vendor/tmail/obsolete.rb +116 -0
  23. data/lib/action_mailer/vendor/tmail/parser.rb +1503 -0
  24. data/lib/action_mailer/vendor/tmail/port.rb +358 -0
  25. data/lib/action_mailer/vendor/tmail/scanner.rb +22 -0
  26. data/lib/action_mailer/vendor/tmail/scanner_r.rb +244 -0
  27. data/lib/action_mailer/vendor/tmail/stringio.rb +260 -0
  28. data/lib/action_mailer/vendor/tmail/tmail.rb +1 -0
  29. data/lib/action_mailer/vendor/tmail/utils.rb +215 -0
  30. data/rakefile +99 -0
  31. data/test/fixtures/templates/signed_up.rhtml +3 -0
  32. data/test/fixtures/test_mailer/signed_up.rhtml +3 -0
  33. data/test/mail_service_test.rb +53 -0
  34. metadata +86 -0
@@ -0,0 +1,358 @@
1
+ #
2
+ # port.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
+ require 'tmail/stringio'
12
+
13
+
14
+ module TMail
15
+
16
+ class Port
17
+ def reproducible?
18
+ false
19
+ end
20
+ end
21
+
22
+
23
+ ###
24
+ ### FilePort
25
+ ###
26
+
27
+ class FilePort < Port
28
+
29
+ def initialize( fname )
30
+ @filename = File.expand_path(fname)
31
+ super()
32
+ end
33
+
34
+ attr_reader :filename
35
+
36
+ alias ident filename
37
+
38
+ def ==( other )
39
+ other.respond_to?(:filename) and @filename == other.filename
40
+ end
41
+
42
+ alias eql? ==
43
+
44
+ def hash
45
+ @filename.hash
46
+ end
47
+
48
+ def inspect
49
+ "#<#{self.class}:#{@filename}>"
50
+ end
51
+
52
+ def reproducible?
53
+ true
54
+ end
55
+
56
+ def size
57
+ File.size @filename
58
+ end
59
+
60
+
61
+ def ropen( &block )
62
+ File.open(@filename, &block)
63
+ end
64
+
65
+ def wopen( &block )
66
+ File.open(@filename, 'w', &block)
67
+ end
68
+
69
+ def aopen( &block )
70
+ File.open(@filename, 'a', &block)
71
+ end
72
+
73
+
74
+ def read_all
75
+ ropen {|f|
76
+ return f.read
77
+ }
78
+ end
79
+
80
+
81
+ def remove
82
+ File.unlink @filename
83
+ end
84
+
85
+ def move_to( port )
86
+ begin
87
+ File.link @filename, port.filename
88
+ rescue Errno::EXDEV
89
+ copy_to port
90
+ end
91
+ File.unlink @filename
92
+ end
93
+
94
+ alias mv move_to
95
+
96
+ def copy_to( port )
97
+ if FilePort === port
98
+ copy_file @filename, port.filename
99
+ else
100
+ File.open(@filename) {|r|
101
+ port.wopen {|w|
102
+ while s = r.sysread(4096)
103
+ w.write << s
104
+ end
105
+ } }
106
+ end
107
+ end
108
+
109
+ alias cp copy_to
110
+
111
+ private
112
+
113
+ # from fileutils.rb
114
+ def copy_file( src, dest )
115
+ st = r = w = nil
116
+
117
+ File.open(src, 'rb') {|r|
118
+ File.open(dest, 'wb') {|w|
119
+ st = r.stat
120
+ begin
121
+ while true
122
+ w.write r.sysread(st.blksize)
123
+ end
124
+ rescue EOFError
125
+ end
126
+ } }
127
+ end
128
+
129
+ end
130
+
131
+
132
+ module MailFlags
133
+
134
+ def seen=( b )
135
+ set_status 'S', b
136
+ end
137
+
138
+ def seen?
139
+ get_status 'S'
140
+ end
141
+
142
+ def replied=( b )
143
+ set_status 'R', b
144
+ end
145
+
146
+ def replied?
147
+ get_status 'R'
148
+ end
149
+
150
+ def flagged=( b )
151
+ set_status 'F', b
152
+ end
153
+
154
+ def flagged?
155
+ get_status 'F'
156
+ end
157
+
158
+ private
159
+
160
+ def procinfostr( str, tag, true_p )
161
+ a = str.upcase.split(//)
162
+ a.push true_p ? tag : nil
163
+ a.delete tag unless true_p
164
+ a.compact.sort.join('').squeeze
165
+ end
166
+
167
+ end
168
+
169
+
170
+ class MhPort < FilePort
171
+
172
+ include MailFlags
173
+
174
+ private
175
+
176
+ def set_status( tag, flag )
177
+ begin
178
+ tmpfile = @filename + '.tmailtmp.' + $$.to_s
179
+ File.open(tmpfile, 'w') {|f|
180
+ write_status f, tag, flag
181
+ }
182
+ File.unlink @filename
183
+ File.link tmpfile, @filename
184
+ ensure
185
+ File.unlink tmpfile
186
+ end
187
+ end
188
+
189
+ def write_status( f, tag, flag )
190
+ stat = ''
191
+ File.open(@filename) {|r|
192
+ while line = r.gets
193
+ if line.strip.empty?
194
+ break
195
+ elsif m = /\AX-TMail-Status:/i.match(line)
196
+ stat = m.post_match.strip
197
+ else
198
+ f.print line
199
+ end
200
+ end
201
+
202
+ s = procinfostr(stat, tag, flag)
203
+ f.puts 'X-TMail-Status: ' + s unless s.empty?
204
+ f.puts
205
+
206
+ while s = r.read(2048)
207
+ f.write s
208
+ end
209
+ }
210
+ end
211
+
212
+ def get_status( tag )
213
+ File.foreach(@filename) {|line|
214
+ return false if line.strip.empty?
215
+ if m = /\AX-TMail-Status:/i.match(line)
216
+ return m.post_match.strip.include?(tag[0])
217
+ end
218
+ }
219
+ false
220
+ end
221
+
222
+ end
223
+
224
+
225
+ class MaildirPort < FilePort
226
+
227
+ def move_to_new
228
+ new = replace_dir(@filename, 'new')
229
+ File.rename @filename, new
230
+ @filename = new
231
+ end
232
+
233
+ def move_to_cur
234
+ new = replace_dir(@filename, 'cur')
235
+ File.rename @filename, new
236
+ @filename = new
237
+ end
238
+
239
+ def replace_dir( path, dir )
240
+ "#{File.dirname File.dirname(path)}/#{dir}/#{File.basename path}"
241
+ end
242
+ private :replace_dir
243
+
244
+
245
+ include MailFlags
246
+
247
+ private
248
+
249
+ MAIL_FILE = /\A(\d+\.[\d_]+\.[^:]+)(?:\:(\d),(\w+)?)?\z/
250
+
251
+ def set_status( tag, flag )
252
+ if m = MAIL_FILE.match(File.basename(@filename))
253
+ s, uniq, type, info, = m.to_a
254
+ return if type and type != '2' # do not change anything
255
+ newname = File.dirname(@filename) + '/' +
256
+ uniq + ':2,' + procinfostr(info.to_s, tag, flag)
257
+ else
258
+ newname = @filename + ':2,' + tag
259
+ end
260
+
261
+ File.link @filename, newname
262
+ File.unlink @filename
263
+ @filename = newname
264
+ end
265
+
266
+ def get_status( tag )
267
+ m = MAIL_FILE.match(File.basename(@filename)) or return false
268
+ m[2] == '2' and m[3].to_s.include?(tag[0])
269
+ end
270
+
271
+ end
272
+
273
+
274
+ ###
275
+ ### StringPort
276
+ ###
277
+
278
+ class StringPort < Port
279
+
280
+ def initialize( str = '' )
281
+ @buffer = str
282
+ super()
283
+ end
284
+
285
+ def string
286
+ @buffer
287
+ end
288
+
289
+ def to_s
290
+ @buffer.dup
291
+ end
292
+
293
+ alias read_all to_s
294
+
295
+ def size
296
+ @buffer.size
297
+ end
298
+
299
+ def ==( other )
300
+ StringPort === other and @buffer.equal? other.string
301
+ end
302
+
303
+ alias eql? ==
304
+
305
+ def hash
306
+ @buffer.id.hash
307
+ end
308
+
309
+ def inspect
310
+ "#<#{self.class}:id=#{sprintf '0x%x', @buffer.id}>"
311
+ end
312
+
313
+ def reproducible?
314
+ true
315
+ end
316
+
317
+ def ropen( &block )
318
+ @buffer or raise Errno::ENOENT, "#{inspect} is already removed"
319
+ StringInput.open(@buffer, &block)
320
+ end
321
+
322
+ def wopen( &block )
323
+ @buffer = ''
324
+ StringOutput.new(@buffer, &block)
325
+ end
326
+
327
+ def aopen( &block )
328
+ @buffer ||= ''
329
+ StringOutput.new(@buffer, &block)
330
+ end
331
+
332
+ def remove
333
+ @buffer = nil
334
+ end
335
+
336
+ alias rm remove
337
+
338
+ def copy_to( port )
339
+ port.wopen {|f|
340
+ f.write @buffer
341
+ }
342
+ end
343
+
344
+ alias cp copy_to
345
+
346
+ def move_to( port )
347
+ if StringPort === port
348
+ str = @buffer
349
+ port.instance_eval { @buffer = str }
350
+ else
351
+ copy_to port
352
+ end
353
+ remove
354
+ end
355
+
356
+ end
357
+
358
+ end # module TMail
@@ -0,0 +1,22 @@
1
+ #
2
+ # scanner.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
+ require 'tmail/utils'
12
+
13
+ module TMail
14
+ require 'tmail/scanner_r.rb'
15
+ begin
16
+ raise LoadError, 'Turn off Ruby extention by user choice' if ENV['NORUBYEXT']
17
+ require 'tmail/scanner_c.so'
18
+ Scanner = Scanner_C
19
+ rescue LoadError
20
+ Scanner = Scanner_R
21
+ end
22
+ end
@@ -0,0 +1,244 @@
1
+ #
2
+ # scanner_r.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
+ require 'tmail/config'
12
+
13
+
14
+ module TMail
15
+
16
+ class Scanner_R
17
+
18
+ Version = '0.10.7'
19
+ Version.freeze
20
+
21
+ MIME_HEADERS = {
22
+ :CTYPE => true,
23
+ :CENCODING => true,
24
+ :CDISPOSITION => true
25
+ }
26
+
27
+ alnum = 'a-zA-Z0-9'
28
+ atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
29
+ tokensyms = %q[ _#!$%&`'*+-{|}~^. ].strip
30
+
31
+ atomchars = alnum + Regexp.quote(atomsyms)
32
+ tokenchars = alnum + Regexp.quote(tokensyms)
33
+ iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
34
+
35
+ eucstr = '(?:[\xa1-\xfe][\xa1-\xfe])+'
36
+ sjisstr = '(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+'
37
+ utf8str = '(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+'
38
+
39
+ quoted_with_iso2022 = /\A(?:[^\\\e"]+|#{iso2022str})+/n
40
+ domlit_with_iso2022 = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
41
+ comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
42
+
43
+ quoted_without_iso2022 = /\A[^\\"]+/n
44
+ domlit_without_iso2022 = /\A[^\\\]]+/n
45
+ comment_without_iso2022 = /\A[^\\()]+/n
46
+
47
+ PATTERN_TABLE = {}
48
+ PATTERN_TABLE['EUC'] =
49
+ [
50
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
51
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
52
+ quoted_with_iso2022,
53
+ domlit_with_iso2022,
54
+ comment_with_iso2022
55
+ ]
56
+ PATTERN_TABLE['SJIS'] =
57
+ [
58
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
59
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
60
+ quoted_with_iso2022,
61
+ domlit_with_iso2022,
62
+ comment_with_iso2022
63
+ ]
64
+ PATTERN_TABLE['UTF8'] =
65
+ [
66
+ /\A(?:[#{atomchars}]+|#{utf8str})+/n,
67
+ /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
68
+ quoted_without_iso2022,
69
+ domlit_without_iso2022,
70
+ comment_without_iso2022
71
+ ]
72
+ PATTERN_TABLE['NONE'] =
73
+ [
74
+ /\A[#{atomchars}]+/n,
75
+ /\A[#{tokenchars}]+/n,
76
+ quoted_without_iso2022,
77
+ domlit_without_iso2022,
78
+ comment_without_iso2022
79
+ ]
80
+
81
+
82
+ def initialize( str, scantype, comments )
83
+ init_scanner str
84
+ @comments = comments || []
85
+ @debug = false
86
+
87
+ # fix scanner mode
88
+ @received = (scantype == :RECEIVED)
89
+ @is_mime_header = MIME_HEADERS[scantype]
90
+
91
+ atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[$KCODE]
92
+ @word_re = (MIME_HEADERS[scantype] ? token : atom)
93
+ end
94
+
95
+ attr_accessor :debug
96
+
97
+ def scan( &block )
98
+ if @debug
99
+ scan_main do |arr|
100
+ s, v = arr
101
+ printf "%7d %-10s %s\n",
102
+ rest_size(),
103
+ s.respond_to?(:id2name) ? s.id2name : s.inspect,
104
+ v.inspect
105
+ yield arr
106
+ end
107
+ else
108
+ scan_main(&block)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ RECV_TOKEN = {
115
+ 'from' => :FROM,
116
+ 'by' => :BY,
117
+ 'via' => :VIA,
118
+ 'with' => :WITH,
119
+ 'id' => :ID,
120
+ 'for' => :FOR
121
+ }
122
+
123
+ def scan_main
124
+ until eof?
125
+ if skip(/\A[\n\r\t ]+/n) # LWSP
126
+ break if eof?
127
+ end
128
+
129
+ if s = readstr(@word_re)
130
+ if @is_mime_header
131
+ yield :TOKEN, s
132
+ else
133
+ # atom
134
+ if /\A\d+\z/ === s
135
+ yield :DIGIT, s
136
+ elsif @received
137
+ yield RECV_TOKEN[s.downcase] || :ATOM, s
138
+ else
139
+ yield :ATOM, s
140
+ end
141
+ end
142
+
143
+ elsif skip(/\A"/)
144
+ yield :QUOTED, scan_quoted_word()
145
+
146
+ elsif skip(/\A\[/)
147
+ yield :DOMLIT, scan_domain_literal()
148
+
149
+ elsif skip(/\A\(/)
150
+ @comments.push scan_comment()
151
+
152
+ else
153
+ c = readchar()
154
+ yield c, c
155
+ end
156
+ end
157
+
158
+ yield false, '$'
159
+ end
160
+
161
+ def scan_quoted_word
162
+ scan_qstr(@quoted_re, /\A"/, 'quoted-word')
163
+ end
164
+
165
+ def scan_domain_literal
166
+ '[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
167
+ end
168
+
169
+ def scan_qstr( pattern, terminal, type )
170
+ result = ''
171
+ until eof?
172
+ if s = readstr(pattern) then result << s
173
+ elsif skip(terminal) then return result
174
+ elsif skip(/\A\\/) then result << readchar()
175
+ else
176
+ raise "TMail FATAL: not match in #{type}"
177
+ end
178
+ end
179
+ scan_error! "found unterminated #{type}"
180
+ end
181
+
182
+ def scan_comment
183
+ result = ''
184
+ nest = 1
185
+ content = @comment_re
186
+
187
+ until eof?
188
+ if s = readstr(content) then result << s
189
+ elsif skip(/\A\)/) then nest -= 1
190
+ return result if nest == 0
191
+ result << ')'
192
+ elsif skip(/\A\(/) then nest += 1
193
+ result << '('
194
+ elsif skip(/\A\\/) then result << readchar()
195
+ else
196
+ raise 'TMail FATAL: not match in comment'
197
+ end
198
+ end
199
+ scan_error! 'found unterminated comment'
200
+ end
201
+
202
+ # string scanner
203
+
204
+ def init_scanner( str )
205
+ @src = str
206
+ end
207
+
208
+ def eof?
209
+ @src.empty?
210
+ end
211
+
212
+ def rest_size
213
+ @src.size
214
+ end
215
+
216
+ def readstr( re )
217
+ if m = re.match(@src)
218
+ @src = m.post_match
219
+ m[0]
220
+ else
221
+ nil
222
+ end
223
+ end
224
+
225
+ def readchar
226
+ readstr(/\A./)
227
+ end
228
+
229
+ def skip( re )
230
+ if m = re.match(@src)
231
+ @src = m.post_match
232
+ true
233
+ else
234
+ false
235
+ end
236
+ end
237
+
238
+ def scan_error!( msg )
239
+ raise SyntaxError, msg
240
+ end
241
+
242
+ end
243
+
244
+ end # module TMail