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,8 @@
1
+ # This is here for Rolls.
2
+ # Rolls uses this instead of lib/tmail.rb.
3
+
4
+ require 'tmail/version'
5
+ require 'tmail/mail'
6
+ require 'tmail/mailbox'
7
+ require 'tmail/core_extensions'
8
+
@@ -0,0 +1,540 @@
1
+ =begin rdoc
2
+
3
+ = Facade.rb Provides an interface to the TMail object
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/utils'
33
+
34
+ module TMail
35
+
36
+ class Mail
37
+
38
+ def header_string( name, default = nil )
39
+ h = @header[name.downcase] or return default
40
+ h.to_s
41
+ end
42
+
43
+ ###
44
+ ### attributes
45
+ ###
46
+
47
+ include TextUtils
48
+
49
+ def set_string_array_attr( key, strs )
50
+ strs.flatten!
51
+ if strs.empty?
52
+ @header.delete key.downcase
53
+ else
54
+ store key, strs.join(', ')
55
+ end
56
+ strs
57
+ end
58
+ private :set_string_array_attr
59
+
60
+ def set_string_attr( key, str )
61
+ if str
62
+ store key, str
63
+ else
64
+ @header.delete key.downcase
65
+ end
66
+ str
67
+ end
68
+ private :set_string_attr
69
+
70
+ def set_addrfield( name, arg )
71
+ if arg
72
+ h = HeaderField.internal_new(name, @config)
73
+ h.addrs.replace [arg].flatten
74
+ @header[name] = h
75
+ else
76
+ @header.delete name
77
+ end
78
+ arg
79
+ end
80
+ private :set_addrfield
81
+
82
+ def addrs2specs( addrs )
83
+ return nil unless addrs
84
+ list = addrs.map {|addr|
85
+ if addr.address_group?
86
+ then addr.map {|a| a.spec }
87
+ else addr.spec
88
+ end
89
+ }.flatten
90
+ return nil if list.empty?
91
+ list
92
+ end
93
+ private :addrs2specs
94
+
95
+ #
96
+ # date time
97
+ #
98
+
99
+ def date( default = nil )
100
+ if h = @header['date']
101
+ h.date
102
+ else
103
+ default
104
+ end
105
+ end
106
+
107
+ def date=( time )
108
+ if time
109
+ store 'Date', time2str(time)
110
+ else
111
+ @header.delete 'date'
112
+ end
113
+ time
114
+ end
115
+
116
+ def strftime( fmt, default = nil )
117
+ if t = date
118
+ t.strftime(fmt)
119
+ else
120
+ default
121
+ end
122
+ end
123
+
124
+ #
125
+ # destination
126
+ #
127
+
128
+ def to_addrs( default = nil )
129
+ if h = @header['to']
130
+ h.addrs
131
+ else
132
+ default
133
+ end
134
+ end
135
+
136
+ def cc_addrs( default = nil )
137
+ if h = @header['cc']
138
+ h.addrs
139
+ else
140
+ default
141
+ end
142
+ end
143
+
144
+ def bcc_addrs( default = nil )
145
+ if h = @header['bcc']
146
+ h.addrs
147
+ else
148
+ default
149
+ end
150
+ end
151
+
152
+ def to_addrs=( arg )
153
+ set_addrfield 'to', arg
154
+ end
155
+
156
+ def cc_addrs=( arg )
157
+ set_addrfield 'cc', arg
158
+ end
159
+
160
+ def bcc_addrs=( arg )
161
+ set_addrfield 'bcc', arg
162
+ end
163
+
164
+ def to( default = nil )
165
+ addrs2specs(to_addrs(nil)) || default
166
+ end
167
+
168
+ def cc( default = nil )
169
+ addrs2specs(cc_addrs(nil)) || default
170
+ end
171
+
172
+ def bcc( default = nil )
173
+ addrs2specs(bcc_addrs(nil)) || default
174
+ end
175
+
176
+ def to=( *strs )
177
+ set_string_array_attr 'To', strs
178
+ end
179
+
180
+ def cc=( *strs )
181
+ set_string_array_attr 'Cc', strs
182
+ end
183
+
184
+ def bcc=( *strs )
185
+ set_string_array_attr 'Bcc', strs
186
+ end
187
+
188
+ #
189
+ # originator
190
+ #
191
+
192
+ def from_addrs( default = nil )
193
+ if h = @header['from']
194
+ h.addrs
195
+ else
196
+ default
197
+ end
198
+ end
199
+
200
+ def from_addrs=( arg )
201
+ set_addrfield 'from', arg
202
+ end
203
+
204
+ def from( default = nil )
205
+ addrs2specs(from_addrs(nil)) || default
206
+ end
207
+
208
+ def from=( *strs )
209
+ set_string_array_attr 'From', strs
210
+ end
211
+
212
+ def friendly_from( default = nil )
213
+ h = @header['from']
214
+ a, = h.addrs
215
+ return default unless a
216
+ return a.phrase if a.phrase
217
+ return h.comments.join(' ') unless h.comments.empty?
218
+ a.spec
219
+ end
220
+
221
+
222
+ def reply_to_addrs( default = nil )
223
+ if h = @header['reply-to']
224
+ h.addrs
225
+ else
226
+ default
227
+ end
228
+ end
229
+
230
+ def reply_to_addrs=( arg )
231
+ set_addrfield 'reply-to', arg
232
+ end
233
+
234
+ def reply_to( default = nil )
235
+ addrs2specs(reply_to_addrs(nil)) || default
236
+ end
237
+
238
+ def reply_to=( *strs )
239
+ set_string_array_attr 'Reply-To', strs
240
+ end
241
+
242
+
243
+ def sender_addr( default = nil )
244
+ f = @header['sender'] or return default
245
+ f.addr or return default
246
+ end
247
+
248
+ def sender_addr=( addr )
249
+ if addr
250
+ h = HeaderField.internal_new('sender', @config)
251
+ h.addr = addr
252
+ @header['sender'] = h
253
+ else
254
+ @header.delete 'sender'
255
+ end
256
+ addr
257
+ end
258
+
259
+ def sender( default )
260
+ f = @header['sender'] or return default
261
+ a = f.addr or return default
262
+ a.spec
263
+ end
264
+
265
+ def sender=( str )
266
+ set_string_attr 'Sender', str
267
+ end
268
+
269
+
270
+ #
271
+ # subject
272
+ #
273
+
274
+ def subject( default = nil )
275
+ if h = @header['subject']
276
+ h.body
277
+ else
278
+ default
279
+ end
280
+ end
281
+ alias quoted_subject subject
282
+
283
+ def subject=( str )
284
+ set_string_attr 'Subject', str
285
+ end
286
+
287
+ #
288
+ # identity & threading
289
+ #
290
+
291
+ def message_id( default = nil )
292
+ if h = @header['message-id']
293
+ h.id || default
294
+ else
295
+ default
296
+ end
297
+ end
298
+
299
+ def message_id=( str )
300
+ set_string_attr 'Message-Id', str
301
+ end
302
+
303
+ def in_reply_to( default = nil )
304
+ if h = @header['in-reply-to']
305
+ h.ids
306
+ else
307
+ default
308
+ end
309
+ end
310
+
311
+ def in_reply_to=( *idstrs )
312
+ set_string_array_attr 'In-Reply-To', idstrs
313
+ end
314
+
315
+ def references( default = nil )
316
+ if h = @header['references']
317
+ h.refs
318
+ else
319
+ default
320
+ end
321
+ end
322
+
323
+ def references=( *strs )
324
+ set_string_array_attr 'References', strs
325
+ end
326
+
327
+ #
328
+ # MIME headers
329
+ #
330
+
331
+ def mime_version( default = nil )
332
+ if h = @header['mime-version']
333
+ h.version || default
334
+ else
335
+ default
336
+ end
337
+ end
338
+
339
+ def mime_version=( m, opt = nil )
340
+ if opt
341
+ if h = @header['mime-version']
342
+ h.major = m
343
+ h.minor = opt
344
+ else
345
+ store 'Mime-Version', "#{m}.#{opt}"
346
+ end
347
+ else
348
+ store 'Mime-Version', m
349
+ end
350
+ m
351
+ end
352
+
353
+ def content_type( default = nil )
354
+ if h = @header['content-type']
355
+ h.content_type || default
356
+ else
357
+ default
358
+ end
359
+ end
360
+
361
+ def main_type( default = nil )
362
+ if h = @header['content-type']
363
+ h.main_type || default
364
+ else
365
+ default
366
+ end
367
+ end
368
+
369
+ def sub_type( default = nil )
370
+ if h = @header['content-type']
371
+ h.sub_type || default
372
+ else
373
+ default
374
+ end
375
+ end
376
+
377
+ def set_content_type( str, sub = nil, param = nil )
378
+ if sub
379
+ main, sub = str, sub
380
+ else
381
+ main, sub = str.split(%r</>, 2)
382
+ raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
383
+ end
384
+ if h = @header['content-type']
385
+ h.main_type = main
386
+ h.sub_type = sub
387
+ h.params.clear
388
+ else
389
+ store 'Content-Type', "#{main}/#{sub}"
390
+ end
391
+ @header['content-type'].params.replace param if param
392
+ str
393
+ end
394
+
395
+ alias content_type= set_content_type
396
+
397
+ def type_param( name, default = nil )
398
+ if h = @header['content-type']
399
+ h[name] || default
400
+ else
401
+ default
402
+ end
403
+ end
404
+
405
+ def charset( default = nil )
406
+ if h = @header['content-type']
407
+ h['charset'] or default
408
+ else
409
+ default
410
+ end
411
+ end
412
+
413
+ def charset=( str )
414
+ if str
415
+ if h = @header[ 'content-type' ]
416
+ h['charset'] = str
417
+ else
418
+ store 'Content-Type', "text/plain; charset=#{str}"
419
+ end
420
+ end
421
+ str
422
+ end
423
+
424
+ def transfer_encoding( default = nil )
425
+ if h = @header['content-transfer-encoding']
426
+ h.encoding || default
427
+ else
428
+ default
429
+ end
430
+ end
431
+
432
+ def transfer_encoding=( str )
433
+ set_string_attr 'Content-Transfer-Encoding', str
434
+ end
435
+
436
+ alias encoding transfer_encoding
437
+ alias encoding= transfer_encoding=
438
+ alias content_transfer_encoding transfer_encoding
439
+ alias content_transfer_encoding= transfer_encoding=
440
+
441
+ def disposition( default = nil )
442
+ if h = @header['content-disposition']
443
+ h.disposition || default
444
+ else
445
+ default
446
+ end
447
+ end
448
+
449
+ alias content_disposition disposition
450
+
451
+ def set_disposition( str, params = nil )
452
+ if h = @header['content-disposition']
453
+ h.disposition = str
454
+ h.params.clear
455
+ else
456
+ store('Content-Disposition', str)
457
+ h = @header['content-disposition']
458
+ end
459
+ h.params.replace params if params
460
+ end
461
+
462
+ alias disposition= set_disposition
463
+ alias set_content_disposition set_disposition
464
+ alias content_disposition= set_disposition
465
+
466
+ def disposition_param( name, default = nil )
467
+ if h = @header['content-disposition']
468
+ h[name] || default
469
+ else
470
+ default
471
+ end
472
+ end
473
+
474
+ ###
475
+ ### utils
476
+ ###
477
+
478
+ def create_reply
479
+ mail = TMail::Mail.parse('')
480
+ mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
481
+ mail.to_addrs = reply_addresses([])
482
+ mail.in_reply_to = [message_id(nil)].compact
483
+ mail.references = references([]) + [message_id(nil)].compact
484
+ mail.mime_version = '1.0'
485
+ mail
486
+ end
487
+
488
+ def base64_encode
489
+ store 'Content-Transfer-Encoding', 'Base64'
490
+ self.body = Base64.folding_encode(self.body)
491
+ end
492
+
493
+ def base64_decode
494
+ if /base64/i === self.transfer_encoding('')
495
+ store 'Content-Transfer-Encoding', '8bit'
496
+ self.body = Base64.decode(self.body, @config.strict_base64decode?)
497
+ end
498
+ end
499
+
500
+ def destinations( default = nil )
501
+ ret = []
502
+ %w( to cc bcc ).each do |nm|
503
+ if h = @header[nm]
504
+ h.addrs.each {|i| ret.push i.address }
505
+ end
506
+ end
507
+ ret.empty? ? default : ret
508
+ end
509
+
510
+ def each_destination( &block )
511
+ destinations([]).each do |i|
512
+ if Address === i
513
+ yield i
514
+ else
515
+ i.each(&block)
516
+ end
517
+ end
518
+ end
519
+
520
+ alias each_dest each_destination
521
+
522
+ def reply_addresses( default = nil )
523
+ reply_to_addrs(nil) or from_addrs(nil) or default
524
+ end
525
+
526
+ def error_reply_addresses( default = nil )
527
+ if s = sender(nil)
528
+ [s]
529
+ else
530
+ from_addrs(default)
531
+ end
532
+ end
533
+
534
+ def multipart?
535
+ main_type('').downcase == 'multipart'
536
+ end
537
+
538
+ end # class Mail
539
+
540
+ end # module TMail