rumbster 1.0.0

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.
@@ -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/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/base64.rb'
13
+ require 'tmail/stringio'
14
+ require 'tmail/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