rmail 0.17

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 (91) hide show
  1. data/NEWS +309 -0
  2. data/NOTES +14 -0
  3. data/README +83 -0
  4. data/THANKS +25 -0
  5. data/TODO +112 -0
  6. data/guide/Intro.txt +122 -0
  7. data/guide/MIME.txt +6 -0
  8. data/guide/TableOfContents.txt +13 -0
  9. data/install.rb +1023 -0
  10. data/lib/rmail.rb +50 -0
  11. data/lib/rmail/address.rb +829 -0
  12. data/lib/rmail/header.rb +987 -0
  13. data/lib/rmail/mailbox.rb +62 -0
  14. data/lib/rmail/mailbox/mboxreader.rb +182 -0
  15. data/lib/rmail/message.rb +201 -0
  16. data/lib/rmail/parser.rb +412 -0
  17. data/lib/rmail/parser/multipart.rb +217 -0
  18. data/lib/rmail/parser/pushbackreader.rb +173 -0
  19. data/lib/rmail/serialize.rb +190 -0
  20. data/lib/rmail/utils.rb +59 -0
  21. data/rmail.gemspec +17 -0
  22. data/tests/addrgrammar.txt +113 -0
  23. data/tests/data/mbox.odd +4 -0
  24. data/tests/data/mbox.simple +8 -0
  25. data/tests/data/multipart/data.1 +5 -0
  26. data/tests/data/multipart/data.10 +1 -0
  27. data/tests/data/multipart/data.11 +9 -0
  28. data/tests/data/multipart/data.12 +9 -0
  29. data/tests/data/multipart/data.13 +3 -0
  30. data/tests/data/multipart/data.14 +3 -0
  31. data/tests/data/multipart/data.15 +3 -0
  32. data/tests/data/multipart/data.16 +3 -0
  33. data/tests/data/multipart/data.17 +0 -0
  34. data/tests/data/multipart/data.2 +5 -0
  35. data/tests/data/multipart/data.3 +2 -0
  36. data/tests/data/multipart/data.4 +3 -0
  37. data/tests/data/multipart/data.5 +1 -0
  38. data/tests/data/multipart/data.6 +2 -0
  39. data/tests/data/multipart/data.7 +3 -0
  40. data/tests/data/multipart/data.8 +5 -0
  41. data/tests/data/multipart/data.9 +4 -0
  42. data/tests/data/parser.badmime1 +4 -0
  43. data/tests/data/parser.badmime2 +6 -0
  44. data/tests/data/parser.nested-multipart +75 -0
  45. data/tests/data/parser.nested-simple +12 -0
  46. data/tests/data/parser.nested-simple2 +16 -0
  47. data/tests/data/parser.nested-simple3 +21 -0
  48. data/tests/data/parser.rfc822 +65 -0
  49. data/tests/data/parser.simple-mime +24 -0
  50. data/tests/data/parser/multipart.1 +8 -0
  51. data/tests/data/parser/multipart.10 +4 -0
  52. data/tests/data/parser/multipart.11 +12 -0
  53. data/tests/data/parser/multipart.12 +12 -0
  54. data/tests/data/parser/multipart.13 +6 -0
  55. data/tests/data/parser/multipart.14 +6 -0
  56. data/tests/data/parser/multipart.15 +6 -0
  57. data/tests/data/parser/multipart.16 +6 -0
  58. data/tests/data/parser/multipart.2 +8 -0
  59. data/tests/data/parser/multipart.3 +5 -0
  60. data/tests/data/parser/multipart.4 +6 -0
  61. data/tests/data/parser/multipart.5 +4 -0
  62. data/tests/data/parser/multipart.6 +5 -0
  63. data/tests/data/parser/multipart.7 +6 -0
  64. data/tests/data/parser/multipart.8 +8 -0
  65. data/tests/data/parser/multipart.9 +7 -0
  66. data/tests/data/transparency/absolute.1 +5 -0
  67. data/tests/data/transparency/absolute.2 +1 -0
  68. data/tests/data/transparency/absolute.3 +2 -0
  69. data/tests/data/transparency/absolute.4 +3 -0
  70. data/tests/data/transparency/absolute.5 +4 -0
  71. data/tests/data/transparency/absolute.6 +49 -0
  72. data/tests/data/transparency/message.1 +73 -0
  73. data/tests/data/transparency/message.2 +34 -0
  74. data/tests/data/transparency/message.3 +63 -0
  75. data/tests/data/transparency/message.4 +5 -0
  76. data/tests/data/transparency/message.5 +15 -0
  77. data/tests/data/transparency/message.6 +1185 -0
  78. data/tests/runtests.rb +35 -0
  79. data/tests/testaddress.rb +1192 -0
  80. data/tests/testbase.rb +207 -0
  81. data/tests/testheader.rb +1207 -0
  82. data/tests/testmailbox.rb +47 -0
  83. data/tests/testmboxreader.rb +161 -0
  84. data/tests/testmessage.rb +257 -0
  85. data/tests/testparser.rb +634 -0
  86. data/tests/testparsermultipart.rb +205 -0
  87. data/tests/testpushbackreader.rb +40 -0
  88. data/tests/testserialize.rb +264 -0
  89. data/tests/testtestbase.rb +112 -0
  90. data/tests/testtranspparency.rb +105 -0
  91. metadata +143 -0
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ # 3. The name of the author may not be used to endorse or promote products
14
+ # derived from this software without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19
+ # NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #
27
+
28
+ fail "must run this script directly" unless __FILE__ == $0
29
+ path = File.expand_path(File.join(File.dirname($0), '..', 'lib'))
30
+ puts "Prepending #{path} to the $LOAD_PATH"
31
+ $LOAD_PATH.unshift(path) # get our stuff first
32
+
33
+ Dir['tests/test*.rb'].each {|f|
34
+ require f
35
+ }
@@ -0,0 +1,1192 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright (C) 2001, 2002, 2003 Matt Armstrong. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ # 3. The name of the author may not be used to endorse or promote products
14
+ # derived from this software without specific prior written permission.
15
+ #
16
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19
+ # NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+ #
27
+
28
+ require 'tests/testbase'
29
+ require 'rmail/address'
30
+
31
+ class TestRMailAddress < TestBase
32
+
33
+ def domain_optional
34
+ # Set to true for tests that include addresses without a domain
35
+ # portion.
36
+ false
37
+ end
38
+
39
+ def method_list
40
+ [:display_name, :name, :address, :comments,
41
+ :format, :domain, :local]
42
+ end
43
+
44
+ def validate_method(object, method, *args)
45
+ assert(method_list.include?(method),
46
+ "#{method.inspect} not in #{method_list.inspect}")
47
+ assert_respond_to(method, object)
48
+ ret = nil
49
+ ret = object.send(method, *args)
50
+ if block_given?
51
+ yield(ret)
52
+ end
53
+ end
54
+
55
+ def validate_interface(address)
56
+ assert_instance_of(RMail::Address, address)
57
+
58
+ validate_method(address, :display_name) { |ret|
59
+ assert_instance_of(String, ret) unless ret.nil?
60
+ }
61
+
62
+ validate_method(address, :name) { |ret|
63
+ assert_instance_of(String, ret) unless ret.nil?
64
+ }
65
+
66
+ validate_method(address, :address) { |ret|
67
+ assert_instance_of(String, ret) unless ret.nil?
68
+ }
69
+
70
+ validate_method(address, :comments) { |ret|
71
+ unless ret.nil?
72
+ assert_instance_of(Array, ret)
73
+ ret.each { |comment|
74
+ assert_instance_of(String, comment)
75
+ }
76
+ end
77
+ }
78
+
79
+ validate_method(address, :format) { |ret|
80
+ assert_instance_of(String, ret) unless ret.nil?
81
+ }
82
+
83
+ validate_method(address, :domain) { |ret|
84
+ assert_instance_of(String, ret) unless ret.nil?
85
+ }
86
+
87
+ validate_method(address, :local) { |ret|
88
+ assert_instance_of(String, ret) unless ret.nil?
89
+ }
90
+ end
91
+
92
+ def validate_case(testcase, debug = false)
93
+ begin
94
+ prev_debug = $DEBUG
95
+ $DEBUG = debug
96
+ results = RMail::Address.parse(testcase[0])
97
+ ensure
98
+ $DEBUG = prev_debug
99
+ end
100
+ assert_kind_of(Array, results)
101
+ assert_kind_of(RMail::Address::List, results)
102
+ results.each { |address|
103
+ validate_interface(address)
104
+ }
105
+ expected_results = testcase[1]
106
+ assert_instance_of(Array, expected_results)
107
+ assert_equal(expected_results.length, results.length,
108
+ "results array wrong length, got #{results.inspect}")
109
+
110
+ results.each_with_index { |address, i|
111
+ assert_instance_of(Hash, expected_results[i])
112
+ methods = method_list
113
+ expected_results[i].each { |method, expected|
114
+ validate_method(address, method) { |ret|
115
+ assert_equal(expected, ret,
116
+ "\nstring #{testcase[0].inspect}\naddr #{address.inspect}\nmethod #{method.inspect}\n")
117
+ }
118
+ methods.delete(method)
119
+ }
120
+ assert_equal(0, methods.length,
121
+ "test case did not test these methods #{methods.inspect}")
122
+ }
123
+ end
124
+
125
+ def test_rfc_2822()
126
+
127
+ # The following are from RFC2822
128
+ validate_case\
129
+ ['John Doe <jdoe@machine.example>',
130
+ [ { :name => 'John Doe',
131
+ :display_name => 'John Doe',
132
+ :address => 'jdoe@machine.example',
133
+ :comments => nil,
134
+ :domain => 'machine.example',
135
+ :local => 'jdoe',
136
+ :format => 'John Doe <jdoe@machine.example>' } ]]
137
+
138
+ validate_case\
139
+ [' Mary Smith <mary@example.net>',
140
+ [ { :name => 'Mary Smith',
141
+ :display_name => 'Mary Smith',
142
+ :address => 'mary@example.net',
143
+ :comments => nil,
144
+ :domain => 'example.net',
145
+ :local => 'mary',
146
+ :format => 'Mary Smith <mary@example.net>' } ]]
147
+
148
+ validate_case\
149
+ ['"Joe Q. Public" <john.q.public@example.com>',
150
+ [ { :name => 'Joe Q. Public',
151
+ :display_name => 'Joe Q. Public',
152
+ :address => 'john.q.public@example.com',
153
+ :comments => nil,
154
+ :domain => 'example.com',
155
+ :local => 'john.q.public',
156
+ :format => '"Joe Q. Public" <john.q.public@example.com>' } ]]
157
+
158
+ validate_case\
159
+ ['Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>',
160
+ [ { :name => 'Mary Smith',
161
+ :display_name => 'Mary Smith',
162
+ :address => 'mary@x.test',
163
+ :comments => nil,
164
+ :domain => 'x.test',
165
+ :local => 'mary',
166
+ :format => 'Mary Smith <mary@x.test>' },
167
+ { :name => nil,
168
+ :display_name => nil,
169
+ :address => 'jdoe@example.org',
170
+ :comments => nil,
171
+ :domain => 'example.org',
172
+ :local => 'jdoe',
173
+ :format => 'jdoe@example.org' },
174
+ { :name => 'Who?',
175
+ :display_name => 'Who?',
176
+ :address => 'one@y.test',
177
+ :comments => nil,
178
+ :domain => 'y.test',
179
+ :local => 'one',
180
+ :format => 'Who? <one@y.test>' } ]]
181
+
182
+ validate_case\
183
+ ['<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>',
184
+ [ { :name => nil,
185
+ :display_name => nil,
186
+ :address => 'boss@nil.test',
187
+ :comments => nil,
188
+ :domain => 'nil.test',
189
+ :local => 'boss',
190
+ :format => 'boss@nil.test' },
191
+ { :name => 'Giant; "Big" Box',
192
+ :display_name => 'Giant; "Big" Box',
193
+ :address => 'sysservices@example.net',
194
+ :comments => nil,
195
+ :domain => 'example.net',
196
+ :local => 'sysservices',
197
+ :format => '"Giant; \"Big\" Box" <sysservices@example.net>' }
198
+ ] ]
199
+
200
+ validate_case\
201
+ ['A Group:Chris Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;',
202
+ [ { :name => 'Chris Jones',
203
+ :display_name => 'Chris Jones',
204
+ :address => 'c@a.test',
205
+ :comments => nil,
206
+ :domain => 'a.test',
207
+ :local => 'c',
208
+ :format => 'Chris Jones <c@a.test>' },
209
+ { :name => nil,
210
+ :display_name => nil,
211
+ :address => 'joe@where.test',
212
+ :comments => nil,
213
+ :domain => 'where.test',
214
+ :local => 'joe',
215
+ :format => 'joe@where.test' },
216
+ { :name => 'John',
217
+ :display_name => 'John',
218
+ :address => 'jdoe@one.test',
219
+ :comments => nil,
220
+ :domain => 'one.test',
221
+ :local => 'jdoe',
222
+ :format => 'John <jdoe@one.test>' }
223
+ ] ]
224
+
225
+ validate_case\
226
+ ['Undisclosed recipients:;',
227
+ [] ]
228
+
229
+ validate_case\
230
+ ['undisclosed recipients: ;',
231
+ [] ]
232
+
233
+ validate_case\
234
+ ['"Mary Smith: Personal Account" <smith@home.example> ',
235
+ [ { :name => 'Mary Smith: Personal Account',
236
+ :display_name => 'Mary Smith: Personal Account',
237
+ :address => 'smith@home.example',
238
+ :comments => nil,
239
+ :domain => 'home.example',
240
+ :local => 'smith',
241
+ :format => '"Mary Smith: Personal Account" <smith@home.example>' }
242
+ ] ]
243
+
244
+ validate_case\
245
+ ['Pete(A wonderful \) chap) <pete(his account)@silly.test(his host)>',
246
+ [ { :name => 'Pete',
247
+ :display_name => 'Pete',
248
+ :address => 'pete@silly.test',
249
+ :comments => ['A wonderful ) chap', 'his account', 'his host'],
250
+ :domain => 'silly.test',
251
+ :local => 'pete',
252
+ :format => 'Pete <pete@silly.test> (A wonderful \) chap) (his account) (his host)' }
253
+ ] ]
254
+
255
+ validate_case\
256
+ ['A Group:a@b.c,d@e.f;',
257
+ [ { :name => nil, :display_name => nil, :comments => nil,
258
+ :local => 'a', :domain => 'b.c', :format => 'a@b.c',
259
+ :address => 'a@b.c' },
260
+ { :name => nil, :display_name => nil, :comments => nil,
261
+ :local => 'd', :domain => 'e.f', :format => 'd@e.f',
262
+ :address => 'd@e.f' } ] ]
263
+
264
+ validate_case\
265
+ ["A Group(Some people)\r\n :Chris Jones <c@(Chris's host.)public.example>,\r\n joe@example.org",
266
+ [ { :name => 'Chris Jones',
267
+ :display_name => 'Chris Jones',
268
+ :address => 'c@public.example',
269
+ :comments => ['Chris\'s host.'],
270
+ :domain => 'public.example',
271
+ :local => 'c',
272
+ :format => 'Chris Jones <c@public.example> (Chris\'s host.)' } ] ]
273
+
274
+ validate_case\
275
+ ["A Group(Some people)\r\n :Chris Jones <c@(Chris's host.)public.example>,\r\n joe@example.org;",
276
+ [ { :name => 'Chris Jones',
277
+ :display_name => 'Chris Jones',
278
+ :address => 'c@public.example',
279
+ :comments => ['Chris\'s host.'],
280
+ :domain => 'public.example',
281
+ :local => 'c',
282
+ :format => 'Chris Jones <c@public.example> (Chris\'s host.)' },
283
+ { :name => nil,
284
+ :display_name => nil,
285
+ :address => 'joe@example.org',
286
+ :comments => nil,
287
+ :domain => 'example.org',
288
+ :local => 'joe',
289
+ :format => 'joe@example.org' }
290
+ ] ]
291
+
292
+ validate_case\
293
+ ['(Empty list)(start)Undisclosed recipients :(nobody(that I know)) ;',
294
+ [] ]
295
+
296
+ # Note, the space is lost after the Q. because we always convert
297
+ # word . word into "word.word" for output. To preserve the space,
298
+ # the guy should quote his name.
299
+ # FIXME: maybe fix this behavior?
300
+ validate_case\
301
+ ['Joe Q. Public <john.q.public@example.com>',
302
+ [ { :name => 'Joe Q.Public',
303
+ :display_name => 'Joe Q.Public',
304
+ :address => 'john.q.public@example.com',
305
+ :comments => nil,
306
+ :domain => 'example.com',
307
+ :local => 'john.q.public',
308
+ :format => '"Joe Q.Public" <john.q.public@example.com>' } ] ]
309
+
310
+ # FIXME: maybe expect "B.J.Major" <bjbear71@example.net>
311
+ validate_case\
312
+ ['B.J. Major <bjbear71@example.net>',
313
+ [ { :name => 'B.J.Major',
314
+ :display_name => 'B.J.Major',
315
+ :address => 'bjbear71@example.net',
316
+ :comments => nil,
317
+ :domain => 'example.net',
318
+ :local => 'bjbear71',
319
+ :format => '"B.J.Major" <bjbear71@example.net>' } ] ]
320
+
321
+ validate_case\
322
+ ['jdoe@test . example',
323
+ [ { :name => nil,
324
+ :display_name => nil,
325
+ :address => 'jdoe@test.example',
326
+ :comments => nil,
327
+ :domain => 'test.example',
328
+ :local => 'jdoe',
329
+ :format => 'jdoe@test.example' } ] ]
330
+
331
+ validate_case\
332
+ ['Mary Smith <@machine.tld:mary@example.net>, , jdoe@test . example',
333
+ [ { :name => 'Mary Smith',
334
+ :display_name => 'Mary Smith',
335
+ :address => 'mary@example.net',
336
+ :comments => nil,
337
+ :domain => 'example.net',
338
+ :local => 'mary',
339
+ :format => 'Mary Smith <mary@example.net>' },
340
+ { :name => nil,
341
+ :display_name => nil,
342
+ :address => 'jdoe@test.example',
343
+ :comments => nil,
344
+ :domain => 'test.example',
345
+ :local => 'jdoe',
346
+ :format => 'jdoe@test.example' } ] ]
347
+
348
+ validate_case\
349
+ ['John Doe <jdoe@machine(comment). example>',
350
+ [ { :name => 'John Doe',
351
+ :display_name => 'John Doe',
352
+ :address => 'jdoe@machine.example',
353
+ :comments => ['comment'],
354
+ :domain => 'machine.example',
355
+ :local => 'jdoe',
356
+ :format => 'John Doe <jdoe@machine.example> (comment)' } ] ]
357
+
358
+ validate_case\
359
+ ["Mary Smith\n\r \n <mary@example.net>",
360
+ [ { :name => 'Mary Smith',
361
+ :display_name => 'Mary Smith',
362
+ :address => 'mary@example.net',
363
+ :comments => nil,
364
+ :domain => 'example.net',
365
+ :local => 'mary',
366
+ :format => 'Mary Smith <mary@example.net>' } ] ]
367
+ end
368
+
369
+ # Tests stolen from majordomoII
370
+ def test_majordomoII
371
+ validate_case\
372
+ ["tibbs@uh.edu (Homey ( j (\(\() t ) Tibbs)",
373
+ [ { :name => 'Homey ( j ((() t ) Tibbs',
374
+ :display_name => nil,
375
+ :address => 'tibbs@uh.edu',
376
+ :domain => 'uh.edu',
377
+ :local => 'tibbs',
378
+ :comments => [ 'Homey ( j ((() t ) Tibbs' ],
379
+ :format => 'tibbs@uh.edu (Homey \( j \(\(\(\) t \) Tibbs)' } ] ]
380
+
381
+ validate_case\
382
+ ['"tibbs@home"@hpc.uh.edu (JLT )',
383
+ [ { :name => 'JLT ',
384
+ :address => 'tibbs@home@hpc.uh.edu',
385
+ :display_name => nil,
386
+ :domain => 'hpc.uh.edu',
387
+ :local => 'tibbs@home',
388
+ :comments => [ 'JLT ' ],
389
+ :format => '"tibbs@home"@hpc.uh.edu (JLT )' } ] ]
390
+
391
+ validate_case\
392
+ ['tibbs@[129.7.3.5]',
393
+ [ { :name => nil,
394
+ :display_name => nil,
395
+ :domain => '[129.7.3.5]',
396
+ :local => 'tibbs',
397
+ :comments => nil,
398
+ :address => 'tibbs@[129.7.3.5]',
399
+ :format => '<tibbs@[129.7.3.5]>' } ] ]
400
+
401
+ validate_case\
402
+ ['A_Foriegner%across.the.pond@relay1.uu.net',
403
+ [ { :name => nil,
404
+ :display_name => nil,
405
+ :domain => 'relay1.uu.net',
406
+ :local => 'A_Foriegner%across.the.pond',
407
+ :comments => nil,
408
+ :address => 'A_Foriegner%across.the.pond@relay1.uu.net',
409
+ :format => 'A_Foriegner%across.the.pond@relay1.uu.net' } ] ]
410
+
411
+ validate_case\
412
+ ['"Jason @ Tibbitts" <tibbs@uh.edu>',
413
+ [ { :name => 'Jason @ Tibbitts',
414
+ :display_name => 'Jason @ Tibbitts',
415
+ :domain => 'uh.edu',
416
+ :local => 'tibbs',
417
+ :comments => nil,
418
+ :address => 'tibbs@uh.edu',
419
+ :format => '"Jason @ Tibbitts" <tibbs@uh.edu>' } ] ]
420
+
421
+ # Majordomo II parses all of these with an error. We have deleted
422
+ # some of the tests since when they are actually legal.
423
+ validate_case ['tibbs@uh.edu Jason Tibbitts', [] ]
424
+ validate_case ['@uh.edu', [] ] # Can't start with @
425
+ validate_case ['J <tibbs>', [] ] # Not FQDN
426
+ validate_case ['<tibbs Da Man', [] ] # Unbalanced
427
+ validate_case ['Jason <tibbs>>', [] ] # Unbalanced
428
+ validate_case ['tibbs, nobody', [] ] # Multiple addresses not allowed
429
+ validate_case ['tibbs@.hpc', [] ] # Illegal @.
430
+ validate_case ['<a@b>@c', [] ] # @ illegal in phrase
431
+ validate_case ['<a@>', [] ] # No hostname
432
+ validate_case ['<a@b>.abc', [] ] # >. illegal
433
+ validate_case ['<a@b> blah <d@e>', [] ] # Two routes illegal
434
+ validate_case ['<tibbs<tib@a>@hpc.uh.edu>', [] ] # Nested routes illegal
435
+ validate_case ['[<tibbs@hpc.uh.edu>]', [] ] # Enclosed in []
436
+ validate_case ['<a@b.cd> Me [blurfl] U', [] ] # Domain literals illegal in comment
437
+ validate_case ['A B @ C <a@b.c>', [] ] # @ not legal in comment
438
+ validate_case ['blah . tibbs@', [] ] # Unquoted . not legal in comment
439
+ validate_case ['tibbs@hpc.uh.edu.', [] ] # Address ends with a dot
440
+ validate_case ['sina.hpc.uh.edu', [] ] # No local-part@
441
+ validate_case ['tibbs@@math.uh.edu', [] ] # Two @s next to each other
442
+ validate_case ['tibbs@math@uh.edu', [] ] # Two @s, not next to each other
443
+ end
444
+
445
+ def test_mailtools_suite()
446
+
447
+ #
448
+ # The following are from the Perl MailTools module version 1.40
449
+ #
450
+ validate_case\
451
+ ['"Joe & J. Harvey" <ddd @Org>, JJV @ BBN',
452
+ [ { :name => 'Joe & J. Harvey',
453
+ :display_name => 'Joe & J. Harvey',
454
+ :address => 'ddd@Org',
455
+ :comments => nil,
456
+ :domain => 'Org',
457
+ :local => 'ddd',
458
+ :format => '"Joe & J. Harvey" <ddd@Org>' },
459
+ { :name => nil,
460
+ :display_name => nil,
461
+ :address => 'JJV@BBN',
462
+ :comments => nil,
463
+ :domain => 'BBN',
464
+ :local => 'JJV',
465
+ :format => 'JJV@BBN' } ] ]
466
+
467
+ validate_case\
468
+ ['"spickett@tiac.net" <Sean.Pickett@zork.tiac.net>',
469
+ [ { :name => 'spickett@tiac.net',
470
+ :display_name => 'spickett@tiac.net',
471
+ :address => 'Sean.Pickett@zork.tiac.net',
472
+ :comments => nil,
473
+ :domain => 'zork.tiac.net',
474
+ :local => 'Sean.Pickett',
475
+ :format => '"spickett@tiac.net" <Sean.Pickett@zork.tiac.net>' } ] ]
476
+
477
+ validate_case\
478
+ ['rls@intgp8.ih.att.com (-Schieve,R.L.)',
479
+ [ { :name => '-Schieve,R.L.',
480
+ :display_name => nil,
481
+ :address => 'rls@intgp8.ih.att.com',
482
+ :comments => ['-Schieve,R.L.'],
483
+ :domain => 'intgp8.ih.att.com',
484
+ :local => 'rls',
485
+ :format => 'rls@intgp8.ih.att.com (-Schieve,R.L.)' } ] ]
486
+
487
+ validate_case\
488
+ ['jrh%cup.portal.com@portal.unix.portal.com',
489
+ [ { :name => nil,
490
+ :display_name => nil,
491
+ :address => 'jrh%cup.portal.com@portal.unix.portal.com',
492
+ :comments => nil,
493
+ :domain => 'portal.unix.portal.com',
494
+ :local => 'jrh%cup.portal.com',
495
+ :format => 'jrh%cup.portal.com@portal.unix.portal.com' } ] ]
496
+
497
+ validate_case\
498
+ ['astrachan@austlcm.sps.mot.com (\'paul astrachan/xvt3\')',
499
+ [ { :name => '\'paul astrachan/xvt3\'',
500
+ :display_name => nil,
501
+ :address => 'astrachan@austlcm.sps.mot.com',
502
+ :comments => ['\'paul astrachan/xvt3\''],
503
+ :domain => 'austlcm.sps.mot.com',
504
+ :local => 'astrachan',
505
+ :format =>
506
+ 'astrachan@austlcm.sps.mot.com (\'paul astrachan/xvt3\')' } ] ]
507
+
508
+ validate_case\
509
+ ['TWINE57%SDELVB.decnet@SNYBUF.CS.SNYBUF.EDU (JAMES R. TWINE - THE NERD)',
510
+ [ { :name => 'JAMES R. TWINE - THE NERD',
511
+ :display_name => nil,
512
+ :address => 'TWINE57%SDELVB.decnet@SNYBUF.CS.SNYBUF.EDU',
513
+ :comments => ['JAMES R. TWINE - THE NERD'],
514
+ :domain => 'SNYBUF.CS.SNYBUF.EDU',
515
+ :local => 'TWINE57%SDELVB.decnet',
516
+ :format =>
517
+ 'TWINE57%SDELVB.decnet@SNYBUF.CS.SNYBUF.EDU (JAMES R. TWINE - THE NERD)'} ] ]
518
+
519
+ validate_case\
520
+ ['David Apfelbaum <da0g+@andrew.cmu.edu>',
521
+ [ { :name => 'David Apfelbaum',
522
+ :display_name => 'David Apfelbaum',
523
+ :address => 'da0g+@andrew.cmu.edu',
524
+ :comments => nil,
525
+ :domain => 'andrew.cmu.edu',
526
+ :local => 'da0g+',
527
+ :format => 'David Apfelbaum <da0g+@andrew.cmu.edu>' } ] ]
528
+
529
+ validate_case\
530
+ ['"JAMES R. TWINE - THE NERD" <TWINE57%SDELVB%SNYDELVA.bitnet@CUNYVM.CUNY.EDU>',
531
+ [ { :name => 'JAMES R. TWINE - THE NERD',
532
+ :display_name => 'JAMES R. TWINE - THE NERD',
533
+ :address => 'TWINE57%SDELVB%SNYDELVA.bitnet@CUNYVM.CUNY.EDU',
534
+ :comments => nil,
535
+ :domain => 'CUNYVM.CUNY.EDU',
536
+ :local => 'TWINE57%SDELVB%SNYDELVA.bitnet',
537
+ :format => '"JAMES R. TWINE - THE NERD" <TWINE57%SDELVB%SNYDELVA.bitnet@CUNYVM.CUNY.EDU>' } ] ]
538
+
539
+ validate_case\
540
+ ['/G=Owen/S=Smith/O=SJ-Research/ADMD=INTERSPAN/C=GB/@mhs-relay.ac.uk',
541
+ [ { :name => nil,
542
+ :display_name => nil,
543
+ :address => '/G=Owen/S=Smith/O=SJ-Research/ADMD=INTERSPAN/C=GB/@mhs-relay.ac.uk',
544
+ :comments => nil,
545
+ :domain => 'mhs-relay.ac.uk',
546
+ :local => '/G=Owen/S=Smith/O=SJ-Research/ADMD=INTERSPAN/C=GB/',
547
+ :format => '/G=Owen/S=Smith/O=SJ-Research/ADMD=INTERSPAN/C=GB/@mhs-relay.ac.uk' } ] ]
548
+
549
+ validate_case\
550
+ ['"Stephen Burke, Liverpool" <BURKE@vxdsya.desy.de>',
551
+ [ { :name => 'Stephen Burke, Liverpool',
552
+ :display_name => 'Stephen Burke, Liverpool',
553
+ :address => 'BURKE@vxdsya.desy.de',
554
+ :comments => nil,
555
+ :domain => 'vxdsya.desy.de',
556
+ :local => 'BURKE',
557
+ :format => '"Stephen Burke, Liverpool" <BURKE@vxdsya.desy.de>' } ] ]
558
+
559
+ validate_case\
560
+ ['The Newcastle Info-Server <info-admin@newcastle.ac.uk>',
561
+ [ { :name => 'The Newcastle Info-Server',
562
+ :display_name => 'The Newcastle Info-Server',
563
+ :address => 'info-admin@newcastle.ac.uk',
564
+ :comments => nil,
565
+ :domain => 'newcastle.ac.uk',
566
+ :local => 'info-admin',
567
+ :format => 'The Newcastle Info-Server <info-admin@newcastle.ac.uk>'
568
+ } ] ]
569
+
570
+ validate_case\
571
+ ['Suba.Peddada@eng.sun.com (Suba Peddada [CONTRACTOR])',
572
+ [ { :name => 'Suba Peddada [CONTRACTOR]',
573
+ :display_name => nil,
574
+ :address => 'Suba.Peddada@eng.sun.com',
575
+ :comments => ['Suba Peddada [CONTRACTOR]'],
576
+ :domain => 'eng.sun.com',
577
+ :local => 'Suba.Peddada',
578
+ :format => 'Suba.Peddada@eng.sun.com (Suba Peddada [CONTRACTOR])'
579
+ } ] ]
580
+
581
+ validate_case\
582
+ ['Paul Manser (0032 memo) <a906187@tiuk.ti.com>',
583
+ [ { :name => 'Paul Manser',
584
+ :display_name => 'Paul Manser',
585
+ :address => 'a906187@tiuk.ti.com',
586
+ :comments => ['0032 memo'],
587
+ :domain => 'tiuk.ti.com',
588
+ :local => 'a906187',
589
+ :format => 'Paul Manser <a906187@tiuk.ti.com> (0032 memo)' } ] ]
590
+
591
+ validate_case\
592
+ ['"gregg (g.) woodcock" <woodcock@bnr.ca>',
593
+ [ { :name => 'gregg (g.) woodcock',
594
+ :display_name => 'gregg (g.) woodcock',
595
+ :address => 'woodcock@bnr.ca',
596
+ :comments => nil,
597
+ :domain => 'bnr.ca',
598
+ :local => 'woodcock',
599
+ :format => '"gregg (g.) woodcock" <woodcock@bnr.ca>' } ] ]
600
+
601
+ validate_case\
602
+ ['Graham.Barr@tiuk.ti.com',
603
+ [ { :name => nil,
604
+ :display_name => nil,
605
+ :address => 'Graham.Barr@tiuk.ti.com',
606
+ :comments => nil,
607
+ :domain => 'tiuk.ti.com',
608
+ :local => 'Graham.Barr',
609
+ :format => 'Graham.Barr@tiuk.ti.com' } ] ]
610
+
611
+ if domain_optional
612
+ validate_case\
613
+ ['a909937 (Graham Barr (0004 bodg))',
614
+ [ { :name => 'Graham Barr (0004 bodg)',
615
+ :display_name => nil,
616
+ :address => 'a909937',
617
+ :comments => ['Graham Barr (0004 bodg)'],
618
+ :domain => nil,
619
+ :local => 'a909937',
620
+ :format => 'a909937 (Graham Barr \(0004 bodg\))' } ] ]
621
+ end
622
+
623
+ validate_case\
624
+ ['david d `zoo\' zuhn <zoo@aggregate.com>',
625
+ [ { :name => 'david d `zoo\' zuhn',
626
+ :display_name => 'david d `zoo\' zuhn',
627
+ :address => 'zoo@aggregate.com',
628
+ :comments => nil,
629
+ :domain => 'aggregate.com',
630
+ :local => 'zoo',
631
+ :format => 'david d `zoo\' zuhn <zoo@aggregate.com>' } ] ]
632
+
633
+ validate_case\
634
+ ['(foo@bar.com (foobar), ned@foo.com (nedfoo) ) <kevin@goess.org>',
635
+ [ { :name => 'foo@bar.com (foobar), ned@foo.com (nedfoo) ',
636
+ :display_name => nil,
637
+ :address => 'kevin@goess.org',
638
+ :comments => ['foo@bar.com (foobar), ned@foo.com (nedfoo) '],
639
+ :domain => 'goess.org',
640
+ :local => 'kevin',
641
+ :format =>
642
+ 'kevin@goess.org (foo@bar.com \(foobar\), ned@foo.com \(nedfoo\) )'
643
+ } ]]
644
+ end
645
+
646
+ def test_rfc_822
647
+
648
+ validate_case\
649
+ ['":sysmail"@ Some-Group. Some-Org, Muhammed.(I am the greatest) Ali @(the)Vegas.WBA',
650
+ [ { :name => nil,
651
+ :display_name => nil,
652
+ :address => ':sysmail@Some-Group.Some-Org',
653
+ :comments => nil,
654
+ :domain => 'Some-Group.Some-Org',
655
+ :local => ':sysmail',
656
+ :format => '":sysmail"@Some-Group.Some-Org' },
657
+ { :name => 'the',
658
+ :display_name => nil,
659
+ :address => 'Muhammed.Ali@Vegas.WBA',
660
+ :comments => ['I am the greatest', 'the'],
661
+ :domain => 'Vegas.WBA',
662
+ :local => 'Muhammed.Ali',
663
+ :format => 'Muhammed.Ali@Vegas.WBA (I am the greatest) (the)' } ] ]
664
+ end
665
+
666
+ def test_misc_addresses()
667
+
668
+ # Make sure that parsing empty stuff works
669
+ assert_equal([], RMail::Address.parse(nil))
670
+ assert_equal([], RMail::Address.parse(""))
671
+ assert_equal([], RMail::Address.parse(" "))
672
+ assert_equal([], RMail::Address.parse("\t"))
673
+ assert_equal([], RMail::Address.parse("\n"))
674
+ assert_equal([], RMail::Address.parse("\r"))
675
+
676
+ # From a bogus header I saw sent to ruby-talk
677
+ validate_case([' ruby-talk@ruby-lang.org (ruby-talk ML),
678
+ <ruby-talk ML <ruby-talk@ruby-lang.org>>',
679
+ [ { :name => 'ruby-talk ML',
680
+ :display_name => nil,
681
+ :comments => [ 'ruby-talk ML' ],
682
+ :domain => 'ruby-lang.org',
683
+ :local => 'ruby-talk',
684
+ :address => 'ruby-talk@ruby-lang.org',
685
+ :format => 'ruby-talk@ruby-lang.org (ruby-talk ML)' }
686
+ ] ])
687
+
688
+ # From Python address parsing bug list. This is valid according
689
+ # to RFC2822.
690
+ validate_case(['Amazon.com <delivers-news2@amazon.com>',
691
+ [ { :name => 'Amazon.com',
692
+ :display_name => 'Amazon.com',
693
+ :address => 'delivers-news2@amazon.com',
694
+ :comments => nil,
695
+ :domain => 'amazon.com',
696
+ :local => 'delivers-news2',
697
+ :format => '"Amazon.com" <delivers-news2@amazon.com>'
698
+ } ] ])
699
+
700
+ validate_case\
701
+ ["\r\n Amazon \r . \n com \t < delivers-news2@amazon.com > \n ",
702
+ [ { :name => 'Amazon.com',
703
+ :display_name => 'Amazon.com',
704
+ :address => 'delivers-news2@amazon.com',
705
+ :comments => nil,
706
+ :domain => 'amazon.com',
707
+ :local => 'delivers-news2',
708
+ :format => '"Amazon.com" <delivers-news2@amazon.com>'
709
+ } ] ]
710
+
711
+ # From postfix-users@postfix.org
712
+ # Date: Tue, 13 Nov 2001 10:58:23 -0800
713
+ # Subject: Apparent bug in strict_rfc821_envelopes (Snapshot-20010714)
714
+ validate_case\
715
+ ['"mailto:rfc"@monkeys.test',
716
+ [ { :name => nil,
717
+ :display_name => nil,
718
+ :address => 'mailto:rfc@monkeys.test',
719
+ :comments => nil,
720
+ :domain => 'monkeys.test',
721
+ :local => 'mailto:rfc',
722
+ :format => '"mailto:rfc"@monkeys.test' } ] ]
723
+
724
+ # An unquoted mailto:rfc will end up being detected as an invalid
725
+ # group display name.
726
+ validate_case ['mailto:rfc@monkeys.test', [] ]
727
+
728
+ # From gnu.emacs.help
729
+ # Date: 24 Nov 2001 15:37:23 -0500
730
+ validate_case\
731
+ ['"Stefan Monnier <foo@acm.com>" <monnier+gnu.emacs.help/news/@flint.cs.yale.edu>',
732
+ [ { :name => 'Stefan Monnier <foo@acm.com>',
733
+ :display_name => 'Stefan Monnier <foo@acm.com>',
734
+ :address => 'monnier+gnu.emacs.help/news/@flint.cs.yale.edu',
735
+ :comments => nil,
736
+ :domain => 'flint.cs.yale.edu',
737
+ :local => 'monnier+gnu.emacs.help/news/',
738
+ :format => '"Stefan Monnier <foo@acm.com>" <monnier+gnu.emacs.help/news/@flint.cs.yale.edu>' } ] ]
739
+
740
+ { :name => nil,
741
+ :display_name => nil,
742
+ :address => nil,
743
+ :comments => nil,
744
+ :domain => nil,
745
+ :local => nil,
746
+ :format => nil }
747
+
748
+ validate_case\
749
+ ['"foo:" . bar@somewhere.test',
750
+ [ { :name => nil,
751
+ :display_name => nil,
752
+ :address => 'foo:.bar@somewhere.test',
753
+ :comments => nil,
754
+ :domain => 'somewhere.test',
755
+ :local => 'foo:.bar',
756
+ :format => '"foo:.bar"@somewhere.test' } ] ]
757
+
758
+ validate_case\
759
+ ['Some Dude <"foo:" . bar@somewhere.test>',
760
+ [ { :name => 'Some Dude',
761
+ :display_name => 'Some Dude',
762
+ :address => 'foo:.bar@somewhere.test',
763
+ :comments => nil,
764
+ :domain => 'somewhere.test',
765
+ :local => 'foo:.bar',
766
+ :format => 'Some Dude <"foo:.bar"@somewhere.test>' } ] ]
767
+
768
+ validate_case\
769
+ ['"q\uo\ted"@example.com, Luke Skywalker <"use"."the.force"@space.test>',
770
+ [ { :name => nil,
771
+ :display_name => nil,
772
+ :address => 'quoted@example.com',
773
+ :comments => nil,
774
+ :domain => 'example.com',
775
+ :local => 'quoted',
776
+ :format => 'quoted@example.com' },
777
+ { :name => 'Luke Skywalker',
778
+ :display_name => 'Luke Skywalker',
779
+ :address => 'use.the.force@space.test',
780
+ :comments => nil,
781
+ :domain => 'space.test',
782
+ :local => 'use.the.force',
783
+ :format => 'Luke Skywalker <use.the.force@space.test>' } ] ]
784
+
785
+ validate_case\
786
+ ['Erik =?ISO-8859-1?Q?B=E5gfors?= <erik@example.net>',
787
+ [ { :name => 'Erik =?ISO-8859-1?Q?B=E5gfors?=',
788
+ :display_name => 'Erik =?ISO-8859-1?Q?B=E5gfors?=',
789
+ :address => 'erik@example.net',
790
+ :comments => nil,
791
+ :domain => 'example.net',
792
+ :local => 'erik',
793
+ :format => 'Erik =?ISO-8859-1?Q?B=E5gfors?= <erik@example.net>'
794
+ } ] ]
795
+ end
796
+
797
+ def test_invalid_addresses()
798
+ # The display name isn't encoded -- bad, but we parse it.
799
+ validate_case\
800
+ ["\322\315\322 \312\353\363\341 <bar@foo.invalid>",
801
+ [ { :name => "\322\315\322 \312\353\363\341",
802
+ :display_name => "\322\315\322 \312\353\363\341",
803
+ :address => 'bar@foo.invalid',
804
+ :comments => nil,
805
+ :domain => 'foo.invalid',
806
+ :local => 'bar',
807
+ :format => "\"\322\315\322 \312\353\363\341\" <bar@foo.invalid>",
808
+ } ] ]
809
+ end
810
+
811
+ def test_domain_literal
812
+
813
+ validate_case\
814
+ ['test@[domain]',
815
+ [ { :name => nil,
816
+ :display_name => nil,
817
+ :address => 'test@[domain]',
818
+ :comments => nil,
819
+ :domain => '[domain]',
820
+ :local => 'test',
821
+ :format => '<test@[domain]>' } ] ]
822
+
823
+ validate_case\
824
+ ['<@[obsdomain]:test@[domain]>',
825
+ [ { :name => nil,
826
+ :display_name => nil,
827
+ :address => 'test@[domain]',
828
+ :comments => nil,
829
+ :domain => '[domain]',
830
+ :local => 'test',
831
+ :format => '<test@[domain]>' } ] ]
832
+
833
+ validate_case\
834
+ ['<@[ob\]sd\\\\omain]:test@[dom\]ai\\\\n]>',
835
+ [ { :name => nil,
836
+ :display_name => nil,
837
+ :address => 'test@[dom]ai\\n]',
838
+ :comments => nil,
839
+ :domain => '[dom]ai\\n]',
840
+ :local => 'test',
841
+ :format => '<test@[dom\]ai\\\\n]>' } ] ]
842
+
843
+ validate_case\
844
+ ["Bob \r<@machine.tld \r,\n,,, @[obsdomain],@foo\t:\ntest @ [domain]>",
845
+ [ { :name => 'Bob',
846
+ :display_name => 'Bob',
847
+ :address => 'test@[domain]',
848
+ :comments => nil,
849
+ :domain => '[domain]',
850
+ :local => 'test',
851
+ :format => 'Bob <test@[domain]>' } ] ]
852
+ end
853
+
854
+ def test_exhaustive()
855
+
856
+ # We don't test every alphanumeric in atext -- assume that if a, m
857
+ # and z work, they all will.
858
+ atext = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a +
859
+ '!#$%&\'*+-/=?^_`{|}~'.split(//) #/
860
+ boring = ('b'..'l').to_a + ('n'..'o').to_a +
861
+ ('p'..'y').to_a + ('B'..'L').to_a + ('N'..'O').to_a +
862
+ ('P'..'Y').to_a + ('1'..'4').to_a + ('6'..'8').to_a
863
+
864
+ (atext - boring).each {|ch|
865
+ validate_case(["#{ch} <#{ch}@test>",
866
+ [ { :name => ch,
867
+ :display_name => ch,
868
+ :address => "#{ch}@test",
869
+ :comments => nil,
870
+ :domain => 'test',
871
+ :local => ch,
872
+ :format => ch + ' <' + ch + '@test>' } ] ])
873
+ }
874
+
875
+ validate_case([atext.join('') + ' <' + atext.join('') + '@test>',
876
+ [ { :name => atext.join(''),
877
+ :display_name => atext.join(''),
878
+ :address => atext.join('') + '@test',
879
+ :comments => nil,
880
+ :domain => 'test',
881
+ :local => atext.join(''),
882
+ :format => atext.join('') + ' <' + atext.join('') +
883
+ '@test>' } ] ])
884
+
885
+ ascii = (0..127).collect {|i| i.chr}
886
+ whitespace = ["\r", "\n", ' ', "\t"]
887
+ qtext = ascii - (whitespace + ['"', '\\'])
888
+ ctext = ascii - (whitespace + ['(', ')', '\\'])
889
+ dtext = ascii - (whitespace + ['[', ']', '\\'])
890
+
891
+ (qtext - atext).each {|ch|
892
+ validate_case(["\"#{ch}\" <\"#{ch}\"@test>",
893
+ [ { :name => ch,
894
+ :display_name => ch,
895
+ :address => "#{ch}@test",
896
+ :comments => nil,
897
+ :domain => 'test',
898
+ :local => "#{ch}",
899
+ :format => "\"#{ch}\" <\"#{ch}\"@test>" } ] ])
900
+ }
901
+ ['"', "\\"].each {|ch|
902
+ validate_case(["\"\\#{ch}\" <\"\\#{ch}\"@test>",
903
+ [ { :name => ch,
904
+ :display_name => ch,
905
+ :address => ch + '@test',
906
+ :comments => nil,
907
+ :domain => 'test',
908
+ :local => ch,
909
+ :format => "\"\\#{ch}\" <\"\\#{ch}\"@test>" } ] ])
910
+ }
911
+
912
+ (ctext - boring).each {|ch|
913
+ validate_case(["bob@test (#{ch})",
914
+ [ { :name => ch,
915
+ :display_name => nil,
916
+ :address => 'bob@test',
917
+ :comments => ["#{ch}"],
918
+ :domain => 'test',
919
+ :local => 'bob',
920
+ :format => "bob@test (#{ch})" } ] ])
921
+ validate_case(["bob@test (\\#{ch})",
922
+ [ { :name => ch,
923
+ :display_name => nil,
924
+ :address => 'bob@test',
925
+ :comments => ["#{ch}"],
926
+ :domain => 'test',
927
+ :local => 'bob',
928
+ :format => "bob@test (#{ch})" } ] ])
929
+ }
930
+ [')', '(', '\\'].each {|ch|
931
+ validate_case(["bob@test (\\#{ch})",
932
+ [ { :name => ch,
933
+ :display_name => nil,
934
+ :address => 'bob@test',
935
+ :comments => ["#{ch}"],
936
+ :domain => 'test',
937
+ :local => 'bob',
938
+ :format => 'bob@test (\\' + ch + ')' } ] ])
939
+ }
940
+
941
+ (dtext - boring).each {|ch|
942
+ validate_case(["test@[\\#{ch}] (Sam)",
943
+ [ { :name => "Sam",
944
+ :display_name => nil,
945
+ :address => 'test@[' + ch + ']',
946
+ :comments => ["Sam"],
947
+ :domain => '[' + ch + ']',
948
+ :local => 'test',
949
+ :format => "<test@[#{ch}]> (Sam)" } ] ] )
950
+ validate_case(["Sally <test@[\\#{ch}]>",
951
+ [ { :name => "Sally",
952
+ :display_name => "Sally",
953
+ :address => 'test@[' + ch + ']',
954
+ :comments => nil,
955
+ :domain => '[' + ch + ']',
956
+ :local => 'test',
957
+ :format => "Sally <test@[#{ch}]>" } ] ] )
958
+ }
959
+
960
+ validate_case(["test@[" + (dtext - boring).join('') + "]",
961
+ [ { :name => nil,
962
+ :display_name => nil,
963
+ :address => 'test@[' + (dtext - boring).join('') + "]",
964
+ :comments => nil,
965
+ :domain => '[' + (dtext - boring).join('') + ']',
966
+ :local => 'test',
967
+ :format => "<test@[" +
968
+ (dtext - boring).join('') + "]>" } ] ])
969
+ validate_case(["Bob <test@[" + (dtext - boring).join('') + "]>",
970
+ [ { :name => "Bob",
971
+ :display_name => "Bob",
972
+ :address => 'test@[' + (dtext - boring).join('') + "]",
973
+ :comments => nil,
974
+ :domain => '[' + (dtext - boring).join('') + ']',
975
+ :local => 'test',
976
+ :format => "Bob <test@[" +
977
+ (dtext - boring).join('') + "]>" } ] ])
978
+ end
979
+
980
+ def test_out_of_spec()
981
+
982
+ validate_case ['bodg fred@tiuk.ti.com', [] ]
983
+ validate_case ['(comment) bodg fred@example.net, matt@example.com',
984
+ [ { :name => nil, :display_name => nil, :comments => nil,
985
+ :address => 'matt@example.com',
986
+ :domain => 'example.com',
987
+ :local => 'matt',
988
+ :format => 'matt@example.com' } ] ]
989
+
990
+ validate_case ['<Investor Alert@example.com>', [] ]
991
+
992
+ validate_case ['"" <bob@example.com>',
993
+ [ { :name => nil,
994
+ :display_name => nil,
995
+ :address => 'bob@example.com',
996
+ :comments => nil,
997
+ :domain => 'example.com',
998
+ :local => 'bob',
999
+ :format => 'bob@example.com' } ] ]
1000
+
1001
+ validate_case\
1002
+ ['"" <""@example.com>',
1003
+ [ ] ]
1004
+
1005
+ validate_case\
1006
+ ['@example.com',
1007
+ [ ] ]
1008
+
1009
+ if domain_optional
1010
+ validate_case\
1011
+ ['bob',
1012
+ [ { :name => nil,
1013
+ :display_name => nil,
1014
+ :address => 'bob',
1015
+ :comments => nil,
1016
+ :domain => nil,
1017
+ :local => 'bob',
1018
+ :format => 'bob' } ] ]
1019
+
1020
+ validate_case\
1021
+ ['bob,sally, sam',
1022
+ [ { :name => nil,
1023
+ :display_name => nil,
1024
+ :address => 'bob',
1025
+ :comments => nil,
1026
+ :domain => nil,
1027
+ :local => 'bob',
1028
+ :format => 'bob' },
1029
+ { :name => nil,
1030
+ :display_name => nil,
1031
+ :address => 'sally',
1032
+ :comments => nil,
1033
+ :domain => nil,
1034
+ :local => 'sally',
1035
+ :format => 'sally' },
1036
+ { :name => nil,
1037
+ :display_name => nil,
1038
+ :address => 'sam',
1039
+ :comments => nil,
1040
+ :domain => nil,
1041
+ :local => 'sam',
1042
+ :format => 'sam' }] ]
1043
+ else
1044
+ validate_case [ 'bob', [] ]
1045
+ validate_case [ 'Bobby, sally,sam', [] ]
1046
+ end
1047
+
1048
+ validate_case(['Undisclosed <>', []])
1049
+ validate_case(['"Mensagem Automatica do Terra" <>', []])
1050
+
1051
+ validate_case(["\177", []])
1052
+ validate_case(["\177\177\177", []])
1053
+
1054
+ end
1055
+
1056
+ def test_random_strings
1057
+
1058
+ # These random strings have generated exceptions before, so test
1059
+ # them forever.
1060
+ RMail::Address.parse("j732[S\031\022\000\fuh\003Ye<2psd\005#1L=Hw*c\0247\006\aE\fXJ\026;\026\032zAAgpCFq+\010")
1061
+ RMail::Address.parse("\016o7=\024d^\001|h<,#\026~(<oS\005 f<u\022u+4\"\020d \023h\004)\036\016\023YY0\n]W]'\025S\t\035")
1062
+ RMail::Address.parse("<")
1063
+ RMail::Address.parse("J.:")
1064
+ # find_shortest_failure("\276\231J.^\351I:\2564") { |s|
1065
+ # RMail::Address.parse(s)
1066
+ # }
1067
+
1068
+ 0.upto(25) {
1069
+ specials = ',;\\()":@<>'
1070
+ strings = [(0..rand(255)).collect {rand(127).chr}.to_s,
1071
+ (0..255).collect {rand(255).chr}.to_s,
1072
+ (0..255).collect {
1073
+ r = rand(specials.length * 5)
1074
+ case r
1075
+ when 0 .. specials.length - 1
1076
+ specials[r].chr
1077
+ else
1078
+ rand(127).chr
1079
+ end
1080
+ }.to_s ]
1081
+ strings.each {|string|
1082
+ assert_no_exception("failed for string #{string.inspect}") {
1083
+ addrs = RMail::Address.parse(string)
1084
+ addrs.each {|address|
1085
+ method_list.each {|method|
1086
+ address.send(method)
1087
+ }
1088
+ }
1089
+ }
1090
+ }
1091
+ }
1092
+ end
1093
+
1094
+ def test_initialize
1095
+ addr = RMail::Address.new
1096
+ assert_instance_of(RMail::Address, addr)
1097
+
1098
+ addr = RMail::Address.new("Bob Smith (Joe Bob) <bobs@example.com>")
1099
+ assert_instance_of(RMail::Address, addr)
1100
+ assert_equal("Bob Smith", addr.display_name)
1101
+ assert_equal("Bob Smith", addr.name)
1102
+ assert_equal("example.com", addr.domain)
1103
+ assert_equal("bobs", addr.local)
1104
+
1105
+ addr = RMail::Address.new("Bob Smith (Joe Bob) <bobs@example.com>" +
1106
+ ", Sally Joe (Hi Babe) <sallyj@test.example>")
1107
+ assert_instance_of(RMail::Address, addr)
1108
+ assert_equal("Bob Smith", addr.display_name)
1109
+ assert_equal("Bob Smith", addr.name)
1110
+ assert_equal("example.com", addr.domain)
1111
+ assert_equal("bobs", addr.local)
1112
+
1113
+ addr = RMail::Address.new("\177\177")
1114
+ assert_instance_of(RMail::Address, addr)
1115
+ assert_equal(nil, addr.display_name)
1116
+ assert_equal(nil, addr.name)
1117
+ assert_equal(nil, addr.domain)
1118
+ assert_equal(nil, addr.local)
1119
+
1120
+ e = assert_exception(ArgumentError) {
1121
+ RMail::Address.new(["bob"])
1122
+ }
1123
+ e = assert_exception(ArgumentError) {
1124
+ RMail::Address.new(Object.new)
1125
+ }
1126
+ e = assert_exception(ArgumentError) {
1127
+ RMail::Address.new(Hash.new)
1128
+ }
1129
+ end
1130
+
1131
+ def test_comments
1132
+ a = RMail::Address.new
1133
+ assert_nil(a.comments)
1134
+ a.comments = [ 'foo', 'bar' ]
1135
+ assert_equal([ 'foo', 'bar' ], a.comments)
1136
+ a.comments = 'foo'
1137
+ assert_equal([ 'foo' ], a.comments)
1138
+ end
1139
+
1140
+ def test_rmail_address_compare
1141
+ a1 = RMail::Address.new
1142
+ a2 = RMail::Address.new
1143
+
1144
+ # Make sure it works on uninitialized addresses
1145
+ assert_equal(0, a1 <=> a2)
1146
+ assert_equal(a1, a2)
1147
+
1148
+ # Display name and comments make no difference
1149
+ a1.display_name = 'foo'
1150
+ a1.comments = 'bar'
1151
+ assert_equal(0, a1 <=> a2)
1152
+ assert_equal(a1, a2)
1153
+
1154
+ # a1 < a2
1155
+ a1 = RMail::Address.new("Zeus <bob@example.com>")
1156
+ a2 = RMail::Address.new("sally@example.com")
1157
+ assert(a1 < a2)
1158
+ assert(a1 != a2)
1159
+
1160
+ # Type coercion
1161
+ assert(a1 < "zorton@example.com")
1162
+ assert(a1 > ".")
1163
+ assert(a1 == "bob@example.com")
1164
+ end
1165
+
1166
+ def test_rmail_address_eql?
1167
+ a1 = RMail::Address.new
1168
+ a2 = RMail::Address.new
1169
+
1170
+ # Make sure it works on uninitialized addresses
1171
+ assert(a1.eql?(a2))
1172
+
1173
+ # Display name and comments make no difference
1174
+ a1.display_name = 'foo'
1175
+ a1.comments = 'bar'
1176
+ assert(a1.eql?(a2))
1177
+
1178
+ a1 = RMail::Address.new("Zeus <bob@example.com>")
1179
+ assert_exception(TypeError) {
1180
+ a1.eql?("bob@eaxample.com")
1181
+ }
1182
+ end
1183
+
1184
+ def test_rmail_address_hash
1185
+ assert_no_exception {
1186
+ RMail::Address.new.hash
1187
+ }
1188
+ a1 = RMail::Address.new("Zeus <bob@example.com>")
1189
+ assert_equal(a1.hash, "bob@example.com".hash)
1190
+ end
1191
+
1192
+ end