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,223 @@
|
|
1
|
+
#
|
2
|
+
# address.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/encode'
|
12
|
+
require 'tmail/parser'
|
13
|
+
|
14
|
+
|
15
|
+
module TMail
|
16
|
+
|
17
|
+
class Address
|
18
|
+
|
19
|
+
include TextUtils
|
20
|
+
|
21
|
+
def Address.parse( str )
|
22
|
+
Parser.parse :ADDRESS, str
|
23
|
+
end
|
24
|
+
|
25
|
+
def address_group?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize( local, domain )
|
30
|
+
if domain
|
31
|
+
domain.each do |s|
|
32
|
+
raise SyntaxError, 'empty word in domain' if s.empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@local = local
|
36
|
+
@domain = domain
|
37
|
+
@name = nil
|
38
|
+
@routes = []
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :name
|
42
|
+
|
43
|
+
def name=( str )
|
44
|
+
@name = str
|
45
|
+
@name = nil if str and str.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
alias phrase name
|
49
|
+
alias phrase= name=
|
50
|
+
|
51
|
+
attr_reader :routes
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
"#<#{self.class} #{address()}>"
|
55
|
+
end
|
56
|
+
|
57
|
+
def local
|
58
|
+
return nil unless @local
|
59
|
+
return '""' if @local.size == 1 and @local[0].empty?
|
60
|
+
@local.map {|i| quote_atom(i) }.join('.')
|
61
|
+
end
|
62
|
+
|
63
|
+
def domain
|
64
|
+
return nil unless @domain
|
65
|
+
join_domain(@domain)
|
66
|
+
end
|
67
|
+
|
68
|
+
def spec
|
69
|
+
s = self.local
|
70
|
+
d = self.domain
|
71
|
+
if s and d
|
72
|
+
s + '@' + d
|
73
|
+
else
|
74
|
+
s
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
alias address spec
|
79
|
+
|
80
|
+
|
81
|
+
def ==( other )
|
82
|
+
other.respond_to? :spec and self.spec == other.spec
|
83
|
+
end
|
84
|
+
|
85
|
+
alias eql? ==
|
86
|
+
|
87
|
+
def hash
|
88
|
+
@local.hash ^ @domain.hash
|
89
|
+
end
|
90
|
+
|
91
|
+
def dup
|
92
|
+
obj = self.class.new(@local.dup, @domain.dup)
|
93
|
+
obj.name = @name.dup if @name
|
94
|
+
obj.routes.replace @routes
|
95
|
+
obj
|
96
|
+
end
|
97
|
+
|
98
|
+
include StrategyInterface
|
99
|
+
|
100
|
+
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
101
|
+
unless @local
|
102
|
+
strategy.meta '<>' # empty return-path
|
103
|
+
return
|
104
|
+
end
|
105
|
+
|
106
|
+
spec_p = (not @name and @routes.empty?)
|
107
|
+
if @name
|
108
|
+
strategy.phrase @name
|
109
|
+
strategy.space
|
110
|
+
end
|
111
|
+
tmp = spec_p ? '' : '<'
|
112
|
+
unless @routes.empty?
|
113
|
+
tmp << @routes.map {|i| '@' + i }.join(',') << ':'
|
114
|
+
end
|
115
|
+
tmp << self.spec
|
116
|
+
tmp << '>' unless spec_p
|
117
|
+
strategy.meta tmp
|
118
|
+
strategy.lwsp ''
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
class AddressGroup
|
125
|
+
|
126
|
+
include Enumerable
|
127
|
+
|
128
|
+
def address_group?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize( name, addrs )
|
133
|
+
@name = name
|
134
|
+
@addresses = addrs
|
135
|
+
end
|
136
|
+
|
137
|
+
attr_reader :name
|
138
|
+
|
139
|
+
def ==( other )
|
140
|
+
other.respond_to? :to_a and @addresses == other.to_a
|
141
|
+
end
|
142
|
+
|
143
|
+
alias eql? ==
|
144
|
+
|
145
|
+
def hash
|
146
|
+
map {|i| i.hash }.hash
|
147
|
+
end
|
148
|
+
|
149
|
+
def []( idx )
|
150
|
+
@addresses[idx]
|
151
|
+
end
|
152
|
+
|
153
|
+
def size
|
154
|
+
@addresses.size
|
155
|
+
end
|
156
|
+
|
157
|
+
def empty?
|
158
|
+
@addresses.empty?
|
159
|
+
end
|
160
|
+
|
161
|
+
def each( &block )
|
162
|
+
@addresses.each(&block)
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_a
|
166
|
+
@addresses.dup
|
167
|
+
end
|
168
|
+
|
169
|
+
alias to_ary to_a
|
170
|
+
|
171
|
+
def include?( a )
|
172
|
+
@addresses.include? a
|
173
|
+
end
|
174
|
+
|
175
|
+
def flatten
|
176
|
+
set = []
|
177
|
+
@addresses.each do |a|
|
178
|
+
if a.respond_to? :flatten
|
179
|
+
set.concat a.flatten
|
180
|
+
else
|
181
|
+
set.push a
|
182
|
+
end
|
183
|
+
end
|
184
|
+
set
|
185
|
+
end
|
186
|
+
|
187
|
+
def each_address( &block )
|
188
|
+
flatten.each(&block)
|
189
|
+
end
|
190
|
+
|
191
|
+
def add( a )
|
192
|
+
@addresses.push a
|
193
|
+
end
|
194
|
+
|
195
|
+
alias push add
|
196
|
+
|
197
|
+
def delete( a )
|
198
|
+
@addresses.delete a
|
199
|
+
end
|
200
|
+
|
201
|
+
include StrategyInterface
|
202
|
+
|
203
|
+
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
204
|
+
strategy.phrase @name
|
205
|
+
strategy.meta ':'
|
206
|
+
strategy.space
|
207
|
+
first = true
|
208
|
+
each do |mbox|
|
209
|
+
if first
|
210
|
+
first = false
|
211
|
+
else
|
212
|
+
strategy.meta ','
|
213
|
+
end
|
214
|
+
strategy.space
|
215
|
+
mbox.accept strategy
|
216
|
+
end
|
217
|
+
strategy.meta ';'
|
218
|
+
strategy.lwsp ''
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end # module TMail
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# base64.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
|
+
module Base64
|
14
|
+
|
15
|
+
module_function
|
16
|
+
|
17
|
+
def rb_folding_encode( str, eol = "\n", limit = 60 )
|
18
|
+
[str].pack('m')
|
19
|
+
end
|
20
|
+
|
21
|
+
def rb_encode( str )
|
22
|
+
[str].pack('m').tr( "\r\n", '' )
|
23
|
+
end
|
24
|
+
|
25
|
+
def rb_decode( str, strict = false )
|
26
|
+
str.unpack('m')
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'tmail/base64.so'
|
31
|
+
alias folding_encode c_folding_encode
|
32
|
+
alias encode c_encode
|
33
|
+
alias decode c_decode
|
34
|
+
class << self
|
35
|
+
alias folding_encode c_folding_encode
|
36
|
+
alias encode c_encode
|
37
|
+
alias decode c_decode
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
alias folding_encode rb_folding_encode
|
41
|
+
alias encode rb_encode
|
42
|
+
alias decode rb_decode
|
43
|
+
class << self
|
44
|
+
alias folding_encode rb_folding_encode
|
45
|
+
alias encode rb_encode
|
46
|
+
alias decode rb_decode
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#
|
2
|
+
# config.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 Config
|
14
|
+
|
15
|
+
def initialize( strict )
|
16
|
+
@strict_parse = strict
|
17
|
+
@strict_base64decode = strict
|
18
|
+
end
|
19
|
+
|
20
|
+
def strict_parse?
|
21
|
+
@strict_parse
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_writer :strict_parse
|
25
|
+
|
26
|
+
def strict_base64decode?
|
27
|
+
@strict_base64decode
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_writer :strict_base64decode
|
31
|
+
|
32
|
+
def new_body_port( mail )
|
33
|
+
StringPort.new
|
34
|
+
end
|
35
|
+
|
36
|
+
alias new_preamble_port new_body_port
|
37
|
+
alias new_part_port new_body_port
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
DEFAULT_CONFIG = Config.new(false)
|
42
|
+
DEFAULT_STRICT_CONFIG = Config.new(true)
|
43
|
+
|
44
|
+
def Config.to_config( arg )
|
45
|
+
return DEFAULT_STRICT_CONFIG if arg == true
|
46
|
+
return DEFAULT_CONFIG if arg == false
|
47
|
+
arg or DEFAULT_CONFIG
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
#
|
2
|
+
# encode.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 'nkf'
|
12
|
+
require 'tmail/base64.rb'
|
13
|
+
require 'tmail/stringio'
|
14
|
+
require 'tmail/utils'
|
15
|
+
|
16
|
+
|
17
|
+
module TMail
|
18
|
+
|
19
|
+
module StrategyInterface
|
20
|
+
|
21
|
+
def create_dest( obj )
|
22
|
+
case obj
|
23
|
+
when nil
|
24
|
+
StringOutput.new
|
25
|
+
when String
|
26
|
+
StringOutput.new(obj)
|
27
|
+
when IO, StringOutput
|
28
|
+
obj
|
29
|
+
else
|
30
|
+
raise TypeError, 'cannot handle this type of object for dest'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
module_function :create_dest
|
34
|
+
|
35
|
+
def encoded( eol = "\r\n", charset = 'j', dest = nil )
|
36
|
+
accept_strategy Encoder, eol, charset, dest
|
37
|
+
end
|
38
|
+
|
39
|
+
def decoded( eol = "\n", charset = 'e', dest = nil )
|
40
|
+
accept_strategy Decoder, eol, charset, dest
|
41
|
+
end
|
42
|
+
|
43
|
+
alias to_s decoded
|
44
|
+
|
45
|
+
def accept_strategy( klass, eol, charset, dest = nil )
|
46
|
+
dest ||= ''
|
47
|
+
accept klass.new(create_dest(dest), charset, eol)
|
48
|
+
dest
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
###
|
55
|
+
### MIME B encoding decoder
|
56
|
+
###
|
57
|
+
|
58
|
+
class Decoder
|
59
|
+
|
60
|
+
include TextUtils
|
61
|
+
|
62
|
+
encoded = '=\?(?:iso-2022-jp|euc-jp|shift_jis)\?[QB]\?[a-z0-9+/=]+\?='
|
63
|
+
ENCODED_WORDS = /#{encoded}(?:\s+#{encoded})*/i
|
64
|
+
|
65
|
+
OUTPUT_ENCODING = {
|
66
|
+
'EUC' => 'e',
|
67
|
+
'SJIS' => 's',
|
68
|
+
}
|
69
|
+
|
70
|
+
def self.decode( str, encoding = nil )
|
71
|
+
encoding ||= (OUTPUT_ENCODING[$KCODE] || 'j')
|
72
|
+
opt = '-m' + encoding
|
73
|
+
str.gsub(ENCODED_WORDS) {|s| NKF.nkf(opt, s) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize( dest, encoding = nil, eol = "\n" )
|
77
|
+
@f = StrategyInterface.create_dest(dest)
|
78
|
+
@encoding = (/\A[ejs]/ === encoding) ? encoding[0,1] : nil
|
79
|
+
@eol = eol
|
80
|
+
end
|
81
|
+
|
82
|
+
def decode( str )
|
83
|
+
self.class.decode(str, @encoding)
|
84
|
+
end
|
85
|
+
private :decode
|
86
|
+
|
87
|
+
def terminate
|
88
|
+
end
|
89
|
+
|
90
|
+
def header_line( str )
|
91
|
+
@f << decode(str)
|
92
|
+
end
|
93
|
+
|
94
|
+
def header_name( nm )
|
95
|
+
@f << nm << ': '
|
96
|
+
end
|
97
|
+
|
98
|
+
def header_body( str )
|
99
|
+
@f << decode(str)
|
100
|
+
end
|
101
|
+
|
102
|
+
def space
|
103
|
+
@f << ' '
|
104
|
+
end
|
105
|
+
|
106
|
+
alias spc space
|
107
|
+
|
108
|
+
def lwsp( str )
|
109
|
+
@f << str
|
110
|
+
end
|
111
|
+
|
112
|
+
def meta( str )
|
113
|
+
@f << str
|
114
|
+
end
|
115
|
+
|
116
|
+
def text( str )
|
117
|
+
@f << decode(str)
|
118
|
+
end
|
119
|
+
|
120
|
+
def phrase( str )
|
121
|
+
@f << quote_phrase(decode(str))
|
122
|
+
end
|
123
|
+
|
124
|
+
def kv_pair( k, v )
|
125
|
+
@f << k << '=' << v
|
126
|
+
end
|
127
|
+
|
128
|
+
def puts( str = nil )
|
129
|
+
@f << str if str
|
130
|
+
@f << @eol
|
131
|
+
end
|
132
|
+
|
133
|
+
def write( str )
|
134
|
+
@f << str
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
###
|
141
|
+
### MIME B-encoding encoder
|
142
|
+
###
|
143
|
+
|
144
|
+
#
|
145
|
+
# FIXME: This class can handle only (euc-jp/shift_jis -> iso-2022-jp).
|
146
|
+
#
|
147
|
+
class Encoder
|
148
|
+
|
149
|
+
include TextUtils
|
150
|
+
|
151
|
+
BENCODE_DEBUG = false unless defined?(BENCODE_DEBUG)
|
152
|
+
|
153
|
+
def Encoder.encode( str )
|
154
|
+
e = new()
|
155
|
+
e.header_body str
|
156
|
+
e.terminate
|
157
|
+
e.dest.string
|
158
|
+
end
|
159
|
+
|
160
|
+
SPACER = "\t"
|
161
|
+
MAX_LINE_LEN = 70
|
162
|
+
|
163
|
+
OPTIONS = {
|
164
|
+
'EUC' => '-Ej -m0',
|
165
|
+
'SJIS' => '-Sj -m0',
|
166
|
+
'UTF8' => nil, # FIXME
|
167
|
+
'NONE' => nil
|
168
|
+
}
|
169
|
+
|
170
|
+
def initialize( dest = nil, encoding = nil, eol = "\r\n", limit = nil )
|
171
|
+
@f = StrategyInterface.create_dest(dest)
|
172
|
+
@opt = OPTIONS[$KCODE]
|
173
|
+
@eol = eol
|
174
|
+
reset
|
175
|
+
end
|
176
|
+
|
177
|
+
def normalize_encoding( str )
|
178
|
+
if @opt
|
179
|
+
then NKF.nkf(@opt, str)
|
180
|
+
else str
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def reset
|
185
|
+
@text = ''
|
186
|
+
@lwsp = ''
|
187
|
+
@curlen = 0
|
188
|
+
end
|
189
|
+
|
190
|
+
def terminate
|
191
|
+
add_lwsp ''
|
192
|
+
reset
|
193
|
+
end
|
194
|
+
|
195
|
+
def dest
|
196
|
+
@f
|
197
|
+
end
|
198
|
+
|
199
|
+
def puts( str = nil )
|
200
|
+
@f << str if str
|
201
|
+
@f << @eol
|
202
|
+
end
|
203
|
+
|
204
|
+
def write( str )
|
205
|
+
@f << str
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# add
|
210
|
+
#
|
211
|
+
|
212
|
+
def header_line( line )
|
213
|
+
scanadd line
|
214
|
+
end
|
215
|
+
|
216
|
+
def header_name( name )
|
217
|
+
add_text name.split(/-/).map {|i| i.capitalize }.join('-')
|
218
|
+
add_text ':'
|
219
|
+
add_lwsp ' '
|
220
|
+
end
|
221
|
+
|
222
|
+
def header_body( str )
|
223
|
+
scanadd normalize_encoding(str)
|
224
|
+
end
|
225
|
+
|
226
|
+
def space
|
227
|
+
add_lwsp ' '
|
228
|
+
end
|
229
|
+
|
230
|
+
alias spc space
|
231
|
+
|
232
|
+
def lwsp( str )
|
233
|
+
add_lwsp str.sub(/[\r\n]+[^\r\n]*\z/, '')
|
234
|
+
end
|
235
|
+
|
236
|
+
def meta( str )
|
237
|
+
add_text str
|
238
|
+
end
|
239
|
+
|
240
|
+
def text( str )
|
241
|
+
scanadd normalize_encoding(str)
|
242
|
+
end
|
243
|
+
|
244
|
+
def phrase( str )
|
245
|
+
str = normalize_encoding(str)
|
246
|
+
if CONTROL_CHAR === str
|
247
|
+
scanadd str
|
248
|
+
else
|
249
|
+
add_text quote_phrase(str)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# FIXME: implement line folding
|
254
|
+
#
|
255
|
+
def kv_pair( k, v )
|
256
|
+
v = normalize_encoding(v)
|
257
|
+
if token_safe?(v)
|
258
|
+
add_text k + '=' + v
|
259
|
+
elsif not CONTROL_CHAR === v
|
260
|
+
add_text k + '=' + quote_token(v)
|
261
|
+
else
|
262
|
+
# apply RFC2231 encoding
|
263
|
+
kv = k + '*=' + "iso-2022-jp'ja'" + encode_value(v)
|
264
|
+
add_text kv
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def encode_value( str )
|
269
|
+
str.gsub(TOKEN_UNSAFE) {|s| '%%%02x' % s[0] }
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
def scanadd( str, force = false )
|
275
|
+
types = ''
|
276
|
+
strs = []
|
277
|
+
|
278
|
+
until str.empty?
|
279
|
+
if m = /\A[^\e\t\r\n ]+/.match(str)
|
280
|
+
types << (force ? 'j' : 'a')
|
281
|
+
strs.push m[0]
|
282
|
+
|
283
|
+
elsif m = /\A[\t\r\n ]+/.match(str)
|
284
|
+
types << 's'
|
285
|
+
strs.push m[0]
|
286
|
+
|
287
|
+
elsif m = /\A\e../.match(str)
|
288
|
+
esc = m[0]
|
289
|
+
str = m.post_match
|
290
|
+
if esc != "\e(B" and m = /\A[^\e]+/.match(str)
|
291
|
+
types << 'j'
|
292
|
+
strs.push m[0]
|
293
|
+
end
|
294
|
+
|
295
|
+
else
|
296
|
+
raise 'TMail FATAL: encoder scan fail'
|
297
|
+
end
|
298
|
+
str = m.post_match
|
299
|
+
end
|
300
|
+
|
301
|
+
do_encode types, strs
|
302
|
+
end
|
303
|
+
|
304
|
+
def do_encode( types, strs )
|
305
|
+
#
|
306
|
+
# result : (A|E)(S(A|E))*
|
307
|
+
# E : W(SW)*
|
308
|
+
# W : (J|A)+ but must contain J # (J|A)*J(J|A)*
|
309
|
+
# A : <<A character string not to be encoded>>
|
310
|
+
# J : <<A character string to be encoded>>
|
311
|
+
# S : <<LWSP>>
|
312
|
+
#
|
313
|
+
# An encoding unit is `E'.
|
314
|
+
# Input (parameter `types') is (J|A)(J|A|S)*(J|A)
|
315
|
+
#
|
316
|
+
if BENCODE_DEBUG
|
317
|
+
puts
|
318
|
+
puts '-- do_encode ------------'
|
319
|
+
puts types.split(//).join(' ')
|
320
|
+
p strs
|
321
|
+
end
|
322
|
+
|
323
|
+
e = /[ja]*j[ja]*(?:s[ja]*j[ja]*)*/
|
324
|
+
|
325
|
+
while m = e.match(types)
|
326
|
+
pre = m.pre_match
|
327
|
+
concat_A_S pre, strs[0, pre.size] unless pre.empty?
|
328
|
+
concat_E m[0], strs[m.begin(0) ... m.end(0)]
|
329
|
+
types = m.post_match
|
330
|
+
strs.slice! 0, m.end(0)
|
331
|
+
end
|
332
|
+
concat_A_S types, strs
|
333
|
+
end
|
334
|
+
|
335
|
+
def concat_A_S( types, strs )
|
336
|
+
i = 0
|
337
|
+
types.each_byte do |t|
|
338
|
+
case t
|
339
|
+
when ?a then add_text strs[i]
|
340
|
+
when ?s then add_lwsp strs[i]
|
341
|
+
else
|
342
|
+
raise "TMail FATAL: unknown flag: #{t.chr}"
|
343
|
+
end
|
344
|
+
i += 1
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
METHOD_ID = {
|
349
|
+
?j => :extract_J,
|
350
|
+
?e => :extract_E,
|
351
|
+
?a => :extract_A,
|
352
|
+
?s => :extract_S
|
353
|
+
}
|
354
|
+
|
355
|
+
def concat_E( types, strs )
|
356
|
+
if BENCODE_DEBUG
|
357
|
+
puts '---- concat_E'
|
358
|
+
puts "types=#{types.split(//).join(' ')}"
|
359
|
+
puts "strs =#{strs.inspect}"
|
360
|
+
end
|
361
|
+
|
362
|
+
flush() unless @text.empty?
|
363
|
+
|
364
|
+
chunk = ''
|
365
|
+
strs.each_with_index do |s,i|
|
366
|
+
mid = METHOD_ID[types[i]]
|
367
|
+
until s.empty?
|
368
|
+
unless c = __send__(mid, chunk.size, s)
|
369
|
+
add_with_encode chunk unless chunk.empty?
|
370
|
+
flush
|
371
|
+
chunk = ''
|
372
|
+
fold
|
373
|
+
c = __send__(mid, 0, s)
|
374
|
+
raise 'TMail FATAL: extract fail' unless c
|
375
|
+
end
|
376
|
+
chunk << c
|
377
|
+
end
|
378
|
+
end
|
379
|
+
add_with_encode chunk unless chunk.empty?
|
380
|
+
end
|
381
|
+
|
382
|
+
def extract_J( chunksize, str )
|
383
|
+
size = max_bytes(chunksize, str.size) - 6
|
384
|
+
size = (size % 2 == 0) ? (size) : (size - 1)
|
385
|
+
return nil if size <= 0
|
386
|
+
"\e$B#{str.slice!(0, size)}\e(B"
|
387
|
+
end
|
388
|
+
|
389
|
+
def extract_A( chunksize, str )
|
390
|
+
size = max_bytes(chunksize, str.size)
|
391
|
+
return nil if size <= 0
|
392
|
+
str.slice!(0, size)
|
393
|
+
end
|
394
|
+
|
395
|
+
alias extract_S extract_A
|
396
|
+
|
397
|
+
def max_bytes( chunksize, ssize )
|
398
|
+
(restsize() - '=?iso-2022-jp?B??='.size) / 4 * 3 - chunksize
|
399
|
+
end
|
400
|
+
|
401
|
+
#
|
402
|
+
# free length buffer
|
403
|
+
#
|
404
|
+
|
405
|
+
def add_text( str )
|
406
|
+
@text << str
|
407
|
+
# puts '---- text -------------------------------------'
|
408
|
+
# puts "+ #{str.inspect}"
|
409
|
+
# puts "txt >>>#{@text.inspect}<<<"
|
410
|
+
end
|
411
|
+
|
412
|
+
def add_with_encode( str )
|
413
|
+
@text << "=?iso-2022-jp?B?#{Base64.encode(str)}?="
|
414
|
+
end
|
415
|
+
|
416
|
+
def add_lwsp( lwsp )
|
417
|
+
# puts '---- lwsp -------------------------------------'
|
418
|
+
# puts "+ #{lwsp.inspect}"
|
419
|
+
fold if restsize() <= 0
|
420
|
+
flush
|
421
|
+
@lwsp = lwsp
|
422
|
+
end
|
423
|
+
|
424
|
+
def flush
|
425
|
+
# puts '---- flush ----'
|
426
|
+
# puts "spc >>>#{@lwsp.inspect}<<<"
|
427
|
+
# puts "txt >>>#{@text.inspect}<<<"
|
428
|
+
@f << @lwsp << @text
|
429
|
+
@curlen += (@lwsp.size + @text.size)
|
430
|
+
@text = ''
|
431
|
+
@lwsp = ''
|
432
|
+
end
|
433
|
+
|
434
|
+
def fold
|
435
|
+
# puts '---- fold ----'
|
436
|
+
@f << @eol
|
437
|
+
@curlen = 0
|
438
|
+
@lwsp = SPACER
|
439
|
+
end
|
440
|
+
|
441
|
+
def restsize
|
442
|
+
MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
|
443
|
+
end
|
444
|
+
|
445
|
+
end
|
446
|
+
|
447
|
+
end # module TMail
|