yakischloba-tmail-pure 0.10.9

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,5 @@
1
+ This is a version of tmail that has been modified to be pure ruby only.
2
+
3
+ The full, original Tmail package can be found at
4
+
5
+ http://tmail.rubyforge.org
data/lib/tmail-pure.rb ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
2
+ require 'tmail-pure/info'
3
+ require 'tmail-pure/mail'
4
+ require 'tmail-pure/mailbox'
5
+ require 'tmail-pure/obsolete'
@@ -0,0 +1,222 @@
1
+ #
2
+ # address.rb
3
+ #
4
+ # Copyright (c) 1998-2004 Minero Aoki
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.1.
9
+ #
10
+
11
+ require 'tmail-pure/encode'
12
+ require 'tmail-pure/parser'
13
+
14
+ module TMail
15
+
16
+ class Address
17
+
18
+ include TextUtils
19
+
20
+ def Address.parse(str)
21
+ Parser.parse :ADDRESS, str
22
+ end
23
+
24
+ def address_group?
25
+ false
26
+ end
27
+
28
+ def initialize(local, domain)
29
+ if domain
30
+ domain.each do |s|
31
+ raise SyntaxError, 'empty word in domain' if s.empty?
32
+ end
33
+ end
34
+ @local = local
35
+ @domain = domain
36
+ @name = nil
37
+ @routes = []
38
+ end
39
+
40
+ attr_reader :name
41
+
42
+ def name=(str)
43
+ @name = str
44
+ @name = nil if str and str.empty?
45
+ end
46
+
47
+ alias phrase name
48
+ alias phrase= name=
49
+
50
+ attr_reader :routes
51
+
52
+ def inspect
53
+ "#<#{self.class} #{address()}>"
54
+ end
55
+
56
+ def local
57
+ return nil unless @local
58
+ return '""' if @local.size == 1 and @local[0].empty?
59
+ @local.map {|i| quote_atom(i) }.join('.')
60
+ end
61
+
62
+ def domain
63
+ return nil unless @domain
64
+ join_domain(@domain)
65
+ end
66
+
67
+ def spec
68
+ s = self.local
69
+ d = self.domain
70
+ if s and d
71
+ s + '@' + d
72
+ else
73
+ s
74
+ end
75
+ end
76
+
77
+ alias address spec
78
+
79
+
80
+ def ==(other)
81
+ other.respond_to? :spec and self.spec == other.spec
82
+ end
83
+
84
+ alias eql? ==
85
+
86
+ def hash
87
+ @local.hash ^ @domain.hash
88
+ end
89
+
90
+ def dup
91
+ obj = self.class.new(@local.dup, @domain.dup)
92
+ obj.name = @name.dup if @name
93
+ obj.routes.replace @routes
94
+ obj
95
+ end
96
+
97
+ include StrategyInterface
98
+
99
+ def accept(strategy, dummy1 = nil, dummy2 = nil)
100
+ unless @local
101
+ strategy.meta '<>' # empty return-path
102
+ return
103
+ end
104
+
105
+ spec_p = (not @name and @routes.empty?)
106
+ if @name
107
+ strategy.phrase @name
108
+ strategy.space
109
+ end
110
+ tmp = spec_p ? '' : '<'
111
+ unless @routes.empty?
112
+ tmp << @routes.map {|i| '@' + i }.join(',') << ':'
113
+ end
114
+ tmp << self.spec
115
+ tmp << '>' unless spec_p
116
+ strategy.meta tmp
117
+ strategy.lwsp ''
118
+ end
119
+
120
+ end
121
+
122
+
123
+ class AddressGroup
124
+
125
+ include Enumerable
126
+
127
+ def address_group?
128
+ true
129
+ end
130
+
131
+ def initialize(name, addrs)
132
+ @name = name
133
+ @addresses = addrs
134
+ end
135
+
136
+ attr_reader :name
137
+
138
+ def ==(other)
139
+ other.respond_to? :to_a and @addresses == other.to_a
140
+ end
141
+
142
+ alias eql? ==
143
+
144
+ def hash
145
+ map {|i| i.hash }.hash
146
+ end
147
+
148
+ def [](idx)
149
+ @addresses[idx]
150
+ end
151
+
152
+ def size
153
+ @addresses.size
154
+ end
155
+
156
+ def empty?
157
+ @addresses.empty?
158
+ end
159
+
160
+ def each(&block)
161
+ @addresses.each(&block)
162
+ end
163
+
164
+ def to_a
165
+ @addresses.dup
166
+ end
167
+
168
+ alias to_ary to_a
169
+
170
+ def include?(a)
171
+ @addresses.include? a
172
+ end
173
+
174
+ def flatten
175
+ set = []
176
+ @addresses.each do |a|
177
+ if a.respond_to? :flatten
178
+ set.concat a.flatten
179
+ else
180
+ set.push a
181
+ end
182
+ end
183
+ set
184
+ end
185
+
186
+ def each_address(&block)
187
+ flatten.each(&block)
188
+ end
189
+
190
+ def add(a)
191
+ @addresses.push a
192
+ end
193
+
194
+ alias push add
195
+
196
+ def delete(a)
197
+ @addresses.delete a
198
+ end
199
+
200
+ include StrategyInterface
201
+
202
+ def accept(strategy, dummy1 = nil, dummy2 = nil)
203
+ strategy.phrase @name
204
+ strategy.meta ':'
205
+ strategy.space
206
+ first = true
207
+ each do |mbox|
208
+ if first
209
+ first = false
210
+ else
211
+ strategy.meta ','
212
+ end
213
+ strategy.space
214
+ mbox.accept strategy
215
+ end
216
+ strategy.meta ';'
217
+ strategy.lwsp ''
218
+ end
219
+
220
+ end
221
+
222
+ end # module TMail
@@ -0,0 +1,52 @@
1
+ #
2
+ # base64.rb
3
+ #
4
+ # Copyright (c) 1998-2004 Minero Aoki
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.1.
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-pure/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,39 @@
1
+ unless Enumerable.method_defined?(:map)
2
+ module Enumerable
3
+ alias map collect
4
+ end
5
+ end
6
+
7
+ unless Enumerable.method_defined?(:select)
8
+ module Enumerable
9
+ alias select find_all
10
+ end
11
+ end
12
+
13
+ unless Enumerable.method_defined?(:reject)
14
+ module Enumerable
15
+ def reject
16
+ result = []
17
+ each do |i|
18
+ result.push i unless yield(i)
19
+ end
20
+ result
21
+ end
22
+ end
23
+ end
24
+
25
+ unless Enumerable.method_defined?(:sort_by)
26
+ module Enumerable
27
+ def sort_by
28
+ map {|i| [yield(i), i] }.sort.map {|val, i| i }
29
+ end
30
+ end
31
+ end
32
+
33
+ unless File.respond_to?(:read)
34
+ def File.read(fname)
35
+ File.open(fname) {|f|
36
+ return f.read
37
+ }
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ #
2
+ # config.rb
3
+ #
4
+ # Copyright (c) 1998-2004 Minero Aoki
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.1.
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-2004 Minero Aoki
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.1.
9
+ #
10
+
11
+ require 'nkf'
12
+ require 'tmail-pure/base64.rb'
13
+ require 'tmail-pure/stringio'
14
+ require 'tmail-pure/textutils'
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(RFC2231_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