rumbster 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.autotest +1 -0
  2. data/Gemfile +11 -0
  3. data/Gemfile.lock +34 -0
  4. data/LICENSE.txt +202 -0
  5. data/README.rdoc +50 -0
  6. data/Rakefile +50 -8
  7. data/VERSION +1 -0
  8. data/lib/message_observers.rb +3 -3
  9. data/lib/rumbster.rb +0 -0
  10. data/lib/smtp_protocol.rb +7 -7
  11. data/lib/smtp_states.rb +24 -58
  12. data/rumbster.gemspec +70 -0
  13. data/test/{message_observers_test.rb → test_message_observers.rb} +1 -1
  14. data/test/{rumbster_test.rb → test_rumbster.rb} +1 -1
  15. data/test/{smtp_protocol_test.rb → test_smtp_protocol.rb} +2 -19
  16. data/test/{smtp_states_test.rb → test_smtp_states.rb} +31 -87
  17. metadata +128 -76
  18. data/COPYING +0 -515
  19. data/README +0 -56
  20. data/vendor/tmail.rb +0 -4
  21. data/vendor/tmail/.cvsignore +0 -3
  22. data/vendor/tmail/Makefile +0 -19
  23. data/vendor/tmail/address.rb +0 -222
  24. data/vendor/tmail/base64.rb +0 -52
  25. data/vendor/tmail/compat.rb +0 -39
  26. data/vendor/tmail/config.rb +0 -50
  27. data/vendor/tmail/encode.rb +0 -447
  28. data/vendor/tmail/header.rb +0 -895
  29. data/vendor/tmail/info.rb +0 -14
  30. data/vendor/tmail/loader.rb +0 -1
  31. data/vendor/tmail/mail.rb +0 -869
  32. data/vendor/tmail/mailbox.rb +0 -386
  33. data/vendor/tmail/mbox.rb +0 -1
  34. data/vendor/tmail/net.rb +0 -260
  35. data/vendor/tmail/obsolete.rb +0 -122
  36. data/vendor/tmail/parser.rb +0 -1475
  37. data/vendor/tmail/parser.y +0 -372
  38. data/vendor/tmail/port.rb +0 -356
  39. data/vendor/tmail/scanner.rb +0 -22
  40. data/vendor/tmail/scanner_r.rb +0 -243
  41. data/vendor/tmail/stringio.rb +0 -256
  42. data/vendor/tmail/textutils.rb +0 -197
  43. data/vendor/tmail/tmail.rb +0 -1
  44. data/vendor/tmail/utils.rb +0 -23
@@ -1,447 +0,0 @@
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
@@ -1,895 +0,0 @@
1
- #
2
- # header.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/encode'
12
- require 'tmail/address'
13
- require 'tmail/parser'
14
- require 'tmail/config'
15
- require 'tmail/textutils'
16
-
17
-
18
- module TMail
19
-
20
- class HeaderField
21
-
22
- include TextUtils
23
-
24
- class << self
25
-
26
- alias newobj new
27
-
28
- def new(name, body, conf = DEFAULT_CONFIG)
29
- klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
30
- klass.newobj body, conf
31
- end
32
-
33
- def new_from_port(port, name, conf = DEFAULT_CONFIG)
34
- re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
35
- str = nil
36
- port.ropen {|f|
37
- f.each do |line|
38
- if m = re.match(line) then str = m.post_match.strip
39
- elsif str and /\A[\t ]/ =~ line then str << ' ' << line.strip
40
- elsif /\A-*\s*\z/ =~ line then break
41
- elsif str then break
42
- end
43
- end
44
- }
45
- new(name, str, Config.to_config(conf))
46
- end
47
-
48
- def internal_new(name, conf)
49
- FNAME_TO_CLASS[name].newobj('', conf, true)
50
- end
51
-
52
- end # class << self
53
-
54
- def initialize(body, conf, intern = false)
55
- @body = body
56
- @config = conf
57
-
58
- @illegal = false
59
- @parsed = false
60
- if intern
61
- @parsed = true
62
- parse_init
63
- end
64
- end
65
-
66
- def inspect
67
- "#<#{self.class} #{@body.inspect}>"
68
- end
69
-
70
- def illegal?
71
- @illegal
72
- end
73
-
74
- def empty?
75
- ensure_parsed
76
- return true if @illegal
77
- isempty?
78
- end
79
-
80
- private
81
-
82
- def ensure_parsed
83
- return if @parsed
84
- @parsed = true
85
- parse
86
- end
87
-
88
- # defabstract parse
89
- # end
90
-
91
- def clear_parse_status
92
- @parsed = false
93
- @illegal = false
94
- end
95
-
96
- public
97
-
98
- def body
99
- ensure_parsed
100
- v = Decoder.new(s = '')
101
- do_accept v
102
- v.terminate
103
- s
104
- end
105
-
106
- def body=(str)
107
- @body = str
108
- clear_parse_status
109
- end
110
-
111
- include StrategyInterface
112
-
113
- def accept(strategy, dummy1 = nil, dummy2 = nil)
114
- ensure_parsed
115
- do_accept strategy
116
- strategy.terminate
117
- end
118
-
119
- # abstract do_accept
120
-
121
- end
122
-
123
-
124
- class UnstructuredHeader < HeaderField
125
-
126
- def body
127
- ensure_parsed
128
- @body
129
- end
130
-
131
- def body=(arg)
132
- ensure_parsed
133
- @body = arg
134
- end
135
-
136
- private
137
-
138
- def parse_init
139
- end
140
-
141
- def parse
142
- @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
143
- end
144
-
145
- def isempty?
146
- not @body
147
- end
148
-
149
- def do_accept(strategy)
150
- strategy.text @body
151
- end
152
-
153
- end
154
-
155
-
156
- class StructuredHeader < HeaderField
157
-
158
- def comments
159
- ensure_parsed
160
- @comments
161
- end
162
-
163
- private
164
-
165
- def parse
166
- save = nil
167
-
168
- begin
169
- parse_init
170
- do_parse
171
- rescue SyntaxError
172
- if not save and mime_encoded? @body
173
- save = @body
174
- @body = Decoder.decode(save)
175
- retry
176
- elsif save
177
- @body = save
178
- end
179
-
180
- @illegal = true
181
- raise if @config.strict_parse?
182
- end
183
- end
184
-
185
- def parse_init
186
- @comments = []
187
- init
188
- end
189
-
190
- def do_parse
191
- obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
192
- set obj if obj
193
- end
194
-
195
- end
196
-
197
-
198
- class DateTimeHeader < StructuredHeader
199
-
200
- PARSE_TYPE = :DATETIME
201
-
202
- def date
203
- ensure_parsed
204
- @date
205
- end
206
-
207
- def date=(arg)
208
- ensure_parsed
209
- @date = arg
210
- end
211
-
212
- private
213
-
214
- def init
215
- @date = nil
216
- end
217
-
218
- def set(t)
219
- @date = t
220
- end
221
-
222
- def isempty?
223
- not @date
224
- end
225
-
226
- def do_accept(strategy)
227
- strategy.meta time2str(@date)
228
- end
229
-
230
- end
231
-
232
-
233
- class AddressHeader < StructuredHeader
234
-
235
- PARSE_TYPE = :MADDRESS
236
-
237
- def addrs
238
- ensure_parsed
239
- @addrs
240
- end
241
-
242
- private
243
-
244
- def init
245
- @addrs = []
246
- end
247
-
248
- def set(a)
249
- @addrs = a
250
- end
251
-
252
- def isempty?
253
- @addrs.empty?
254
- end
255
-
256
- def do_accept(strategy)
257
- first = true
258
- @addrs.each do |a|
259
- if first
260
- first = false
261
- else
262
- strategy.meta ','
263
- strategy.space
264
- end
265
- a.accept strategy
266
- end
267
-
268
- @comments.each do |c|
269
- strategy.space
270
- strategy.meta '('
271
- strategy.text c
272
- strategy.meta ')'
273
- end
274
- end
275
-
276
- end
277
-
278
-
279
- class ReturnPathHeader < AddressHeader
280
-
281
- PARSE_TYPE = :RETPATH
282
-
283
- def addr
284
- addrs()[0]
285
- end
286
-
287
- def spec
288
- a = addr() or return nil
289
- a.spec
290
- end
291
-
292
- def routes
293
- a = addr() or return nil
294
- a.routes
295
- end
296
-
297
- private
298
-
299
- def do_accept(strategy)
300
- a = addr()
301
-
302
- strategy.meta '<'
303
- unless a.routes.empty?
304
- strategy.meta a.routes.map {|i| '@' + i }.join(',')
305
- strategy.meta ':'
306
- end
307
- spec = a.spec
308
- strategy.meta spec if spec
309
- strategy.meta '>'
310
- end
311
-
312
- end
313
-
314
-
315
- class SingleAddressHeader < AddressHeader
316
-
317
- def addr
318
- addrs()[0]
319
- end
320
-
321
- private
322
-
323
- def do_accept(strategy)
324
- a = addr()
325
- a.accept strategy
326
- @comments.each do |c|
327
- strategy.space
328
- strategy.meta '('
329
- strategy.text c
330
- strategy.meta ')'
331
- end
332
- end
333
-
334
- end
335
-
336
-
337
- class MessageIdHeader < StructuredHeader
338
-
339
- def id
340
- ensure_parsed
341
- @id
342
- end
343
-
344
- def id=(arg)
345
- ensure_parsed
346
- @id = arg
347
- end
348
-
349
- private
350
-
351
- def init
352
- @id = nil
353
- end
354
-
355
- def isempty?
356
- not @id
357
- end
358
-
359
- def do_parse
360
- @id = @body.slice(MESSAGE_ID) or
361
- raise SyntaxError, "wrong Message-ID format: #{@body}"
362
- end
363
-
364
- def do_accept(strategy)
365
- strategy.meta @id
366
- end
367
-
368
- end
369
-
370
-
371
- class ReferencesHeader < StructuredHeader
372
-
373
- def refs
374
- ensure_parsed
375
- @refs
376
- end
377
-
378
- def each_id
379
- self.refs.each do |i|
380
- yield i if MESSAGE_ID =~ i
381
- end
382
- end
383
-
384
- def ids
385
- ensure_parsed
386
- @ids
387
- end
388
-
389
- def each_phrase
390
- self.refs.each do |i|
391
- yield i unless MESSAGE_ID =~ i
392
- end
393
- end
394
-
395
- def phrases
396
- result = []
397
- each_phrase do |i|
398
- result.push i
399
- end
400
- result
401
- end
402
-
403
- private
404
-
405
- def init
406
- @refs = []
407
- @ids = []
408
- end
409
-
410
- def isempty?
411
- @ids.empty?
412
- end
413
-
414
- def do_parse
415
- str = @body
416
- while m = MESSAGE_ID.match(str)
417
- pre = m.pre_match.strip
418
- @refs.push pre unless pre.empty?
419
- @refs.push s = m[0]
420
- @ids.push s
421
- str = m.post_match
422
- end
423
- str = str.strip
424
- @refs.push str unless str.empty?
425
- end
426
-
427
- def do_accept(strategy)
428
- first = true
429
- @ids.each do |i|
430
- if first
431
- first = false
432
- else
433
- strategy.space
434
- end
435
- strategy.meta i
436
- end
437
- end
438
-
439
- end
440
-
441
-
442
- class ReceivedHeader < StructuredHeader
443
-
444
- PARSE_TYPE = :RECEIVED
445
-
446
- def from
447
- ensure_parsed
448
- @from
449
- end
450
-
451
- def from=(arg)
452
- ensure_parsed
453
- @from = arg
454
- end
455
-
456
- def by
457
- ensure_parsed
458
- @by
459
- end
460
-
461
- def by=(arg)
462
- ensure_parsed
463
- @by = arg
464
- end
465
-
466
- def via
467
- ensure_parsed
468
- @via
469
- end
470
-
471
- def via=(arg)
472
- ensure_parsed
473
- @via = arg
474
- end
475
-
476
- def with
477
- ensure_parsed
478
- @with
479
- end
480
-
481
- def id
482
- ensure_parsed
483
- @id
484
- end
485
-
486
- def id=(arg)
487
- ensure_parsed
488
- @id = arg
489
- end
490
-
491
- def _for
492
- ensure_parsed
493
- @_for
494
- end
495
-
496
- def _for=(arg)
497
- ensure_parsed
498
- @_for = arg
499
- end
500
-
501
- def date
502
- ensure_parsed
503
- @date
504
- end
505
-
506
- def date=(arg)
507
- ensure_parsed
508
- @date = arg
509
- end
510
-
511
- private
512
-
513
- def init
514
- @from = @by = @via = @with = @id = @_for = nil
515
- @with = []
516
- @date = nil
517
- end
518
-
519
- def set(args)
520
- @from, @by, @via, @with, @id, @_for, @date = *args
521
- end
522
-
523
- def isempty?
524
- @with.empty? and not (@from or @by or @via or @id or @_for or @date)
525
- end
526
-
527
- def do_accept(strategy)
528
- list = []
529
- list.push 'from ' + @from if @from
530
- list.push 'by ' + @by if @by
531
- list.push 'via ' + @via if @via
532
- @with.each do |i|
533
- list.push 'with ' + i
534
- end
535
- list.push 'id ' + @id if @id
536
- list.push 'for <' + @_for + '>' if @_for
537
-
538
- first = true
539
- list.each do |i|
540
- strategy.space unless first
541
- strategy.meta i
542
- first = false
543
- end
544
- if @date
545
- strategy.meta ';'
546
- strategy.space
547
- strategy.meta time2str(@date)
548
- end
549
- end
550
-
551
- end
552
-
553
-
554
- class KeywordsHeader < StructuredHeader
555
-
556
- PARSE_TYPE = :KEYWORDS
557
-
558
- def keys
559
- ensure_parsed
560
- @keys
561
- end
562
-
563
- private
564
-
565
- def init
566
- @keys = []
567
- end
568
-
569
- def set(a)
570
- @keys = a
571
- end
572
-
573
- def isempty?
574
- @keys.empty?
575
- end
576
-
577
- def do_accept(strategy)
578
- first = true
579
- @keys.each do |i|
580
- if first
581
- first = false
582
- else
583
- strategy.meta ','
584
- end
585
- strategy.meta i
586
- end
587
- end
588
-
589
- end
590
-
591
-
592
- class EncryptedHeader < StructuredHeader
593
-
594
- PARSE_TYPE = :ENCRYPTED
595
-
596
- def encrypter
597
- ensure_parsed
598
- @encrypter
599
- end
600
-
601
- def encrypter=(arg)
602
- ensure_parsed
603
- @encrypter = arg
604
- end
605
-
606
- def keyword
607
- ensure_parsed
608
- @keyword
609
- end
610
-
611
- def keyword=(arg)
612
- ensure_parsed
613
- @keyword = arg
614
- end
615
-
616
- private
617
-
618
- def init
619
- @encrypter = nil
620
- @keyword = nil
621
- end
622
-
623
- def set(args)
624
- @encrypter, @keyword = args
625
- end
626
-
627
- def isempty?
628
- not (@encrypter or @keyword)
629
- end
630
-
631
- def do_accept(strategy)
632
- if @key
633
- strategy.meta @encrypter + ','
634
- strategy.space
635
- strategy.meta @keyword
636
- else
637
- strategy.meta @encrypter
638
- end
639
- end
640
-
641
- end
642
-
643
-
644
- class MimeVersionHeader < StructuredHeader
645
-
646
- PARSE_TYPE = :MIMEVERSION
647
-
648
- def major
649
- ensure_parsed
650
- @major
651
- end
652
-
653
- def major=(arg)
654
- ensure_parsed
655
- @major = arg
656
- end
657
-
658
- def minor
659
- ensure_parsed
660
- @minor
661
- end
662
-
663
- def minor=(arg)
664
- ensure_parsed
665
- @minor = arg
666
- end
667
-
668
- def version
669
- sprintf('%d.%d', major, minor)
670
- end
671
-
672
- private
673
-
674
- def init
675
- @major = nil
676
- @minor = nil
677
- end
678
-
679
- def set(args)
680
- @major, @minor = *args
681
- end
682
-
683
- def isempty?
684
- not (@major or @minor)
685
- end
686
-
687
- def do_accept(strategy)
688
- strategy.meta sprintf('%d.%d', @major, @minor)
689
- end
690
-
691
- end
692
-
693
-
694
- class ContentTypeHeader < StructuredHeader
695
-
696
- PARSE_TYPE = :CTYPE
697
-
698
- def main_type
699
- ensure_parsed
700
- @main
701
- end
702
-
703
- def main_type=(arg)
704
- ensure_parsed
705
- @main = arg.downcase
706
- end
707
-
708
- def sub_type
709
- ensure_parsed
710
- @sub
711
- end
712
-
713
- def sub_type=(arg)
714
- ensure_parsed
715
- @sub = arg.downcase
716
- end
717
-
718
- def content_type
719
- ensure_parsed
720
- @sub ? sprintf('%s/%s', @main, @sub) : @main
721
- end
722
-
723
- def params
724
- ensure_parsed
725
- @params
726
- end
727
-
728
- def [](key)
729
- ensure_parsed
730
- @params and @params[key]
731
- end
732
-
733
- def []=(key, val)
734
- ensure_parsed
735
- (@params ||= {})[key] = val
736
- end
737
-
738
- private
739
-
740
- def init
741
- @main = @sub = @params = nil
742
- end
743
-
744
- def set(args)
745
- @main, @sub, @params = *args
746
- end
747
-
748
- def isempty?
749
- not (@main or @sub)
750
- end
751
-
752
- def do_accept(strategy)
753
- if @sub
754
- strategy.meta sprintf('%s/%s', @main, @sub)
755
- else
756
- strategy.meta @main
757
- end
758
- @params.each do |k,v|
759
- strategy.meta ';'
760
- strategy.space
761
- strategy.kv_pair k, v
762
- end
763
- end
764
-
765
- end
766
-
767
-
768
- class ContentTransferEncodingHeader < StructuredHeader
769
-
770
- PARSE_TYPE = :CENCODING
771
-
772
- def encoding
773
- ensure_parsed
774
- @encoding
775
- end
776
-
777
- def encoding=(arg)
778
- ensure_parsed
779
- @encoding = arg
780
- end
781
-
782
- private
783
-
784
- def init
785
- @encoding = nil
786
- end
787
-
788
- def set(s)
789
- @encoding = s
790
- end
791
-
792
- def isempty?
793
- not @encoding
794
- end
795
-
796
- def do_accept(strategy)
797
- strategy.meta @encoding.capitalize
798
- end
799
-
800
- end
801
-
802
-
803
- class ContentDispositionHeader < StructuredHeader
804
-
805
- PARSE_TYPE = :CDISPOSITION
806
-
807
- def disposition
808
- ensure_parsed
809
- @disposition
810
- end
811
-
812
- def disposition=(str)
813
- ensure_parsed
814
- @disposition = str.downcase
815
- end
816
-
817
- def params
818
- ensure_parsed
819
- @params
820
- end
821
-
822
- def [](key)
823
- ensure_parsed
824
- @params and @params[key]
825
- end
826
-
827
- def []=(key, val)
828
- ensure_parsed
829
- (@params ||= {})[key] = val
830
- end
831
-
832
- private
833
-
834
- def init
835
- @disposition = @params = nil
836
- end
837
-
838
- def set(args)
839
- @disposition, @params = *args
840
- end
841
-
842
- def isempty?
843
- not @disposition and (not @params or @params.empty?)
844
- end
845
-
846
- def do_accept(strategy)
847
- strategy.meta @disposition
848
- @params.each do |k,v|
849
- strategy.meta ';'
850
- strategy.space
851
- strategy.kv_pair k, v
852
- end
853
- end
854
-
855
- end
856
-
857
-
858
- class HeaderField # redefine
859
-
860
- FNAME_TO_CLASS = {
861
- 'date' => DateTimeHeader,
862
- 'resent-date' => DateTimeHeader,
863
- 'to' => AddressHeader,
864
- 'cc' => AddressHeader,
865
- 'bcc' => AddressHeader,
866
- 'from' => AddressHeader,
867
- 'reply-to' => AddressHeader,
868
- 'resent-to' => AddressHeader,
869
- 'resent-cc' => AddressHeader,
870
- 'resent-bcc' => AddressHeader,
871
- 'resent-from' => AddressHeader,
872
- 'resent-reply-to' => AddressHeader,
873
- 'sender' => SingleAddressHeader,
874
- 'resent-sender' => SingleAddressHeader,
875
- 'return-path' => ReturnPathHeader,
876
- 'message-id' => MessageIdHeader,
877
- 'resent-message-id' => MessageIdHeader,
878
- 'in-reply-to' => ReferencesHeader,
879
- 'received' => ReceivedHeader,
880
- 'references' => ReferencesHeader,
881
- 'keywords' => KeywordsHeader,
882
- 'encrypted' => EncryptedHeader,
883
- 'mime-version' => MimeVersionHeader,
884
- 'content-type' => ContentTypeHeader,
885
- 'content-transfer-encoding' => ContentTransferEncodingHeader,
886
- 'content-disposition' => ContentDispositionHeader,
887
- 'content-id' => MessageIdHeader,
888
- 'subject' => UnstructuredHeader,
889
- 'comments' => UnstructuredHeader,
890
- 'content-description' => UnstructuredHeader
891
- }
892
-
893
- end
894
-
895
- end # module TMail