lonbaker-tmail 1.2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/CHANGES +74 -0
  2. data/LICENSE +21 -0
  3. data/NOTES +7 -0
  4. data/README +169 -0
  5. data/ext/Makefile +20 -0
  6. data/ext/tmailscanner/tmail/MANIFEST +4 -0
  7. data/ext/tmailscanner/tmail/depend +1 -0
  8. data/ext/tmailscanner/tmail/extconf.rb +33 -0
  9. data/ext/tmailscanner/tmail/tmailscanner.c +583 -0
  10. data/lib/tmail.rb +5 -0
  11. data/lib/tmail/Makefile +18 -0
  12. data/lib/tmail/address.rb +426 -0
  13. data/lib/tmail/attachments.rb +46 -0
  14. data/lib/tmail/base64.rb +46 -0
  15. data/lib/tmail/compat.rb +41 -0
  16. data/lib/tmail/config.rb +67 -0
  17. data/lib/tmail/core_extensions.rb +63 -0
  18. data/lib/tmail/encode.rb +590 -0
  19. data/lib/tmail/header.rb +962 -0
  20. data/lib/tmail/index.rb +9 -0
  21. data/lib/tmail/interface.rb +1130 -0
  22. data/lib/tmail/loader.rb +3 -0
  23. data/lib/tmail/mail.rb +580 -0
  24. data/lib/tmail/mailbox.rb +496 -0
  25. data/lib/tmail/main.rb +6 -0
  26. data/lib/tmail/mbox.rb +3 -0
  27. data/lib/tmail/net.rb +248 -0
  28. data/lib/tmail/obsolete.rb +132 -0
  29. data/lib/tmail/parser.rb +1476 -0
  30. data/lib/tmail/parser.y +381 -0
  31. data/lib/tmail/port.rb +379 -0
  32. data/lib/tmail/quoting.rb +118 -0
  33. data/lib/tmail/require_arch.rb +58 -0
  34. data/lib/tmail/scanner.rb +49 -0
  35. data/lib/tmail/scanner_r.rb +261 -0
  36. data/lib/tmail/stringio.rb +280 -0
  37. data/lib/tmail/utils.rb +351 -0
  38. data/lib/tmail/version.rb +39 -0
  39. data/meta/MANIFEST +128 -0
  40. data/meta/project.yaml +30 -0
  41. data/meta/unixname +1 -0
  42. data/sample/bench_base64.rb +48 -0
  43. data/sample/data/multipart +23 -0
  44. data/sample/data/normal +29 -0
  45. data/sample/data/sendtest +5 -0
  46. data/sample/data/simple +14 -0
  47. data/sample/data/test +27 -0
  48. data/sample/extract-attachements.rb +33 -0
  49. data/sample/from-check.rb +26 -0
  50. data/sample/multipart.rb +26 -0
  51. data/sample/parse-bench.rb +68 -0
  52. data/sample/parse-test.rb +19 -0
  53. data/sample/sendmail.rb +94 -0
  54. data/test/extctrl.rb +6 -0
  55. data/test/fixtures/mailbox +414 -0
  56. data/test/fixtures/mailbox_without_any_from_or_sender +10 -0
  57. data/test/fixtures/mailbox_without_from +11 -0
  58. data/test/fixtures/mailbox_without_return_path +12 -0
  59. data/test/fixtures/raw_base64_decoded_string +0 -0
  60. data/test/fixtures/raw_base64_email +83 -0
  61. data/test/fixtures/raw_base64_encoded_string +1 -0
  62. data/test/fixtures/raw_email +14 -0
  63. data/test/fixtures/raw_email10 +20 -0
  64. data/test/fixtures/raw_email11 +34 -0
  65. data/test/fixtures/raw_email12 +32 -0
  66. data/test/fixtures/raw_email13 +29 -0
  67. data/test/fixtures/raw_email2 +114 -0
  68. data/test/fixtures/raw_email3 +70 -0
  69. data/test/fixtures/raw_email4 +59 -0
  70. data/test/fixtures/raw_email5 +19 -0
  71. data/test/fixtures/raw_email6 +20 -0
  72. data/test/fixtures/raw_email7 +66 -0
  73. data/test/fixtures/raw_email8 +47 -0
  74. data/test/fixtures/raw_email9 +28 -0
  75. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  76. data/test/fixtures/raw_email_reply +32 -0
  77. data/test/fixtures/raw_email_simple +11 -0
  78. data/test/fixtures/raw_email_with_bad_date +48 -0
  79. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  80. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  81. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  82. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  83. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  84. data/test/kcode.rb +14 -0
  85. data/test/test_address.rb +1211 -0
  86. data/test/test_attachments.rb +60 -0
  87. data/test/test_base64.rb +64 -0
  88. data/test/test_encode.rb +85 -0
  89. data/test/test_header.rb +969 -0
  90. data/test/test_helper.rb +9 -0
  91. data/test/test_mail.rb +753 -0
  92. data/test/test_mbox.rb +184 -0
  93. data/test/test_port.rb +436 -0
  94. data/test/test_quote.rb +98 -0
  95. data/test/test_scanner.rb +209 -0
  96. data/test/test_utils.rb +36 -0
  97. metadata +159 -0
@@ -0,0 +1,962 @@
1
+ #--
2
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
24
+ # with permission of Minero Aoki.
25
+ #++
26
+
27
+ require 'tmail/encode'
28
+ require 'tmail/address'
29
+ require 'tmail/parser'
30
+ require 'tmail/config'
31
+ require 'tmail/utils'
32
+
33
+ #:startdoc:
34
+ module TMail
35
+
36
+ # Provides methods to handle and manipulate headers in the email
37
+ class HeaderField
38
+
39
+ include TextUtils
40
+
41
+ class << self
42
+
43
+ alias newobj new
44
+
45
+ def new( name, body, conf = DEFAULT_CONFIG )
46
+ klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
47
+ klass.newobj body, conf
48
+ end
49
+
50
+ # Returns a HeaderField object matching the header you specify in the "name" param.
51
+ # Requires an initialized TMail::Port to be passed in.
52
+ #
53
+ # The method searches the header of the Port you pass into it to find a match on
54
+ # the header line you pass. Once a match is found, it will unwrap the matching line
55
+ # as needed to return an initialized HeaderField object.
56
+ #
57
+ # If you want to get the Envelope sender of the email object, pass in "EnvelopeSender",
58
+ # if you want the From address of the email itself, pass in 'From'.
59
+ #
60
+ # This is because a mailbox doesn't have the : after the From that designates the
61
+ # beginning of the envelope sender (which can be different to the from address of
62
+ # the email)
63
+ #
64
+ # Other fields can be passed as normal, "Reply-To", "Received" etc.
65
+ #
66
+ # Note: Change of behaviour in 1.2.1 => returns nil if it does not find the specified
67
+ # header field, otherwise returns an instantiated object of the correct header class
68
+ #
69
+ # For example:
70
+ # port = TMail::FilePort.new("/test/fixtures/raw_email_simple")
71
+ # h = TMail::HeaderField.new_from_port(port, "From")
72
+ # h.addrs.to_s #=> "Mikel Lindsaar <mikel@nowhere.com>"
73
+ # h = TMail::HeaderField.new_from_port(port, "EvelopeSender")
74
+ # h.addrs.to_s #=> "mike@anotherplace.com.au"
75
+ # h = TMail::HeaderField.new_from_port(port, "SomeWeirdHeaderField")
76
+ # h #=> nil
77
+ def new_from_port( port, name, conf = DEFAULT_CONFIG )
78
+ if name == "EnvelopeSender"
79
+ name = "From"
80
+ re = Regexp.new('\A(From) ', 'i')
81
+ else
82
+ re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
83
+ end
84
+ str = nil
85
+ port.ropen {|f|
86
+ f.each do |line|
87
+ if m = re.match(line) then str = m.post_match.strip
88
+ elsif str and /\A[\t ]/ === line then str << ' ' << line.strip
89
+ elsif /\A-*\s*\z/ === line then break
90
+ elsif str then break
91
+ end
92
+ end
93
+ }
94
+ new(name, str, Config.to_config(conf)) if str
95
+ end
96
+
97
+ def internal_new( name, conf )
98
+ FNAME_TO_CLASS[name].newobj('', conf, true)
99
+ end
100
+
101
+ end # class << self
102
+
103
+ def initialize( body, conf, intern = false )
104
+ @body = body
105
+ @config = conf
106
+
107
+ @illegal = false
108
+ @parsed = false
109
+
110
+ if intern
111
+ @parsed = true
112
+ parse_init
113
+ end
114
+ end
115
+
116
+ def inspect
117
+ "#<#{self.class} #{@body.inspect}>"
118
+ end
119
+
120
+ def illegal?
121
+ @illegal
122
+ end
123
+
124
+ def empty?
125
+ ensure_parsed
126
+ return true if @illegal
127
+ isempty?
128
+ end
129
+
130
+ private
131
+
132
+ def ensure_parsed
133
+ return if @parsed
134
+ @parsed = true
135
+ parse
136
+ end
137
+
138
+ # defabstract parse
139
+ # end
140
+
141
+ def clear_parse_status
142
+ @parsed = false
143
+ @illegal = false
144
+ end
145
+
146
+ public
147
+
148
+ def body
149
+ ensure_parsed
150
+ v = Decoder.new(s = '')
151
+ do_accept v
152
+ v.terminate
153
+ s
154
+ end
155
+
156
+ def body=( str )
157
+ @body = str
158
+ clear_parse_status
159
+ end
160
+
161
+ include StrategyInterface
162
+
163
+ def accept( strategy )
164
+ ensure_parsed
165
+ do_accept strategy
166
+ strategy.terminate
167
+ end
168
+
169
+ # abstract do_accept
170
+
171
+ end
172
+
173
+
174
+ class UnstructuredHeader < HeaderField
175
+
176
+ def body
177
+ ensure_parsed
178
+ @body
179
+ end
180
+
181
+ def body=( arg )
182
+ ensure_parsed
183
+ @body = arg
184
+ end
185
+
186
+ private
187
+
188
+ def parse_init
189
+ end
190
+
191
+ def parse
192
+ @body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
193
+ end
194
+
195
+ def isempty?
196
+ not @body
197
+ end
198
+
199
+ def do_accept( strategy )
200
+ strategy.text @body
201
+ end
202
+
203
+ end
204
+
205
+
206
+ class StructuredHeader < HeaderField
207
+
208
+ def comments
209
+ ensure_parsed
210
+ if @comments[0]
211
+ [Decoder.decode(@comments[0])]
212
+ else
213
+ @comments
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ def parse
220
+ save = nil
221
+
222
+ begin
223
+ parse_init
224
+ do_parse
225
+ rescue SyntaxError
226
+ if not save and mime_encoded? @body
227
+ save = @body
228
+ @body = Decoder.decode(save)
229
+ retry
230
+ elsif save
231
+ @body = save
232
+ end
233
+
234
+ @illegal = true
235
+ raise if @config.strict_parse?
236
+ end
237
+ end
238
+
239
+ def parse_init
240
+ @comments = []
241
+ init
242
+ end
243
+
244
+ def do_parse
245
+ quote_boundary
246
+ obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
247
+ set obj if obj
248
+ end
249
+
250
+ end
251
+
252
+
253
+ class DateTimeHeader < StructuredHeader
254
+
255
+ PARSE_TYPE = :DATETIME
256
+
257
+ def date
258
+ ensure_parsed
259
+ @date
260
+ end
261
+
262
+ def date=( arg )
263
+ ensure_parsed
264
+ @date = arg
265
+ end
266
+
267
+ private
268
+
269
+ def init
270
+ clean_date
271
+ @date = nil
272
+ end
273
+
274
+ def set( t )
275
+ @date = t
276
+ end
277
+
278
+ def isempty?
279
+ not @date
280
+ end
281
+
282
+ def do_accept( strategy )
283
+ strategy.meta time2str(@date)
284
+ end
285
+
286
+ end
287
+
288
+
289
+ class AddressHeader < StructuredHeader
290
+
291
+ PARSE_TYPE = :MADDRESS
292
+
293
+ def addrs
294
+ ensure_parsed
295
+ @addrs
296
+ end
297
+
298
+ private
299
+
300
+ def init
301
+ @addrs = []
302
+ end
303
+
304
+ def set( a )
305
+ @addrs = a
306
+ end
307
+
308
+ def isempty?
309
+ @addrs.empty?
310
+ end
311
+
312
+ def do_accept( strategy )
313
+ first = true
314
+ @addrs.each do |a|
315
+ if first
316
+ first = false
317
+ else
318
+ strategy.puts_meta ','
319
+ strategy.space
320
+ end
321
+ a.accept strategy
322
+ end
323
+
324
+ @comments.each do |c|
325
+ strategy.space
326
+ strategy.meta '('
327
+ strategy.text c
328
+ strategy.meta ')'
329
+ end
330
+ end
331
+
332
+ end
333
+
334
+
335
+ class ReturnPathHeader < AddressHeader
336
+
337
+ PARSE_TYPE = :RETPATH
338
+
339
+ def addr
340
+ addrs()[0]
341
+ end
342
+
343
+ def spec
344
+ a = addr() or return nil
345
+ a.spec
346
+ end
347
+
348
+ def routes
349
+ a = addr() or return nil
350
+ a.routes
351
+ end
352
+
353
+ private
354
+
355
+ def do_accept( strategy )
356
+ a = addr()
357
+
358
+ strategy.meta '<'
359
+ unless a.routes.empty?
360
+ strategy.meta a.routes.map {|i| '@' + i }.join(',')
361
+ strategy.meta ':'
362
+ end
363
+ spec = a.spec
364
+ strategy.meta spec if spec
365
+ strategy.meta '>'
366
+ end
367
+
368
+ end
369
+
370
+
371
+ class SingleAddressHeader < AddressHeader
372
+
373
+ def addr
374
+ addrs()[0]
375
+ end
376
+
377
+ private
378
+
379
+ def do_accept( strategy )
380
+ a = addr()
381
+ a.accept strategy
382
+ @comments.each do |c|
383
+ strategy.space
384
+ strategy.meta '('
385
+ strategy.text c
386
+ strategy.meta ')'
387
+ end
388
+ end
389
+
390
+ end
391
+
392
+
393
+ class MessageIdHeader < StructuredHeader
394
+
395
+ def id
396
+ ensure_parsed
397
+ @id
398
+ end
399
+
400
+ def id=( arg )
401
+ ensure_parsed
402
+ @id = arg
403
+ end
404
+
405
+ private
406
+
407
+ def init
408
+ @id = nil
409
+ end
410
+
411
+ def isempty?
412
+ not @id
413
+ end
414
+
415
+ def do_parse
416
+ @id = @body.slice(MESSAGE_ID) or
417
+ raise SyntaxError, "wrong Message-ID format: #{@body}"
418
+ end
419
+
420
+ def do_accept( strategy )
421
+ strategy.meta @id
422
+ end
423
+
424
+ end
425
+
426
+
427
+ class ReferencesHeader < StructuredHeader
428
+
429
+ def refs
430
+ ensure_parsed
431
+ @refs
432
+ end
433
+
434
+ def each_id
435
+ self.refs.each do |i|
436
+ yield i if MESSAGE_ID === i
437
+ end
438
+ end
439
+
440
+ def ids
441
+ ensure_parsed
442
+ @ids
443
+ end
444
+
445
+ def each_phrase
446
+ self.refs.each do |i|
447
+ yield i unless MESSAGE_ID === i
448
+ end
449
+ end
450
+
451
+ def phrases
452
+ ret = []
453
+ each_phrase {|i| ret.push i }
454
+ ret
455
+ end
456
+
457
+ private
458
+
459
+ def init
460
+ @refs = []
461
+ @ids = []
462
+ end
463
+
464
+ def isempty?
465
+ @ids.empty?
466
+ end
467
+
468
+ def do_parse
469
+ str = @body
470
+ while m = MESSAGE_ID.match(str)
471
+ pre = m.pre_match.strip
472
+ @refs.push pre unless pre.empty?
473
+ @refs.push s = m[0]
474
+ @ids.push s
475
+ str = m.post_match
476
+ end
477
+ str = str.strip
478
+ @refs.push str unless str.empty?
479
+ end
480
+
481
+ def do_accept( strategy )
482
+ first = true
483
+ @ids.each do |i|
484
+ if first
485
+ first = false
486
+ else
487
+ strategy.space
488
+ end
489
+ strategy.meta i
490
+ end
491
+ end
492
+
493
+ end
494
+
495
+
496
+ class ReceivedHeader < StructuredHeader
497
+
498
+ PARSE_TYPE = :RECEIVED
499
+
500
+ def from
501
+ ensure_parsed
502
+ @from
503
+ end
504
+
505
+ def from=( arg )
506
+ ensure_parsed
507
+ @from = arg
508
+ end
509
+
510
+ def by
511
+ ensure_parsed
512
+ @by
513
+ end
514
+
515
+ def by=( arg )
516
+ ensure_parsed
517
+ @by = arg
518
+ end
519
+
520
+ def via
521
+ ensure_parsed
522
+ @via
523
+ end
524
+
525
+ def via=( arg )
526
+ ensure_parsed
527
+ @via = arg
528
+ end
529
+
530
+ def with
531
+ ensure_parsed
532
+ @with
533
+ end
534
+
535
+ def id
536
+ ensure_parsed
537
+ @id
538
+ end
539
+
540
+ def id=( arg )
541
+ ensure_parsed
542
+ @id = arg
543
+ end
544
+
545
+ def _for
546
+ ensure_parsed
547
+ @_for
548
+ end
549
+
550
+ def _for=( arg )
551
+ ensure_parsed
552
+ @_for = arg
553
+ end
554
+
555
+ def date
556
+ ensure_parsed
557
+ @date
558
+ end
559
+
560
+ def date=( arg )
561
+ ensure_parsed
562
+ @date = arg
563
+ end
564
+
565
+ private
566
+
567
+ def init
568
+ quote_parentheses
569
+ @from = @by = @via = @with = @id = @_for = nil
570
+ @with = []
571
+ @date = nil
572
+ end
573
+
574
+ def set( args )
575
+ @from, @by, @via, @with, @id, @_for, @date = *args
576
+ end
577
+
578
+ def isempty?
579
+ @with.empty? and not (@from or @by or @via or @id or @_for or @date)
580
+ end
581
+
582
+ def do_accept( strategy )
583
+ list = []
584
+ list.push 'from ' + @from if @from
585
+ list.push 'by ' + @by if @by
586
+ list.push 'via ' + @via if @via
587
+ @with.each do |i|
588
+ list.push 'with ' + i
589
+ end
590
+ list.push 'id ' + @id if @id
591
+ list.push 'for <' + @_for + '>' if @_for
592
+
593
+ first = true
594
+ list.each do |i|
595
+ strategy.space unless first
596
+ strategy.meta i
597
+ first = false
598
+ end
599
+ if @date
600
+ strategy.meta ';'
601
+ strategy.space
602
+ strategy.meta time2str(@date)
603
+ end
604
+ end
605
+
606
+ end
607
+
608
+
609
+ class KeywordsHeader < StructuredHeader
610
+
611
+ PARSE_TYPE = :KEYWORDS
612
+
613
+ def keys
614
+ ensure_parsed
615
+ @keys
616
+ end
617
+
618
+ private
619
+
620
+ def init
621
+ @keys = []
622
+ end
623
+
624
+ def set( a )
625
+ @keys = a
626
+ end
627
+
628
+ def isempty?
629
+ @keys.empty?
630
+ end
631
+
632
+ def do_accept( strategy )
633
+ first = true
634
+ @keys.each do |i|
635
+ if first
636
+ first = false
637
+ else
638
+ strategy.meta ','
639
+ end
640
+ strategy.meta i
641
+ end
642
+ end
643
+
644
+ end
645
+
646
+
647
+ class EncryptedHeader < StructuredHeader
648
+
649
+ PARSE_TYPE = :ENCRYPTED
650
+
651
+ def encrypter
652
+ ensure_parsed
653
+ @encrypter
654
+ end
655
+
656
+ def encrypter=( arg )
657
+ ensure_parsed
658
+ @encrypter = arg
659
+ end
660
+
661
+ def keyword
662
+ ensure_parsed
663
+ @keyword
664
+ end
665
+
666
+ def keyword=( arg )
667
+ ensure_parsed
668
+ @keyword = arg
669
+ end
670
+
671
+ private
672
+
673
+ def init
674
+ @encrypter = nil
675
+ @keyword = nil
676
+ end
677
+
678
+ def set( args )
679
+ @encrypter, @keyword = args
680
+ end
681
+
682
+ def isempty?
683
+ not (@encrypter or @keyword)
684
+ end
685
+
686
+ def do_accept( strategy )
687
+ if @key
688
+ strategy.meta @encrypter + ','
689
+ strategy.space
690
+ strategy.meta @keyword
691
+ else
692
+ strategy.meta @encrypter
693
+ end
694
+ end
695
+
696
+ end
697
+
698
+
699
+ class MimeVersionHeader < StructuredHeader
700
+
701
+ PARSE_TYPE = :MIMEVERSION
702
+
703
+ def major
704
+ ensure_parsed
705
+ @major
706
+ end
707
+
708
+ def major=( arg )
709
+ ensure_parsed
710
+ @major = arg
711
+ end
712
+
713
+ def minor
714
+ ensure_parsed
715
+ @minor
716
+ end
717
+
718
+ def minor=( arg )
719
+ ensure_parsed
720
+ @minor = arg
721
+ end
722
+
723
+ def version
724
+ sprintf('%d.%d', major, minor)
725
+ end
726
+
727
+ private
728
+
729
+ def init
730
+ @major = nil
731
+ @minor = nil
732
+ end
733
+
734
+ def set( args )
735
+ @major, @minor = *args
736
+ end
737
+
738
+ def isempty?
739
+ not (@major or @minor)
740
+ end
741
+
742
+ def do_accept( strategy )
743
+ strategy.meta sprintf('%d.%d', @major, @minor)
744
+ end
745
+
746
+ end
747
+
748
+
749
+ class ContentTypeHeader < StructuredHeader
750
+
751
+ PARSE_TYPE = :CTYPE
752
+
753
+ def main_type
754
+ ensure_parsed
755
+ @main
756
+ end
757
+
758
+ def main_type=( arg )
759
+ ensure_parsed
760
+ @main = arg.downcase
761
+ end
762
+
763
+ def sub_type
764
+ ensure_parsed
765
+ @sub
766
+ end
767
+
768
+ def sub_type=( arg )
769
+ ensure_parsed
770
+ @sub = arg.downcase
771
+ end
772
+
773
+ def content_type
774
+ ensure_parsed
775
+ @sub ? sprintf('%s/%s', @main, @sub) : @main
776
+ end
777
+
778
+ def params
779
+ ensure_parsed
780
+ unless @params.blank?
781
+ @params.each do |k, v|
782
+ @params[k] = unquote(v)
783
+ end
784
+ end
785
+ @params
786
+ end
787
+
788
+ def []( key )
789
+ ensure_parsed
790
+ @params and unquote(@params[key])
791
+ end
792
+
793
+ def []=( key, val )
794
+ ensure_parsed
795
+ (@params ||= {})[key] = val
796
+ end
797
+
798
+ private
799
+
800
+ def init
801
+ @main = @sub = @params = nil
802
+ end
803
+
804
+ def set( args )
805
+ @main, @sub, @params = *args
806
+ end
807
+
808
+ def isempty?
809
+ not (@main or @sub)
810
+ end
811
+
812
+ def do_accept( strategy )
813
+ if @sub
814
+ strategy.meta sprintf('%s/%s', @main, @sub)
815
+ else
816
+ strategy.meta @main
817
+ end
818
+ @params.each do |k,v|
819
+ if v
820
+ strategy.meta ';'
821
+ strategy.space
822
+ strategy.kv_pair k, v
823
+ end
824
+ end
825
+ end
826
+
827
+ end
828
+
829
+
830
+ class ContentTransferEncodingHeader < StructuredHeader
831
+
832
+ PARSE_TYPE = :CENCODING
833
+
834
+ def encoding
835
+ ensure_parsed
836
+ @encoding
837
+ end
838
+
839
+ def encoding=( arg )
840
+ ensure_parsed
841
+ @encoding = arg
842
+ end
843
+
844
+ private
845
+
846
+ def init
847
+ @encoding = nil
848
+ end
849
+
850
+ def set( s )
851
+ @encoding = s
852
+ end
853
+
854
+ def isempty?
855
+ not @encoding
856
+ end
857
+
858
+ def do_accept( strategy )
859
+ strategy.meta @encoding.capitalize
860
+ end
861
+
862
+ end
863
+
864
+
865
+ class ContentDispositionHeader < StructuredHeader
866
+
867
+ PARSE_TYPE = :CDISPOSITION
868
+
869
+ def disposition
870
+ ensure_parsed
871
+ @disposition
872
+ end
873
+
874
+ def disposition=( str )
875
+ ensure_parsed
876
+ @disposition = str.downcase
877
+ end
878
+
879
+ def params
880
+ ensure_parsed
881
+ unless @params.blank?
882
+ @params.each do |k, v|
883
+ @params[k] = unquote(v)
884
+ end
885
+ end
886
+ @params
887
+ end
888
+
889
+ def []( key )
890
+ ensure_parsed
891
+ @params and unquote(@params[key])
892
+ end
893
+
894
+ def []=( key, val )
895
+ ensure_parsed
896
+ (@params ||= {})[key] = val
897
+ end
898
+
899
+ private
900
+
901
+ def init
902
+ @disposition = @params = nil
903
+ end
904
+
905
+ def set( args )
906
+ @disposition, @params = *args
907
+ end
908
+
909
+ def isempty?
910
+ not @disposition and (not @params or @params.empty?)
911
+ end
912
+
913
+ def do_accept( strategy )
914
+ strategy.meta @disposition
915
+ @params.each do |k,v|
916
+ strategy.meta ';'
917
+ strategy.space
918
+ strategy.kv_pair k, unquote(v)
919
+ end
920
+ end
921
+
922
+ end
923
+
924
+
925
+ class HeaderField # redefine
926
+
927
+ FNAME_TO_CLASS = {
928
+ 'date' => DateTimeHeader,
929
+ 'resent-date' => DateTimeHeader,
930
+ 'to' => AddressHeader,
931
+ 'cc' => AddressHeader,
932
+ 'bcc' => AddressHeader,
933
+ 'from' => AddressHeader,
934
+ 'reply-to' => AddressHeader,
935
+ 'resent-to' => AddressHeader,
936
+ 'resent-cc' => AddressHeader,
937
+ 'resent-bcc' => AddressHeader,
938
+ 'resent-from' => AddressHeader,
939
+ 'resent-reply-to' => AddressHeader,
940
+ 'sender' => SingleAddressHeader,
941
+ 'resent-sender' => SingleAddressHeader,
942
+ 'return-path' => ReturnPathHeader,
943
+ 'message-id' => MessageIdHeader,
944
+ 'resent-message-id' => MessageIdHeader,
945
+ 'in-reply-to' => ReferencesHeader,
946
+ 'received' => ReceivedHeader,
947
+ 'references' => ReferencesHeader,
948
+ 'keywords' => KeywordsHeader,
949
+ 'encrypted' => EncryptedHeader,
950
+ 'mime-version' => MimeVersionHeader,
951
+ 'content-type' => ContentTypeHeader,
952
+ 'content-transfer-encoding' => ContentTransferEncodingHeader,
953
+ 'content-disposition' => ContentDispositionHeader,
954
+ 'content-id' => UnstructuredHeader,
955
+ 'subject' => UnstructuredHeader,
956
+ 'comments' => UnstructuredHeader,
957
+ 'content-description' => UnstructuredHeader
958
+ }
959
+
960
+ end
961
+
962
+ end # module TMail