tmail 1.1.1

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.
Files changed (102) hide show
  1. data/LICENSE +21 -0
  2. data/README +157 -0
  3. data/bat/changelog +19 -0
  4. data/bat/clobber/package +10 -0
  5. data/bat/compile +42 -0
  6. data/bat/config.yaml +8 -0
  7. data/bat/prepare +8 -0
  8. data/bat/publish +51 -0
  9. data/bat/rdoc +42 -0
  10. data/bat/release +12 -0
  11. data/bat/setup +1616 -0
  12. data/bat/stats +138 -0
  13. data/bat/tag +25 -0
  14. data/bat/test +25 -0
  15. data/ext/tmail/Makefile +25 -0
  16. data/ext/tmail/base64/MANIFEST +4 -0
  17. data/ext/tmail/base64/base64.c +264 -0
  18. data/ext/tmail/base64/depend +1 -0
  19. data/ext/tmail/base64/extconf.rb +38 -0
  20. data/ext/tmail/scanner_c/MANIFEST +4 -0
  21. data/ext/tmail/scanner_c/depend +1 -0
  22. data/ext/tmail/scanner_c/extconf.rb +38 -0
  23. data/ext/tmail/scanner_c/scanner_c.c +582 -0
  24. data/lib/tmail.rb +4 -0
  25. data/lib/tmail/Makefile +19 -0
  26. data/lib/tmail/address.rb +245 -0
  27. data/lib/tmail/attachments.rb +47 -0
  28. data/lib/tmail/base64.rb +75 -0
  29. data/lib/tmail/compat.rb +39 -0
  30. data/lib/tmail/config.rb +71 -0
  31. data/lib/tmail/core_extensions.rb +67 -0
  32. data/lib/tmail/encode.rb +524 -0
  33. data/lib/tmail/header.rb +931 -0
  34. data/lib/tmail/index.rb +8 -0
  35. data/lib/tmail/interface.rb +540 -0
  36. data/lib/tmail/loader.rb +1 -0
  37. data/lib/tmail/mail.rb +507 -0
  38. data/lib/tmail/mailbox.rb +435 -0
  39. data/lib/tmail/mbox.rb +1 -0
  40. data/lib/tmail/net.rb +282 -0
  41. data/lib/tmail/obsolete.rb +137 -0
  42. data/lib/tmail/parser.rb +1475 -0
  43. data/lib/tmail/parser.y +381 -0
  44. data/lib/tmail/port.rb +379 -0
  45. data/lib/tmail/quoting.rb +142 -0
  46. data/lib/tmail/require_arch.rb +56 -0
  47. data/lib/tmail/scanner.rb +44 -0
  48. data/lib/tmail/scanner_r.rb +263 -0
  49. data/lib/tmail/stringio.rb +279 -0
  50. data/lib/tmail/tmail.rb +1 -0
  51. data/lib/tmail/utils.rb +281 -0
  52. data/lib/tmail/version.rb +38 -0
  53. data/meta/icli.yaml +16 -0
  54. data/meta/tmail-1.1.1.roll +24 -0
  55. data/sample/data/multipart +23 -0
  56. data/sample/data/normal +29 -0
  57. data/sample/data/sendtest +5 -0
  58. data/sample/data/simple +14 -0
  59. data/sample/data/test +27 -0
  60. data/sample/extract-attachements.rb +33 -0
  61. data/sample/from-check.rb +26 -0
  62. data/sample/multipart.rb +26 -0
  63. data/sample/parse-bench.rb +68 -0
  64. data/sample/parse-test.rb +19 -0
  65. data/sample/sendmail.rb +94 -0
  66. data/test/extctrl.rb +6 -0
  67. data/test/fixtures/raw_base64_decoded_string +0 -0
  68. data/test/fixtures/raw_base64_email +83 -0
  69. data/test/fixtures/raw_base64_encoded_string +1 -0
  70. data/test/fixtures/raw_email +14 -0
  71. data/test/fixtures/raw_email10 +20 -0
  72. data/test/fixtures/raw_email11 +34 -0
  73. data/test/fixtures/raw_email12 +32 -0
  74. data/test/fixtures/raw_email13 +29 -0
  75. data/test/fixtures/raw_email2 +114 -0
  76. data/test/fixtures/raw_email3 +70 -0
  77. data/test/fixtures/raw_email4 +59 -0
  78. data/test/fixtures/raw_email5 +19 -0
  79. data/test/fixtures/raw_email6 +20 -0
  80. data/test/fixtures/raw_email7 +66 -0
  81. data/test/fixtures/raw_email8 +47 -0
  82. data/test/fixtures/raw_email9 +28 -0
  83. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  84. data/test/fixtures/raw_email_simple +11 -0
  85. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  86. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  87. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  88. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  89. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  90. data/test/kcode.rb +14 -0
  91. data/test/test_address.rb +1128 -0
  92. data/test/test_attachments.rb +35 -0
  93. data/test/test_base64.rb +63 -0
  94. data/test/test_encode.rb +77 -0
  95. data/test/test_header.rb +885 -0
  96. data/test/test_helper.rb +2 -0
  97. data/test/test_mail.rb +623 -0
  98. data/test/test_mbox.rb +126 -0
  99. data/test/test_port.rb +430 -0
  100. data/test/test_scanner.rb +209 -0
  101. data/test/test_utils.rb +37 -0
  102. metadata +205 -0
@@ -0,0 +1,4 @@
1
+ require 'tmail/version'
2
+ require 'tmail/mail'
3
+ require 'tmail/mailbox'
4
+ require 'tmail/core_extensions'
@@ -0,0 +1,19 @@
1
+ #
2
+ # lib/tmail/Makefile
3
+ #
4
+
5
+ debug:
6
+ rm -f parser.rb
7
+ make parser.rb DEBUG=true
8
+
9
+ parser.rb: parser.y
10
+ if [ "$(DEBUG)" = true ]; then \
11
+ racc -v -g -o$@ parser.y ;\
12
+ else \
13
+ racc -E -o$@ parser.y ;\
14
+ fi
15
+
16
+ clean:
17
+ rm -f parser.rb parser.output
18
+
19
+ distclean: clean
@@ -0,0 +1,245 @@
1
+ =begin rdoc
2
+
3
+ = Address handling class
4
+
5
+ =end
6
+ #
7
+ #--
8
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining
11
+ # a copy of this software and associated documentation files (the
12
+ # "Software"), to deal in the Software without restriction, including
13
+ # without limitation the rights to use, copy, modify, merge, publish,
14
+ # distribute, sublicense, and/or sell copies of the Software, and to
15
+ # permit persons to whom the Software is furnished to do so, subject to
16
+ # the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be
19
+ # included in all copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+ #
29
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
30
+ # with permission of Minero Aoki.
31
+ #++
32
+
33
+ require 'tmail/encode'
34
+ require 'tmail/parser'
35
+
36
+
37
+ module TMail
38
+
39
+ class Address
40
+
41
+ include TextUtils
42
+
43
+ def Address.parse( str )
44
+ Parser.parse :ADDRESS, str
45
+ end
46
+
47
+ def address_group?
48
+ false
49
+ end
50
+
51
+ def initialize( local, domain )
52
+ if domain
53
+ domain.each do |s|
54
+ raise SyntaxError, 'empty word in domain' if s.empty?
55
+ end
56
+ end
57
+
58
+ @local = local
59
+ @domain = domain
60
+ @name = nil
61
+ @routes = []
62
+ end
63
+
64
+ attr_reader :name
65
+
66
+ def name=( str )
67
+ @name = str
68
+ @name = nil if str and str.empty?
69
+ end
70
+
71
+ alias phrase name
72
+ alias phrase= name=
73
+
74
+ attr_reader :routes
75
+
76
+ def inspect
77
+ "#<#{self.class} #{address()}>"
78
+ end
79
+
80
+ def local
81
+ return nil unless @local
82
+ return '""' if @local.size == 1 and @local[0].empty?
83
+ @local.map {|i| quote_atom(i) }.join('.')
84
+ end
85
+
86
+ def domain
87
+ return nil unless @domain
88
+ join_domain(@domain)
89
+ end
90
+
91
+ def spec
92
+ s = self.local
93
+ d = self.domain
94
+ if s and d
95
+ s + '@' + d
96
+ else
97
+ s
98
+ end
99
+ end
100
+
101
+ alias address spec
102
+
103
+ def ==( other )
104
+ other.respond_to? :spec and self.spec == other.spec
105
+ end
106
+
107
+ alias eql? ==
108
+
109
+ def hash
110
+ @local.hash ^ @domain.hash
111
+ end
112
+
113
+ def dup
114
+ obj = self.class.new(@local.dup, @domain.dup)
115
+ obj.name = @name.dup if @name
116
+ obj.routes.replace @routes
117
+ obj
118
+ end
119
+
120
+ include StrategyInterface
121
+
122
+ def accept( strategy, dummy1 = nil, dummy2 = nil )
123
+ unless @local
124
+ strategy.meta '<>' # empty return-path
125
+ return
126
+ end
127
+
128
+ spec_p = (not @name and @routes.empty?)
129
+ if @name
130
+ strategy.phrase @name
131
+ strategy.space
132
+ end
133
+ tmp = spec_p ? '' : '<'
134
+ unless @routes.empty?
135
+ tmp << @routes.map {|i| '@' + i }.join(',') << ':'
136
+ end
137
+ tmp << self.spec
138
+ tmp << '>' unless spec_p
139
+ strategy.meta tmp
140
+ strategy.lwsp ''
141
+ end
142
+
143
+ end
144
+
145
+
146
+ class AddressGroup
147
+
148
+ include Enumerable
149
+
150
+ def address_group?
151
+ true
152
+ end
153
+
154
+ def initialize( name, addrs )
155
+ @name = name
156
+ @addresses = addrs
157
+ end
158
+
159
+ attr_reader :name
160
+
161
+ def ==( other )
162
+ other.respond_to? :to_a and @addresses == other.to_a
163
+ end
164
+
165
+ alias eql? ==
166
+
167
+ def hash
168
+ map {|i| i.hash }.hash
169
+ end
170
+
171
+ def []( idx )
172
+ @addresses[idx]
173
+ end
174
+
175
+ def size
176
+ @addresses.size
177
+ end
178
+
179
+ def empty?
180
+ @addresses.empty?
181
+ end
182
+
183
+ def each( &block )
184
+ @addresses.each(&block)
185
+ end
186
+
187
+ def to_a
188
+ @addresses.dup
189
+ end
190
+
191
+ alias to_ary to_a
192
+
193
+ def include?( a )
194
+ @addresses.include? a
195
+ end
196
+
197
+ def flatten
198
+ set = []
199
+ @addresses.each do |a|
200
+ if a.respond_to? :flatten
201
+ set.concat a.flatten
202
+ else
203
+ set.push a
204
+ end
205
+ end
206
+ set
207
+ end
208
+
209
+ def each_address( &block )
210
+ flatten.each(&block)
211
+ end
212
+
213
+ def add( a )
214
+ @addresses.push a
215
+ end
216
+
217
+ alias push add
218
+
219
+ def delete( a )
220
+ @addresses.delete a
221
+ end
222
+
223
+ include StrategyInterface
224
+
225
+ def accept( strategy, dummy1 = nil, dummy2 = nil )
226
+ strategy.phrase @name
227
+ strategy.meta ':'
228
+ strategy.space
229
+ first = true
230
+ each do |mbox|
231
+ if first
232
+ first = false
233
+ else
234
+ strategy.meta ','
235
+ end
236
+ strategy.space
237
+ mbox.accept strategy
238
+ end
239
+ strategy.meta ';'
240
+ strategy.lwsp ''
241
+ end
242
+
243
+ end
244
+
245
+ end # module TMail
@@ -0,0 +1,47 @@
1
+ =begin rdoc
2
+
3
+ = Attachment handling class
4
+
5
+ =end
6
+
7
+ require 'stringio'
8
+
9
+ module TMail
10
+ class Attachment < StringIO
11
+ attr_accessor :original_filename, :content_type
12
+ end
13
+
14
+ class Mail
15
+ def has_attachments?
16
+ multipart? && parts.any? { |part| attachment?(part) }
17
+ end
18
+
19
+ def attachment?(part)
20
+ (part['content-disposition'] && part['content-disposition'].disposition == "attachment") ||
21
+ part.header['content-type'].main_type != "text"
22
+ end
23
+
24
+ def attachments
25
+ if multipart?
26
+ parts.collect { |part|
27
+ if part.multipart?
28
+ part.attachments
29
+ elsif attachment?(part)
30
+ content = part.body # unquoted automatically by TMail#body
31
+ file_name = (part['content-location'] &&
32
+ part['content-location'].body) ||
33
+ part.sub_header("content-type", "name") ||
34
+ part.sub_header("content-disposition", "filename")
35
+
36
+ next if file_name.blank? || content.blank?
37
+
38
+ attachment = Attachment.new(content)
39
+ attachment.original_filename = file_name.strip
40
+ attachment.content_type = part.content_type
41
+ attachment
42
+ end
43
+ }.flatten.compact
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,75 @@
1
+ =begin rdoc
2
+
3
+ = Base64 handling class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ require 'tmail/require_arch'
33
+
34
+ module TMail
35
+
36
+ module Base64
37
+
38
+ module_function
39
+
40
+ def rb_folding_encode( str, eol = "\n", limit = 60 )
41
+ [str].pack('m')
42
+ end
43
+
44
+ def rb_encode( str )
45
+ [str].pack('m').tr( "\r\n", '' )
46
+ end
47
+
48
+ def rb_decode( str, strict = false )
49
+ str.unpack('m').first
50
+ end
51
+
52
+ begin
53
+ require_arch('base64') #require 'tmail/base64.so'
54
+ alias folding_encode c_folding_encode
55
+ alias encode c_encode
56
+ alias decode c_decode
57
+ class << self
58
+ alias folding_encode c_folding_encode
59
+ alias encode c_encode
60
+ alias decode c_decode
61
+ end
62
+ rescue LoadError
63
+ alias folding_encode rb_folding_encode
64
+ alias encode rb_encode
65
+ alias decode rb_decode
66
+ class << self
67
+ alias folding_encode rb_folding_encode
68
+ alias encode rb_encode
69
+ alias decode rb_decode
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ 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,71 @@
1
+ =begin rdoc
2
+
3
+ = Configuration Class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ module TMail
33
+
34
+ class Config
35
+
36
+ def initialize( strict )
37
+ @strict_parse = strict
38
+ @strict_base64decode = strict
39
+ end
40
+
41
+ def strict_parse?
42
+ @strict_parse
43
+ end
44
+
45
+ attr_writer :strict_parse
46
+
47
+ def strict_base64decode?
48
+ @strict_base64decode
49
+ end
50
+
51
+ attr_writer :strict_base64decode
52
+
53
+ def new_body_port( mail )
54
+ StringPort.new
55
+ end
56
+
57
+ alias new_preamble_port new_body_port
58
+ alias new_part_port new_body_port
59
+
60
+ end
61
+
62
+ DEFAULT_CONFIG = Config.new(false)
63
+ DEFAULT_STRICT_CONFIG = Config.new(true)
64
+
65
+ def Config.to_config( arg )
66
+ return DEFAULT_STRICT_CONFIG if arg == true
67
+ return DEFAULT_CONFIG if arg == false
68
+ arg or DEFAULT_CONFIG
69
+ end
70
+
71
+ end