valodzka-valodzka-tmail 1.2.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/CHANGES +74 -0
  2. data/LICENSE +21 -0
  3. data/NOTES +7 -0
  4. data/README +169 -0
  5. data/Rakefile +2 -0
  6. data/ext/Makefile +20 -0
  7. data/ext/tmailscanner/tmail/MANIFEST +4 -0
  8. data/ext/tmailscanner/tmail/depend +1 -0
  9. data/ext/tmailscanner/tmail/extconf.rb +33 -0
  10. data/ext/tmailscanner/tmail/tmailscanner.c +583 -0
  11. data/lib/tmail.rb +5 -0
  12. data/lib/tmail/Makefile +18 -0
  13. data/lib/tmail/address.rb +392 -0
  14. data/lib/tmail/attachments.rb +46 -0
  15. data/lib/tmail/base64.rb +46 -0
  16. data/lib/tmail/compat.rb +41 -0
  17. data/lib/tmail/config.rb +67 -0
  18. data/lib/tmail/core_extensions.rb +63 -0
  19. data/lib/tmail/encode.rb +590 -0
  20. data/lib/tmail/header.rb +960 -0
  21. data/lib/tmail/index.rb +9 -0
  22. data/lib/tmail/interface.rb +1152 -0
  23. data/lib/tmail/loader.rb +3 -0
  24. data/lib/tmail/mail.rb +582 -0
  25. data/lib/tmail/mailbox.rb +496 -0
  26. data/lib/tmail/main.rb +6 -0
  27. data/lib/tmail/mbox.rb +3 -0
  28. data/lib/tmail/net.rb +250 -0
  29. data/lib/tmail/obsolete.rb +132 -0
  30. data/lib/tmail/parser.rb +1060 -0
  31. data/lib/tmail/parser.y +416 -0
  32. data/lib/tmail/port.rb +379 -0
  33. data/lib/tmail/quoting.rb +125 -0
  34. data/lib/tmail/require_arch.rb +58 -0
  35. data/lib/tmail/scanner.rb +49 -0
  36. data/lib/tmail/scanner_r.rb +261 -0
  37. data/lib/tmail/stringio.rb +280 -0
  38. data/lib/tmail/utils.rb +337 -0
  39. data/lib/tmail/version.rb +39 -0
  40. data/log/BugTrackingLog.txt +1245 -0
  41. data/log/Changelog.txt +534 -0
  42. data/log/Testlog.txt +2340 -0
  43. data/log/Todo.txt +30 -0
  44. data/meta/MANIFEST +128 -0
  45. data/meta/VERSION +1 -0
  46. data/meta/project.yaml +30 -0
  47. data/meta/unixname +1 -0
  48. data/sample/bench_base64.rb +48 -0
  49. data/sample/data/multipart +23 -0
  50. data/sample/data/normal +29 -0
  51. data/sample/data/sendtest +5 -0
  52. data/sample/data/simple +14 -0
  53. data/sample/data/test +27 -0
  54. data/sample/extract-attachements.rb +33 -0
  55. data/sample/from-check.rb +26 -0
  56. data/sample/multipart.rb +26 -0
  57. data/sample/parse-bench.rb +68 -0
  58. data/sample/parse-test.rb +19 -0
  59. data/sample/sendmail.rb +94 -0
  60. data/setup.rb +1482 -0
  61. data/site/contributing/index.html +183 -0
  62. data/site/css/clean.css +27 -0
  63. data/site/css/layout.css +31 -0
  64. data/site/css/style.css +60 -0
  65. data/site/download/index.html +61 -0
  66. data/site/img/envelope.jpg +0 -0
  67. data/site/img/mailman.gif +0 -0
  68. data/site/img/stamp-sm.jpg +0 -0
  69. data/site/img/stamp.jpg +0 -0
  70. data/site/img/stampborder.jpg +0 -0
  71. data/site/img/tfire.jpg +0 -0
  72. data/site/img/tmail.png +0 -0
  73. data/site/index.html +270 -0
  74. data/site/js/jquery.js +31 -0
  75. data/site/log/Changelog.xsl +33 -0
  76. data/site/log/changelog.xml +1677 -0
  77. data/site/outdated/BUGS +3 -0
  78. data/site/outdated/DEPENDS +1 -0
  79. data/site/outdated/Incompatibilities +89 -0
  80. data/site/outdated/Incompatibilities.ja +102 -0
  81. data/site/outdated/NEWS +9 -0
  82. data/site/outdated/README.ja +73 -0
  83. data/site/outdated/doc.ja/address.html +275 -0
  84. data/site/outdated/doc.ja/basics.html +405 -0
  85. data/site/outdated/doc.ja/config.html +49 -0
  86. data/site/outdated/doc.ja/details.html +146 -0
  87. data/site/outdated/doc.ja/index.html +39 -0
  88. data/site/outdated/doc.ja/mail.html +793 -0
  89. data/site/outdated/doc.ja/mailbox.html +265 -0
  90. data/site/outdated/doc.ja/port.html +95 -0
  91. data/site/outdated/doc.ja/tmail.html +58 -0
  92. data/site/outdated/doc.ja/usage.html +202 -0
  93. data/site/outdated/rdd/address.rrd.m +229 -0
  94. data/site/outdated/rdd/basics.rd.m +275 -0
  95. data/site/outdated/rdd/config.rrd.m +26 -0
  96. data/site/outdated/rdd/details.rd.m +117 -0
  97. data/site/outdated/rdd/index.rhtml.m +54 -0
  98. data/site/outdated/rdd/mail.rrd.m +701 -0
  99. data/site/outdated/rdd/mailbox.rrd.m +228 -0
  100. data/site/outdated/rdd/port.rrd.m +69 -0
  101. data/site/outdated/rdd/tmail.rrd.m +33 -0
  102. data/site/outdated/rdd/usage.rd.m +247 -0
  103. data/site/quickstart/index.html +69 -0
  104. data/site/quickstart/quickstart.html +52 -0
  105. data/site/quickstart/usage.html +193 -0
  106. data/site/reference/address.html +247 -0
  107. data/site/reference/config.html +30 -0
  108. data/site/reference/index.html +101 -0
  109. data/site/reference/mail.html +726 -0
  110. data/site/reference/mailbox.html +245 -0
  111. data/site/reference/port.html +75 -0
  112. data/site/reference/tmail.html +35 -0
  113. data/test/extctrl.rb +6 -0
  114. data/test/fixtures/mailbox +414 -0
  115. data/test/fixtures/mailbox_without_any_from_or_sender +10 -0
  116. data/test/fixtures/mailbox_without_from +11 -0
  117. data/test/fixtures/mailbox_without_return_path +12 -0
  118. data/test/fixtures/raw_attack_email_with_zero_length_whitespace +29 -0
  119. data/test/fixtures/raw_base64_decoded_string +0 -0
  120. data/test/fixtures/raw_base64_email +83 -0
  121. data/test/fixtures/raw_base64_encoded_string +1 -0
  122. data/test/fixtures/raw_email +14 -0
  123. data/test/fixtures/raw_email10 +20 -0
  124. data/test/fixtures/raw_email11 +34 -0
  125. data/test/fixtures/raw_email12 +32 -0
  126. data/test/fixtures/raw_email13 +29 -0
  127. data/test/fixtures/raw_email2 +114 -0
  128. data/test/fixtures/raw_email3 +70 -0
  129. data/test/fixtures/raw_email4 +59 -0
  130. data/test/fixtures/raw_email5 +19 -0
  131. data/test/fixtures/raw_email6 +20 -0
  132. data/test/fixtures/raw_email7 +66 -0
  133. data/test/fixtures/raw_email8 +47 -0
  134. data/test/fixtures/raw_email9 +28 -0
  135. data/test/fixtures/raw_email_multiple_from +30 -0
  136. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  137. data/test/fixtures/raw_email_reply +32 -0
  138. data/test/fixtures/raw_email_simple +11 -0
  139. data/test/fixtures/raw_email_with_bad_date +48 -0
  140. data/test/fixtures/raw_email_with_double_carriage_return +179 -0
  141. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  142. data/test/fixtures/raw_email_with_mimepart_without_content_type +94 -0
  143. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  144. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  145. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  146. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  147. data/test/kcode.rb +14 -0
  148. data/test/temp_test_one.rb +46 -0
  149. data/test/test_address.rb +1214 -0
  150. data/test/test_attachments.rb +73 -0
  151. data/test/test_base64.rb +64 -0
  152. data/test/test_encode.rb +85 -0
  153. data/test/test_header.rb +1002 -0
  154. data/test/test_helper.rb +9 -0
  155. data/test/test_mail.rb +761 -0
  156. data/test/test_mbox.rb +184 -0
  157. data/test/test_port.rb +436 -0
  158. data/test/test_quote.rb +107 -0
  159. data/test/test_scanner.rb +209 -0
  160. data/test/test_utils.rb +36 -0
  161. data/work/script/make +26 -0
  162. data/work/script/rdoc +39 -0
  163. data/work/script/setup +1616 -0
  164. data/work/script/test +30 -0
  165. metadata +298 -0
@@ -0,0 +1,3 @@
1
+ #:stopdoc:
2
+ require 'tmail/mailbox'
3
+ #:startdoc:
data/lib/tmail/mail.rb ADDED
@@ -0,0 +1,582 @@
1
+ =begin rdoc
2
+
3
+ = Mail 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
+
33
+
34
+ require 'tmail/interface'
35
+ require 'tmail/encode'
36
+ require 'tmail/header'
37
+ require 'tmail/port'
38
+ require 'tmail/config'
39
+ require 'tmail/utils'
40
+ require 'tmail/attachments'
41
+ require 'tmail/quoting'
42
+ require 'socket'
43
+
44
+ module TMail
45
+
46
+ # == Mail Class
47
+ #
48
+ # Accessing a TMail object done via the TMail::Mail class. As email can be fairly complex
49
+ # creatures, you will find a large amount of accessor and setter methods in this class!
50
+ #
51
+ # Most of the below methods handle the header, in fact, what TMail does best is handle the
52
+ # header of the email object. There are only a few methods that deal directly with the body
53
+ # of the email, such as base64_encode and base64_decode.
54
+ #
55
+ # === Using TMail inside your code
56
+ #
57
+ # The usual way is to install the gem (see the {README}[link:/README] on how to do this) and
58
+ # then put at the top of your class:
59
+ #
60
+ # require 'tmail'
61
+ #
62
+ # You can then create a new TMail object in your code with:
63
+ #
64
+ # @email = TMail::Mail.new
65
+ #
66
+ # Or if you have an email as a string, you can initialize a new TMail::Mail object and get it
67
+ # to parse that string for you like so:
68
+ #
69
+ # @email = TMail::Mail.parse(email_text)
70
+ #
71
+ # You can also read a single email off the disk, for example:
72
+ #
73
+ # @email = TMail::Mail.load('filename.txt')
74
+ #
75
+ # Also, you can read a mailbox (usual unix mbox format) and end up with an array of TMail
76
+ # objects by doing something like this:
77
+ #
78
+ # # Note, we pass true as the last variable to open the mailbox read only
79
+ # mailbox = TMail::UNIXMbox.new("mailbox", nil, true)
80
+ # @emails = []
81
+ # mailbox.each_port { |m| @emails << TMail::Mail.new(m) }
82
+ #
83
+ class Mail
84
+
85
+ class << self
86
+
87
+ # Opens an email that has been saved out as a file by itself.
88
+ #
89
+ # This function will read a file non-destructively and then parse
90
+ # the contents and return a TMail::Mail object.
91
+ #
92
+ # Does not handle multiple email mailboxes (like a unix mbox) for that
93
+ # use the TMail::UNIXMbox class.
94
+ #
95
+ # Example:
96
+ # mail = TMail::Mail.load('filename')
97
+ #
98
+ def load( fname )
99
+ new(FilePort.new(fname))
100
+ end
101
+
102
+ alias load_from load
103
+ alias loadfrom load
104
+
105
+ # Parses an email from the supplied string and returns a TMail::Mail
106
+ # object.
107
+ #
108
+ # Example:
109
+ # require 'rubygems'; require 'tmail'
110
+ # email_string =<<HEREDOC
111
+ # To: mikel@lindsaar.net
112
+ # From: mikel@me.com
113
+ # Subject: This is a short Email
114
+ #
115
+ # Hello there Mikel!
116
+ #
117
+ # HEREDOC
118
+ # mail = TMail::Mail.parse(email_string)
119
+ # #=> #<TMail::Mail port=#<TMail::StringPort:id=0xa30ac0> bodyport=nil>
120
+ # mail.body
121
+ # #=> "Hello there Mikel!\n\n"
122
+ def parse( str )
123
+ new(StringPort.new(str))
124
+ end
125
+
126
+ end
127
+
128
+ def initialize( port = nil, conf = DEFAULT_CONFIG ) #:nodoc:
129
+ @port = port || StringPort.new
130
+ @config = Config.to_config(conf)
131
+
132
+ @header = {}
133
+ @body_port = nil
134
+ @body_parsed = false
135
+ @epilogue = ''
136
+ @parts = []
137
+
138
+ @port.ropen {|f|
139
+ parse_header f
140
+ parse_body f unless @port.reproducible?
141
+ }
142
+ end
143
+
144
+ # Provides access to the port this email is using to hold it's data
145
+ #
146
+ # Example:
147
+ # mail = TMail::Mail.parse(email_string)
148
+ # mail.port
149
+ # #=> #<TMail::StringPort:id=0xa2c952>
150
+ attr_reader :port
151
+
152
+ def inspect
153
+ "\#<#{self.class} port=#{@port.inspect} bodyport=#{@body_port.inspect}>"
154
+ end
155
+
156
+ #
157
+ # to_s interfaces
158
+ #
159
+
160
+ public
161
+
162
+ include StrategyInterface
163
+
164
+ def write_back( eol = "\n", charset = 'e' )
165
+ parse_body
166
+ @port.wopen {|stream| encoded eol, charset, stream }
167
+ end
168
+
169
+ def accept( strategy )
170
+ with_multipart_encoding(strategy) {
171
+ ordered_each do |name, field|
172
+ next if field.empty?
173
+ strategy.header_name canonical(name)
174
+ field.accept strategy
175
+ strategy.puts
176
+ end
177
+ strategy.puts
178
+ body_port().ropen {|r|
179
+ strategy.write r.read
180
+ }
181
+ }
182
+ end
183
+
184
+ private
185
+
186
+ def canonical( name )
187
+ name.split(/-/).map {|s| s.capitalize }.join('-')
188
+ end
189
+
190
+ def with_multipart_encoding( strategy )
191
+ if parts().empty? # DO NOT USE @parts
192
+ yield
193
+
194
+ else
195
+ bound = ::TMail.new_boundary
196
+ if @header.key? 'content-type'
197
+ @header['content-type'].params['boundary'] = bound
198
+ else
199
+ store 'Content-Type', %<multipart/mixed; boundary="#{bound}">
200
+ end
201
+
202
+ yield
203
+
204
+ parts().each do |tm|
205
+ strategy.puts
206
+ strategy.puts '--' + bound
207
+ tm.accept strategy
208
+ end
209
+ strategy.puts
210
+ strategy.puts '--' + bound + '--'
211
+ strategy.write epilogue()
212
+ end
213
+ end
214
+
215
+ ###
216
+ ### header
217
+ ###
218
+
219
+ public
220
+
221
+ ALLOW_MULTIPLE = {
222
+ 'received' => true,
223
+ 'resent-date' => true,
224
+ 'resent-from' => true,
225
+ 'resent-sender' => true,
226
+ 'resent-to' => true,
227
+ 'resent-cc' => true,
228
+ 'resent-bcc' => true,
229
+ 'resent-message-id' => true,
230
+ 'comments' => true,
231
+ 'keywords' => true
232
+ }
233
+ USE_ARRAY = ALLOW_MULTIPLE
234
+
235
+ def header
236
+ @header.dup
237
+ end
238
+
239
+ # Returns a TMail::AddressHeader object of the field you are querying.
240
+ # Examples:
241
+ # @mail['from'] #=> #<TMail::AddressHeader "mikel@test.com.au">
242
+ # @mail['to'] #=> #<TMail::AddressHeader "mikel@test.com.au">
243
+ #
244
+ # You can get the string value of this by passing "to_s" to the query:
245
+ # Example:
246
+ # @mail['to'].to_s #=> "mikel@test.com.au"
247
+ def []( key )
248
+ @header[key.downcase]
249
+ end
250
+
251
+ def sub_header(key, param)
252
+ (hdr = self[key]) ? hdr[param] : nil
253
+ end
254
+
255
+ alias fetch []
256
+
257
+ # Allows you to set or delete TMail header objects at will.
258
+ # Examples:
259
+ # @mail = TMail::Mail.new
260
+ # @mail['to'].to_s # => 'mikel@test.com.au'
261
+ # @mail['to'] = 'mikel@elsewhere.org'
262
+ # @mail['to'].to_s # => 'mikel@elsewhere.org'
263
+ # @mail.encoded # => "To: mikel@elsewhere.org\r\n\r\n"
264
+ # @mail['to'] = nil
265
+ # @mail['to'].to_s # => nil
266
+ # @mail.encoded # => "\r\n"
267
+ #
268
+ # Note: setting mail[] = nil actually deletes the header field in question from the object,
269
+ # it does not just set the value of the hash to nil
270
+ def []=( key, val )
271
+ dkey = key.downcase
272
+
273
+ if val.nil?
274
+ @header.delete dkey
275
+ return nil
276
+ end
277
+
278
+ case val
279
+ when String
280
+ header = new_hf(key, val)
281
+ when HeaderField
282
+ ;
283
+ when Array
284
+ ALLOW_MULTIPLE.include? dkey or
285
+ raise ArgumentError, "#{key}: Header must not be multiple"
286
+ @header[dkey] = val
287
+ return val
288
+ else
289
+ header = new_hf(key, val.to_s)
290
+ end
291
+ if ALLOW_MULTIPLE.include? dkey
292
+ (@header[dkey] ||= []).push header
293
+ else
294
+ @header[dkey] = header
295
+ end
296
+
297
+ val
298
+ end
299
+
300
+ alias store []=
301
+
302
+ # Allows you to loop through each header in the TMail::Mail object in a block
303
+ # Example:
304
+ # @mail['to'] = 'mikel@elsewhere.org'
305
+ # @mail['from'] = 'me@me.com'
306
+ # @mail.each_header { |k,v| puts "#{k} = #{v}" }
307
+ # # => from = me@me.com
308
+ # # => to = mikel@elsewhere.org
309
+ def each_header
310
+ @header.each do |key, val|
311
+ [val].flatten.each {|v| yield key, v }
312
+ end
313
+ end
314
+
315
+ alias each_pair each_header
316
+
317
+ def each_header_name( &block )
318
+ @header.each_key(&block)
319
+ end
320
+
321
+ alias each_key each_header_name
322
+
323
+ def each_field( &block )
324
+ @header.values.flatten.each(&block)
325
+ end
326
+
327
+ alias each_value each_field
328
+
329
+ FIELD_ORDER = %w(
330
+ return-path received
331
+ resent-date resent-from resent-sender resent-to
332
+ resent-cc resent-bcc resent-message-id
333
+ date from sender reply-to to cc bcc
334
+ message-id in-reply-to references
335
+ subject comments keywords
336
+ mime-version content-type content-transfer-encoding
337
+ content-disposition content-description
338
+ )
339
+
340
+ def ordered_each
341
+ list = @header.keys
342
+ FIELD_ORDER.each do |name|
343
+ if list.delete(name)
344
+ [@header[name]].flatten.each {|v| yield name, v }
345
+ end
346
+ end
347
+ list.each do |name|
348
+ [@header[name]].flatten.each {|v| yield name, v }
349
+ end
350
+ end
351
+
352
+ def clear
353
+ @header.clear
354
+ end
355
+
356
+ def delete( key )
357
+ @header.delete key.downcase
358
+ end
359
+
360
+ def delete_if
361
+ @header.delete_if do |key,val|
362
+ if Array === val
363
+ val.delete_if {|v| yield key, v }
364
+ val.empty?
365
+ else
366
+ yield key, val
367
+ end
368
+ end
369
+ end
370
+
371
+ def keys
372
+ @header.keys
373
+ end
374
+
375
+ def key?( key )
376
+ @header.key? key.downcase
377
+ end
378
+
379
+ def values_at( *args )
380
+ args.map {|k| @header[k.downcase] }.flatten
381
+ end
382
+
383
+ alias indexes values_at
384
+ alias indices values_at
385
+
386
+ private
387
+
388
+ def parse_header( f )
389
+ name = field = nil
390
+ unixfrom = nil
391
+ stop = false
392
+
393
+ while (line = f.gets) && !stop
394
+ # thread \r\r\n as \r\n\r\n (double new line)
395
+ stop = true if line =~ /\r\r\n\z/
396
+
397
+ case line
398
+ when /\A[ \t]/ # continue from prev line
399
+ raise SyntaxError, 'mail is began by space' unless field
400
+ field << ' ' << line.strip
401
+
402
+ when /\A([^\: \t]+):\s*/ # new header line
403
+ add_hf name, field if field
404
+ name = $1
405
+ field = $' #.strip
406
+
407
+ when /\A\-*\s*\z/ # end of header
408
+ add_hf name, field if field
409
+ name = field = nil
410
+ break
411
+
412
+ when /\AFrom (\S+)/
413
+ unixfrom = $1
414
+
415
+ when /^charset=.*/
416
+
417
+ else
418
+ raise SyntaxError, "wrong mail header: '#{line.inspect}'"
419
+ end
420
+ end
421
+ add_hf name, field if name
422
+
423
+ if unixfrom
424
+ add_hf 'Return-Path', "<#{unixfrom}>" unless @header['return-path']
425
+ end
426
+ end
427
+
428
+ def add_hf( name, field )
429
+ key = name.downcase
430
+ field = new_hf(name, field)
431
+
432
+ if ALLOW_MULTIPLE.include? key
433
+ (@header[key] ||= []).push field
434
+ else
435
+ @header[key] = field
436
+ end
437
+ end
438
+
439
+ def new_hf( name, field )
440
+ HeaderField.new(name, field, @config)
441
+ end
442
+
443
+ ###
444
+ ### body
445
+ ###
446
+
447
+ public
448
+
449
+ def body_port
450
+ parse_body
451
+ @body_port
452
+ end
453
+
454
+ def each( &block )
455
+ body_port().ropen {|f| f.each(&block) }
456
+ end
457
+
458
+ def quoted_body
459
+ body_port.ropen {|f| return f.read }
460
+ end
461
+
462
+ def quoted_body= str
463
+ body_port.wopen { |f| f.write str }
464
+ str
465
+ end
466
+
467
+ def body=( str )
468
+ # Sets the body of the email to a new (encoded) string.
469
+ #
470
+ # We also reparses the email if the body is ever reassigned, this is a performance hit, however when
471
+ # you assign the body, you usually want to be able to make sure that you can access the attachments etc.
472
+ #
473
+ # Usage:
474
+ #
475
+ # mail.body = "Hello, this is\nthe body text"
476
+ # # => "Hello, this is\nthe body"
477
+ # mail.body
478
+ # # => "Hello, this is\nthe body"
479
+ @body_parsed = false
480
+ parse_body(StringInput.new(str))
481
+ parse_body
482
+ @body_port.wopen {|f| f.write str }
483
+ str
484
+ end
485
+
486
+ alias preamble quoted_body
487
+ alias preamble= quoted_body=
488
+
489
+ def epilogue
490
+ parse_body
491
+ @epilogue.dup
492
+ end
493
+
494
+ def epilogue=( str )
495
+ parse_body
496
+ @epilogue = str
497
+ str
498
+ end
499
+
500
+ def parts
501
+ parse_body
502
+ @parts
503
+ end
504
+
505
+ def each_part( &block )
506
+ parts().each(&block)
507
+ end
508
+
509
+ # Returns true if the content type of this part of the email is
510
+ # a disposition attachment
511
+ def disposition_is_attachment?
512
+ (self['content-disposition'] && self['content-disposition'].disposition == "attachment")
513
+ end
514
+
515
+ # Returns true if this part's content main type is text, else returns false.
516
+ # By main type is meant "text/plain" is text. "text/html" is text
517
+ def content_type_is_text?
518
+ self.header['content-type'] && (self.header['content-type'].main_type != "text")
519
+ end
520
+
521
+ private
522
+
523
+ def parse_body( f = nil )
524
+ return if @body_parsed
525
+ if f
526
+ parse_body_0 f
527
+ else
528
+ @port.ropen {|f|
529
+ skip_header f
530
+ parse_body_0 f
531
+ }
532
+ end
533
+ @body_parsed = true
534
+ end
535
+
536
+ def skip_header( f )
537
+ while line = f.gets
538
+ return if /\A[\r\n]*\z/ === line
539
+ end
540
+ end
541
+
542
+ def parse_body_0( f )
543
+ if multipart?
544
+ read_multipart f
545
+ else
546
+ @body_port = @config.new_body_port(self)
547
+ @body_port.wopen {|w|
548
+ w.write f.read
549
+ }
550
+ end
551
+ end
552
+
553
+ def read_multipart( src )
554
+ bound = @header['content-type'].params['boundary'] || ::TMail.new_boundary
555
+ is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/
556
+ lastbound = "--#{bound}--"
557
+
558
+ ports = [ @config.new_preamble_port(self) ]
559
+ begin
560
+ f = ports.last.wopen
561
+ while line = src.gets
562
+ if is_sep === line
563
+ f.close
564
+ break if line.strip == lastbound
565
+ ports.push @config.new_part_port(self)
566
+ f = ports.last.wopen
567
+ else
568
+ f << line
569
+ end
570
+ end
571
+ @epilogue = (src.read || '')
572
+ ensure
573
+ f.close if f and not f.closed?
574
+ end
575
+
576
+ @body_port = ports.shift
577
+ @parts = ports.map {|p| self.class.new(p, @config) }
578
+ end
579
+
580
+ end # class Mail
581
+
582
+ end # module TMail