ledermann-tmail 1.2.5

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 (164) hide show
  1. data/CHANGES +74 -0
  2. data/LICENSE +21 -0
  3. data/NOTES +7 -0
  4. data/README +179 -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 +614 -0
  11. data/lib/tmail.rb +6 -0
  12. data/lib/tmail/Makefile +18 -0
  13. data/lib/tmail/address.rb +392 -0
  14. data/lib/tmail/attachments.rb +65 -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 +962 -0
  21. data/lib/tmail/index.rb +9 -0
  22. data/lib/tmail/interface.rb +1162 -0
  23. data/lib/tmail/loader.rb +3 -0
  24. data/lib/tmail/mail.rb +578 -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 +155 -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 +361 -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 +272 -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_illegal_boundary +58 -0
  141. data/test/fixtures/raw_email_with_mimepart_without_content_type +94 -0
  142. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  143. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  144. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  145. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  146. data/test/kcode.rb +14 -0
  147. data/test/temp_test_one.rb +46 -0
  148. data/test/test_address.rb +1216 -0
  149. data/test/test_attachments.rb +131 -0
  150. data/test/test_base64.rb +64 -0
  151. data/test/test_encode.rb +139 -0
  152. data/test/test_header.rb +1021 -0
  153. data/test/test_helper.rb +9 -0
  154. data/test/test_mail.rb +756 -0
  155. data/test/test_mbox.rb +184 -0
  156. data/test/test_port.rb +440 -0
  157. data/test/test_quote.rb +107 -0
  158. data/test/test_scanner.rb +209 -0
  159. data/test/test_utils.rb +36 -0
  160. data/work/script/make +26 -0
  161. data/work/script/rdoc +39 -0
  162. data/work/script/setup +1616 -0
  163. data/work/script/test +30 -0
  164. metadata +297 -0
data/lib/tmail.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'tmail/version'
2
+ require 'tmail/mail'
3
+ require 'tmail/mailbox'
4
+ require 'tmail/core_extensions'
5
+ require 'tmail/net'
6
+ require 'vendor/rchardet-1.3/lib/rchardet'
@@ -0,0 +1,18 @@
1
+ # lib/tmail/Makefile
2
+ #
3
+
4
+ debug:
5
+ rm -f parser.rb
6
+ make parser.rb DEBUG=true
7
+
8
+ parser.rb: parser.y
9
+ if [ "$(DEBUG)" = true ]; then \
10
+ racc -v -g -o$@ parser.y ;\
11
+ else \
12
+ racc -E -o$@ parser.y ;\
13
+ fi
14
+
15
+ clean:
16
+ rm -f parser.rb parser.output
17
+
18
+ distclean: clean
@@ -0,0 +1,392 @@
1
+ =begin rdoc
2
+
3
+ = Address handling class
4
+
5
+ =end
6
+ #--
7
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ #
28
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29
+ # with permission of Minero Aoki.
30
+ #++
31
+
32
+ require 'tmail/encode'
33
+ require 'tmail/parser'
34
+
35
+
36
+ module TMail
37
+
38
+ # = Class Address
39
+ #
40
+ # Provides a complete handling library for email addresses. Can parse a string of an
41
+ # address directly or take in preformatted addresses themselves. Allows you to add
42
+ # and remove phrases from the front of the address and provides a compare function for
43
+ # email addresses.
44
+ #
45
+ # == Parsing and Handling a Valid Address:
46
+ #
47
+ # Just pass the email address in as a string to Address.parse:
48
+ #
49
+ # email = TMail::Address.parse('Mikel Lindsaar <mikel@lindsaar.net>')
50
+ # #=> #<TMail::Address mikel@lindsaar.net>
51
+ # email.address
52
+ # #=> "mikel@lindsaar.net"
53
+ # email.local
54
+ # #=> "mikel"
55
+ # email.domain
56
+ # #=> "lindsaar.net"
57
+ # email.name # Aliased as phrase as well
58
+ # #=> "Mikel Lindsaar"
59
+ #
60
+ # == Detecting an Invalid Address
61
+ #
62
+ # If you want to check the syntactical validity of an email address, just pass it to
63
+ # Address.parse and catch any SyntaxError:
64
+ #
65
+ # begin
66
+ # TMail::Address.parse("mikel 2@@@@@ me .com")
67
+ # rescue TMail::SyntaxError
68
+ # puts("Invalid Email Address Detected")
69
+ # else
70
+ # puts("Address is valid")
71
+ # end
72
+ # #=> "Invalid Email Address Detected"
73
+ class Address
74
+
75
+ include TextUtils #:nodoc:
76
+
77
+ # Sometimes you need to parse an address, TMail can do it for you and provide you with
78
+ # a fairly robust method of detecting a valid address.
79
+ #
80
+ # Takes in a string, returns a TMail::Address object.
81
+ #
82
+ # Raises a TMail::SyntaxError on invalid email format
83
+ def Address.parse( str )
84
+ Parser.parse :ADDRESS, str
85
+ end
86
+
87
+ def address_group? #:nodoc:
88
+ false
89
+ end
90
+
91
+ # Address.new(local, domain)
92
+ #
93
+ # Accepts:
94
+ #
95
+ # * local - Left of the at symbol
96
+ #
97
+ # * domain - Array of the domain split at the periods.
98
+ #
99
+ # For example:
100
+ #
101
+ # Address.new("mikel", ["lindsaar", "net"])
102
+ # #=> "#<TMail::Address mikel@lindsaar.net>"
103
+ def initialize( local, domain )
104
+ if domain
105
+ domain.each do |s|
106
+ raise SyntaxError, 'empty word in domain' if s.empty?
107
+ end
108
+ end
109
+
110
+ # This is to catch an unquoted "@" symbol in the local part of the
111
+ # address. Handles addresses like <"@"@me.com> and makes sure they
112
+ # stay like <"@"@me.com> (previously were becoming <@@me.com>)
113
+ if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
114
+ @local = "\"#{local.join}\""
115
+ else
116
+ @local = local
117
+ end
118
+
119
+ @domain = domain
120
+ @name = nil
121
+ @routes = []
122
+ end
123
+
124
+ # Provides the name or 'phrase' of the email address.
125
+ #
126
+ # For Example:
127
+ #
128
+ # email = TMail::Address.parse("Mikel Lindsaar <mikel@lindsaar.net>")
129
+ # email.name
130
+ # #=> "Mikel Lindsaar"
131
+ def name
132
+ @name
133
+ end
134
+
135
+ # Setter method for the name or phrase of the email
136
+ #
137
+ # For Example:
138
+ #
139
+ # email = TMail::Address.parse("mikel@lindsaar.net")
140
+ # email.name
141
+ # #=> nil
142
+ # email.name = "Mikel Lindsaar"
143
+ # email.to_s
144
+ # #=> "Mikel Lindsaar <mikel@me.com>"
145
+ def name=( str )
146
+ @name = str
147
+ @name = nil if str and str.empty?
148
+ end
149
+
150
+ #:stopdoc:
151
+ alias phrase name
152
+ alias phrase= name=
153
+ #:startdoc:
154
+
155
+ # This is still here from RFC 822, and is now obsolete per RFC2822 Section 4.
156
+ #
157
+ # "When interpreting addresses, the route portion SHOULD be ignored."
158
+ #
159
+ # It is still here, so you can access it.
160
+ #
161
+ # Routes return the route portion at the front of the email address, if any.
162
+ #
163
+ # For Example:
164
+ # email = TMail::Address.parse( "<@sa,@another:Mikel@me.com>")
165
+ # => #<TMail::Address Mikel@me.com>
166
+ # email.to_s
167
+ # => "<@sa,@another:Mikel@me.com>"
168
+ # email.routes
169
+ # => ["sa", "another"]
170
+ def routes
171
+ @routes
172
+ end
173
+
174
+ def inspect #:nodoc:
175
+ "#<#{self.class} #{address()}>"
176
+ end
177
+
178
+ # Returns the local part of the email address
179
+ #
180
+ # For Example:
181
+ #
182
+ # email = TMail::Address.parse("mikel@lindsaar.net")
183
+ # email.local
184
+ # #=> "mikel"
185
+ def local
186
+ return nil unless @local
187
+ return '""' if @local.size == 1 and @local[0].empty?
188
+ # Check to see if it is an array before trying to map it
189
+ if @local.respond_to?(:map)
190
+ @local.map {|i| quote_atom(i) }.join('.')
191
+ else
192
+ quote_atom(@local)
193
+ end
194
+ end
195
+
196
+ # Returns the domain part of the email address
197
+ #
198
+ # For Example:
199
+ #
200
+ # email = TMail::Address.parse("mikel@lindsaar.net")
201
+ # email.local
202
+ # #=> "lindsaar.net"
203
+ def domain
204
+ return nil unless @domain
205
+ join_domain(@domain)
206
+ end
207
+
208
+ # Returns the full specific address itself
209
+ #
210
+ # For Example:
211
+ #
212
+ # email = TMail::Address.parse("mikel@lindsaar.net")
213
+ # email.address
214
+ # #=> "mikel@lindsaar.net"
215
+ def spec
216
+ s = self.local
217
+ d = self.domain
218
+ if s and d
219
+ s + '@' + d
220
+ else
221
+ s
222
+ end
223
+ end
224
+
225
+ alias address spec
226
+
227
+ # Provides == function to the email. Only checks the actual address
228
+ # and ignores the name/phrase component
229
+ #
230
+ # For Example
231
+ #
232
+ # addr1 = TMail::Address.parse("My Address <mikel@lindsaar.net>")
233
+ # #=> "#<TMail::Address mikel@lindsaar.net>"
234
+ # addr2 = TMail::Address.parse("Another <mikel@lindsaar.net>")
235
+ # #=> "#<TMail::Address mikel@lindsaar.net>"
236
+ # addr1 == addr2
237
+ # #=> true
238
+ def ==( other )
239
+ other.respond_to? :spec and self.spec == other.spec
240
+ end
241
+
242
+ alias eql? ==
243
+
244
+ # Provides a unique hash value for this record against the local and domain
245
+ # parts, ignores the name/phrase value
246
+ #
247
+ # email = TMail::Address.parse("mikel@lindsaar.net")
248
+ # email.hash
249
+ # #=> 18767598
250
+ def hash
251
+ @local.hash ^ @domain.hash
252
+ end
253
+
254
+ # Duplicates a TMail::Address object returning the duplicate
255
+ #
256
+ # addr1 = TMail::Address.parse("mikel@lindsaar.net")
257
+ # addr2 = addr1.dup
258
+ # addr1.id == addr2.id
259
+ # #=> false
260
+ def dup
261
+ obj = self.class.new(@local.dup, @domain.dup)
262
+ obj.name = @name.dup if @name
263
+ obj.routes.replace @routes
264
+ obj
265
+ end
266
+
267
+ include StrategyInterface #:nodoc:
268
+
269
+ def accept( strategy, dummy1 = nil, dummy2 = nil ) #:nodoc:
270
+ unless @local
271
+ strategy.meta '<>' # empty return-path
272
+ return
273
+ end
274
+
275
+ spec_p = (not @name and @routes.empty?)
276
+ if @name
277
+ strategy.phrase @name
278
+ strategy.space
279
+ end
280
+ tmp = spec_p ? '' : '<'
281
+ unless @routes.empty?
282
+ tmp << @routes.map {|i| '@' + i }.join(',') << ':'
283
+ end
284
+ tmp << self.spec
285
+ tmp << '>' unless spec_p
286
+ strategy.meta tmp
287
+ strategy.lwsp ''
288
+ end
289
+
290
+ end
291
+
292
+
293
+ class AddressGroup
294
+
295
+ include Enumerable
296
+
297
+ def address_group?
298
+ true
299
+ end
300
+
301
+ def initialize( name, addrs )
302
+ @name = name
303
+ @addresses = addrs
304
+ end
305
+
306
+ attr_reader :name
307
+
308
+ def ==( other )
309
+ other.respond_to? :to_a and @addresses == other.to_a
310
+ end
311
+
312
+ alias eql? ==
313
+
314
+ def hash
315
+ map {|i| i.hash }.hash
316
+ end
317
+
318
+ def []( idx )
319
+ @addresses[idx]
320
+ end
321
+
322
+ def size
323
+ @addresses.size
324
+ end
325
+
326
+ def empty?
327
+ @addresses.empty?
328
+ end
329
+
330
+ def each( &block )
331
+ @addresses.each(&block)
332
+ end
333
+
334
+ def to_a
335
+ @addresses.dup
336
+ end
337
+
338
+ alias to_ary to_a
339
+
340
+ def include?( a )
341
+ @addresses.include? a
342
+ end
343
+
344
+ def flatten
345
+ set = []
346
+ @addresses.each do |a|
347
+ if a.respond_to? :flatten
348
+ set.concat a.flatten
349
+ else
350
+ set.push a
351
+ end
352
+ end
353
+ set
354
+ end
355
+
356
+ def each_address( &block )
357
+ flatten.each(&block)
358
+ end
359
+
360
+ def add( a )
361
+ @addresses.push a
362
+ end
363
+
364
+ alias push add
365
+
366
+ def delete( a )
367
+ @addresses.delete a
368
+ end
369
+
370
+ include StrategyInterface
371
+
372
+ def accept( strategy, dummy1 = nil, dummy2 = nil )
373
+ strategy.phrase @name
374
+ strategy.meta ':'
375
+ strategy.space
376
+ first = true
377
+ each do |mbox|
378
+ if first
379
+ first = false
380
+ else
381
+ strategy.puts_meta ','
382
+ end
383
+ strategy.space
384
+ mbox.accept strategy
385
+ end
386
+ strategy.meta ';'
387
+ strategy.lwsp ''
388
+ end
389
+
390
+ end
391
+
392
+ end # module TMail
@@ -0,0 +1,65 @@
1
+ =begin rdoc
2
+
3
+ = Attachment handling file
4
+
5
+ =end
6
+
7
+ require 'kconv'
8
+ require 'stringio'
9
+
10
+ module TMail
11
+ class Attachment < StringIO
12
+ attr_accessor :original_filename, :content_type
13
+ alias quoted_filename original_filename
14
+ end
15
+
16
+ class Mail
17
+ def has_attachments?
18
+ attachment?(self) || multipart? && parts.any? { |part| attachment?(part) }
19
+ end
20
+
21
+ # Returns true if this part's content main type is text, else returns false.
22
+ # By main type is meant "text/plain" is text. "text/html" is text
23
+ def text_content_type?
24
+ self.header['content-type'] && (self.header['content-type'].main_type == 'text')
25
+ end
26
+
27
+ def inline_attachment?(part)
28
+ part['content-id'] || (part['content-disposition'] && part['content-disposition'].disposition == 'inline' && !part.text_content_type?)
29
+ end
30
+
31
+ def attachment?(part)
32
+ part.disposition_is_attachment? || (!part.content_type.nil? && !part.text_content_type?) unless part.multipart?
33
+ end
34
+
35
+ def attachments
36
+ if multipart?
37
+ parts.collect { |part| attachment(part) }.flatten.compact
38
+ elsif attachment?(self)
39
+ [attachment(self)]
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def attachment(part)
46
+ if part.multipart?
47
+ part.attachments
48
+ elsif attachment?(part)
49
+ content = part.body # unquoted automatically by TMail#body
50
+ file_name = (part['content-location'] && part['content-location'].body) ||
51
+ part.sub_header('content-type', 'name') ||
52
+ part.sub_header('content-disposition', 'filename') ||
53
+ 'noname'
54
+
55
+ return if content.blank?
56
+
57
+ attachment = TMail::Attachment.new(content)
58
+ attachment.original_filename = file_name.strip unless file_name.blank?
59
+ attachment.content_type = part.content_type
60
+ attachment
61
+ end
62
+ end
63
+
64
+ end
65
+ end