actionmailer-2.3.17-rack-upgrade 2.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +387 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +149 -0
  4. data/Rakefile +97 -0
  5. data/install.rb +30 -0
  6. data/lib/action_mailer.rb +62 -0
  7. data/lib/action_mailer/adv_attr_accessor.rb +30 -0
  8. data/lib/action_mailer/base.rb +739 -0
  9. data/lib/action_mailer/helpers.rb +113 -0
  10. data/lib/action_mailer/mail_helper.rb +17 -0
  11. data/lib/action_mailer/part.rb +107 -0
  12. data/lib/action_mailer/part_container.rb +55 -0
  13. data/lib/action_mailer/quoting.rb +62 -0
  14. data/lib/action_mailer/test_case.rb +64 -0
  15. data/lib/action_mailer/test_helper.rb +68 -0
  16. data/lib/action_mailer/utils.rb +7 -0
  17. data/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb +1466 -0
  18. data/lib/action_mailer/vendor/text_format.rb +10 -0
  19. data/lib/action_mailer/vendor/tmail-1.2.7/tmail.rb +6 -0
  20. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/Makefile +18 -0
  21. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/address.rb +392 -0
  22. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/attachments.rb +65 -0
  23. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/base64.rb +46 -0
  24. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/compat.rb +41 -0
  25. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/config.rb +67 -0
  26. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/core_extensions.rb +63 -0
  27. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/encode.rb +590 -0
  28. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/header.rb +962 -0
  29. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/index.rb +9 -0
  30. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/interface.rb +1162 -0
  31. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/loader.rb +3 -0
  32. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mail.rb +578 -0
  33. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mailbox.rb +496 -0
  34. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/main.rb +6 -0
  35. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/mbox.rb +3 -0
  36. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/net.rb +250 -0
  37. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/obsolete.rb +132 -0
  38. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.rb +1060 -0
  39. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.y +416 -0
  40. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/port.rb +379 -0
  41. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/quoting.rb +164 -0
  42. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/require_arch.rb +58 -0
  43. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner.rb +49 -0
  44. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb +262 -0
  45. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/stringio.rb +280 -0
  46. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb +362 -0
  47. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/COPYING +504 -0
  48. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/README +12 -0
  49. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet.rb +67 -0
  50. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5freq.rb +927 -0
  51. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5prober.rb +42 -0
  52. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/chardistribution.rb +238 -0
  53. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetgroupprober.rb +112 -0
  54. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetprober.rb +75 -0
  55. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/codingstatemachine.rb +64 -0
  56. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/constants.rb +42 -0
  57. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escprober.rb +89 -0
  58. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escsm.rb +244 -0
  59. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/eucjpprober.rb +88 -0
  60. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrfreq.rb +596 -0
  61. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrprober.rb +42 -0
  62. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwfreq.rb +430 -0
  63. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwprober.rb +42 -0
  64. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312freq.rb +474 -0
  65. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312prober.rb +42 -0
  66. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/hebrewprober.rb +289 -0
  67. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jisfreq.rb +570 -0
  68. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jpcntx.rb +229 -0
  69. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langbulgarianmodel.rb +229 -0
  70. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langcyrillicmodel.rb +330 -0
  71. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langgreekmodel.rb +227 -0
  72. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhebrewmodel.rb +202 -0
  73. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhungarianmodel.rb +226 -0
  74. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langthaimodel.rb +201 -0
  75. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/latin1prober.rb +147 -0
  76. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcharsetprober.rb +89 -0
  77. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcsgroupprober.rb +45 -0
  78. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcssm.rb +542 -0
  79. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcharsetprober.rb +124 -0
  80. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcsgroupprober.rb +56 -0
  81. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sjisprober.rb +88 -0
  82. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb +168 -0
  83. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/utf8prober.rb +87 -0
  84. data/lib/action_mailer/vendor/tmail-1.2.7/tmail/version.rb +39 -0
  85. data/lib/action_mailer/vendor/tmail.rb +17 -0
  86. data/lib/action_mailer/version.rb +9 -0
  87. data/lib/actionmailer.rb +2 -0
  88. data/test/abstract_unit.rb +62 -0
  89. data/test/asset_host_test.rb +54 -0
  90. data/test/delivery_method_test.rb +51 -0
  91. data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
  92. data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
  93. data/test/fixtures/auto_layout_mailer/multipart.text.html.erb +1 -0
  94. data/test/fixtures/auto_layout_mailer/multipart.text.plain.erb +1 -0
  95. data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
  96. data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
  97. data/test/fixtures/first_mailer/share.erb +1 -0
  98. data/test/fixtures/helper_mailer/use_example_helper.erb +1 -0
  99. data/test/fixtures/helper_mailer/use_helper.erb +1 -0
  100. data/test/fixtures/helper_mailer/use_helper_method.erb +1 -0
  101. data/test/fixtures/helper_mailer/use_mail_helper.erb +5 -0
  102. data/test/fixtures/helpers/example_helper.rb +5 -0
  103. data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
  104. data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
  105. data/test/fixtures/layouts/spam.html.erb +1 -0
  106. data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
  107. data/test/fixtures/raw_email +14 -0
  108. data/test/fixtures/raw_email10 +20 -0
  109. data/test/fixtures/raw_email12 +32 -0
  110. data/test/fixtures/raw_email13 +29 -0
  111. data/test/fixtures/raw_email2 +114 -0
  112. data/test/fixtures/raw_email3 +70 -0
  113. data/test/fixtures/raw_email4 +59 -0
  114. data/test/fixtures/raw_email5 +19 -0
  115. data/test/fixtures/raw_email6 +20 -0
  116. data/test/fixtures/raw_email7 +66 -0
  117. data/test/fixtures/raw_email8 +47 -0
  118. data/test/fixtures/raw_email9 +28 -0
  119. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  120. data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
  121. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  122. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  123. data/test/fixtures/second_mailer/share.erb +1 -0
  124. data/test/fixtures/templates/signed_up.erb +3 -0
  125. data/test/fixtures/test_mailer/_subtemplate.text.plain.erb +1 -0
  126. data/test/fixtures/test_mailer/body_ivar.erb +2 -0
  127. data/test/fixtures/test_mailer/custom_templating_extension.text.html.haml +6 -0
  128. data/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml +6 -0
  129. data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
  130. data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
  131. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb +10 -0
  132. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +10 -0
  133. data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb +2 -0
  134. data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb +1 -0
  135. data/test/fixtures/test_mailer/included_subtemplate.text.plain.erb +1 -0
  136. data/test/fixtures/test_mailer/rxml_template.builder +2 -0
  137. data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
  138. data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
  139. data/test/fixtures/test_mailer/signed_up_with_url.erb +5 -0
  140. data/test/mail_helper_test.rb +95 -0
  141. data/test/mail_layout_test.rb +123 -0
  142. data/test/mail_render_test.rb +116 -0
  143. data/test/mail_service_test.rb +1145 -0
  144. data/test/quoting_test.rb +105 -0
  145. data/test/test_helper_test.rb +129 -0
  146. data/test/tmail_test.rb +22 -0
  147. data/test/url_test.rb +76 -0
  148. metadata +209 -0
@@ -0,0 +1,3 @@
1
+ #:stopdoc:
2
+ require 'tmail/mailbox'
3
+ #:startdoc:
@@ -0,0 +1,578 @@
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
+
392
+ while line = f.gets
393
+ case line
394
+ when /\A[ \t]/ # continue from prev line
395
+ raise SyntaxError, 'mail is began by space' unless field
396
+ field << ' ' << line.strip
397
+
398
+ when /\A([^\: \t]+):\s*/ # new header line
399
+ add_hf name, field if field
400
+ name = $1
401
+ field = $' #.strip
402
+
403
+ when /\A\-*\s*\z/ # end of header
404
+ add_hf name, field if field
405
+ name = field = nil
406
+ break
407
+
408
+ when /\AFrom (\S+)/
409
+ unixfrom = $1
410
+
411
+ when /^charset=.*/
412
+
413
+ else
414
+ raise SyntaxError, "wrong mail header: '#{line.inspect}'"
415
+ end
416
+ end
417
+ add_hf name, field if name
418
+
419
+ if unixfrom
420
+ add_hf 'Return-Path', "<#{unixfrom}>" unless @header['return-path']
421
+ end
422
+ end
423
+
424
+ def add_hf( name, field )
425
+ key = name.downcase
426
+ field = new_hf(name, field)
427
+
428
+ if ALLOW_MULTIPLE.include? key
429
+ (@header[key] ||= []).push field
430
+ else
431
+ @header[key] = field
432
+ end
433
+ end
434
+
435
+ def new_hf( name, field )
436
+ HeaderField.new(name, field, @config)
437
+ end
438
+
439
+ ###
440
+ ### body
441
+ ###
442
+
443
+ public
444
+
445
+ def body_port
446
+ parse_body
447
+ @body_port
448
+ end
449
+
450
+ def each( &block )
451
+ body_port().ropen {|f| f.each(&block) }
452
+ end
453
+
454
+ def quoted_body
455
+ body_port.ropen {|f| return f.read }
456
+ end
457
+
458
+ def quoted_body= str
459
+ body_port.wopen { |f| f.write str }
460
+ str
461
+ end
462
+
463
+ def body=( str )
464
+ # Sets the body of the email to a new (encoded) string.
465
+ #
466
+ # We also reparses the email if the body is ever reassigned, this is a performance hit, however when
467
+ # you assign the body, you usually want to be able to make sure that you can access the attachments etc.
468
+ #
469
+ # Usage:
470
+ #
471
+ # mail.body = "Hello, this is\nthe body text"
472
+ # # => "Hello, this is\nthe body"
473
+ # mail.body
474
+ # # => "Hello, this is\nthe body"
475
+ @body_parsed = false
476
+ parse_body(StringInput.new(str))
477
+ parse_body
478
+ @body_port.wopen {|f| f.write str }
479
+ str
480
+ end
481
+
482
+ alias preamble quoted_body
483
+ alias preamble= quoted_body=
484
+
485
+ def epilogue
486
+ parse_body
487
+ @epilogue.dup
488
+ end
489
+
490
+ def epilogue=( str )
491
+ parse_body
492
+ @epilogue = str
493
+ str
494
+ end
495
+
496
+ def parts
497
+ parse_body
498
+ @parts
499
+ end
500
+
501
+ def each_part( &block )
502
+ parts().each(&block)
503
+ end
504
+
505
+ # Returns true if the content type of this part of the email is
506
+ # a disposition attachment
507
+ def disposition_is_attachment?
508
+ (self['content-disposition'] && self['content-disposition'].disposition == "attachment")
509
+ end
510
+
511
+ # Returns true if this part's content main type is text, else returns false.
512
+ # By main type is meant "text/plain" is text. "text/html" is text
513
+ def content_type_is_text?
514
+ self.header['content-type'] && (self.header['content-type'].main_type != "text")
515
+ end
516
+
517
+ private
518
+
519
+ def parse_body( f = nil )
520
+ return if @body_parsed
521
+ if f
522
+ parse_body_0 f
523
+ else
524
+ @port.ropen {|f|
525
+ skip_header f
526
+ parse_body_0 f
527
+ }
528
+ end
529
+ @body_parsed = true
530
+ end
531
+
532
+ def skip_header( f )
533
+ while line = f.gets
534
+ return if /\A[\r\n]*\z/ === line
535
+ end
536
+ end
537
+
538
+ def parse_body_0( f )
539
+ if multipart?
540
+ read_multipart f
541
+ else
542
+ @body_port = @config.new_body_port(self)
543
+ @body_port.wopen {|w|
544
+ w.write f.read
545
+ }
546
+ end
547
+ end
548
+
549
+ def read_multipart( src )
550
+ bound = @header['content-type'].params['boundary'] || ::TMail.new_boundary
551
+ is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/
552
+ lastbound = "--#{bound}--"
553
+
554
+ ports = [ @config.new_preamble_port(self) ]
555
+ begin
556
+ f = ports.last.wopen
557
+ while line = src.gets
558
+ if is_sep === line
559
+ f.close
560
+ break if line.strip == lastbound
561
+ ports.push @config.new_part_port(self)
562
+ f = ports.last.wopen
563
+ else
564
+ f << line
565
+ end
566
+ end
567
+ @epilogue = (src.read || '')
568
+ ensure
569
+ f.close if f and not f.closed?
570
+ end
571
+
572
+ @body_port = ports.shift
573
+ @parts = ports.map {|p| self.class.new(p, @config) }
574
+ end
575
+
576
+ end # class Mail
577
+
578
+ end # module TMail