tmail 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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