otherinbox-mail 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/CHANGELOG.rdoc +607 -0
  2. data/CONTRIBUTING.md +45 -0
  3. data/Dependencies.txt +3 -0
  4. data/Gemfile +26 -0
  5. data/Gemfile.lock +44 -0
  6. data/README.md +663 -0
  7. data/Rakefile +40 -0
  8. data/TODO.rdoc +9 -0
  9. data/lib/VERSION +4 -0
  10. data/lib/mail.rb +101 -0
  11. data/lib/mail/attachments_list.rb +104 -0
  12. data/lib/mail/body.rb +291 -0
  13. data/lib/mail/configuration.rb +75 -0
  14. data/lib/mail/core_extensions/nil.rb +17 -0
  15. data/lib/mail/core_extensions/object.rb +13 -0
  16. data/lib/mail/core_extensions/shell_escape.rb +56 -0
  17. data/lib/mail/core_extensions/smtp.rb +25 -0
  18. data/lib/mail/core_extensions/string.rb +33 -0
  19. data/lib/mail/core_extensions/string/access.rb +145 -0
  20. data/lib/mail/core_extensions/string/multibyte.rb +78 -0
  21. data/lib/mail/elements.rb +14 -0
  22. data/lib/mail/elements/address.rb +306 -0
  23. data/lib/mail/elements/address_list.rb +74 -0
  24. data/lib/mail/elements/content_disposition_element.rb +30 -0
  25. data/lib/mail/elements/content_location_element.rb +25 -0
  26. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  27. data/lib/mail/elements/content_type_element.rb +35 -0
  28. data/lib/mail/elements/date_time_element.rb +26 -0
  29. data/lib/mail/elements/envelope_from_element.rb +34 -0
  30. data/lib/mail/elements/message_ids_element.rb +29 -0
  31. data/lib/mail/elements/mime_version_element.rb +26 -0
  32. data/lib/mail/elements/phrase_list.rb +21 -0
  33. data/lib/mail/elements/received_element.rb +30 -0
  34. data/lib/mail/encodings.rb +274 -0
  35. data/lib/mail/encodings/7bit.rb +31 -0
  36. data/lib/mail/encodings/8bit.rb +31 -0
  37. data/lib/mail/encodings/base64.rb +33 -0
  38. data/lib/mail/encodings/binary.rb +31 -0
  39. data/lib/mail/encodings/quoted_printable.rb +38 -0
  40. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  41. data/lib/mail/envelope.rb +35 -0
  42. data/lib/mail/field.rb +234 -0
  43. data/lib/mail/field_list.rb +33 -0
  44. data/lib/mail/fields.rb +35 -0
  45. data/lib/mail/fields/bcc_field.rb +56 -0
  46. data/lib/mail/fields/cc_field.rb +55 -0
  47. data/lib/mail/fields/comments_field.rb +41 -0
  48. data/lib/mail/fields/common/address_container.rb +16 -0
  49. data/lib/mail/fields/common/common_address.rb +125 -0
  50. data/lib/mail/fields/common/common_date.rb +42 -0
  51. data/lib/mail/fields/common/common_field.rb +51 -0
  52. data/lib/mail/fields/common/common_message_id.rb +44 -0
  53. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  54. data/lib/mail/fields/content_description_field.rb +19 -0
  55. data/lib/mail/fields/content_disposition_field.rb +69 -0
  56. data/lib/mail/fields/content_id_field.rb +63 -0
  57. data/lib/mail/fields/content_location_field.rb +42 -0
  58. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  59. data/lib/mail/fields/content_type_field.rb +198 -0
  60. data/lib/mail/fields/date_field.rb +57 -0
  61. data/lib/mail/fields/from_field.rb +55 -0
  62. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  63. data/lib/mail/fields/keywords_field.rb +44 -0
  64. data/lib/mail/fields/message_id_field.rb +83 -0
  65. data/lib/mail/fields/mime_version_field.rb +53 -0
  66. data/lib/mail/fields/optional_field.rb +13 -0
  67. data/lib/mail/fields/received_field.rb +75 -0
  68. data/lib/mail/fields/references_field.rb +55 -0
  69. data/lib/mail/fields/reply_to_field.rb +55 -0
  70. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  71. data/lib/mail/fields/resent_cc_field.rb +55 -0
  72. data/lib/mail/fields/resent_date_field.rb +35 -0
  73. data/lib/mail/fields/resent_from_field.rb +55 -0
  74. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  75. data/lib/mail/fields/resent_sender_field.rb +62 -0
  76. data/lib/mail/fields/resent_to_field.rb +55 -0
  77. data/lib/mail/fields/return_path_field.rb +65 -0
  78. data/lib/mail/fields/sender_field.rb +67 -0
  79. data/lib/mail/fields/structured_field.rb +51 -0
  80. data/lib/mail/fields/subject_field.rb +16 -0
  81. data/lib/mail/fields/to_field.rb +55 -0
  82. data/lib/mail/fields/unstructured_field.rb +191 -0
  83. data/lib/mail/header.rb +265 -0
  84. data/lib/mail/indifferent_hash.rb +146 -0
  85. data/lib/mail/mail.rb +255 -0
  86. data/lib/mail/matchers/has_sent_mail.rb +124 -0
  87. data/lib/mail/message.rb +2059 -0
  88. data/lib/mail/multibyte.rb +42 -0
  89. data/lib/mail/multibyte/chars.rb +474 -0
  90. data/lib/mail/multibyte/exceptions.rb +8 -0
  91. data/lib/mail/multibyte/unicode.rb +392 -0
  92. data/lib/mail/multibyte/utils.rb +60 -0
  93. data/lib/mail/network.rb +14 -0
  94. data/lib/mail/network/delivery_methods/exim.rb +53 -0
  95. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  96. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  97. data/lib/mail/network/delivery_methods/smtp.rb +153 -0
  98. data/lib/mail/network/delivery_methods/smtp_connection.rb +74 -0
  99. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  100. data/lib/mail/network/retriever_methods/base.rb +63 -0
  101. data/lib/mail/network/retriever_methods/imap.rb +168 -0
  102. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  103. data/lib/mail/network/retriever_methods/test_retriever.rb +47 -0
  104. data/lib/mail/parsers/address_lists.rb +64 -0
  105. data/lib/mail/parsers/address_lists.treetop +19 -0
  106. data/lib/mail/parsers/content_disposition.rb +535 -0
  107. data/lib/mail/parsers/content_disposition.treetop +46 -0
  108. data/lib/mail/parsers/content_location.rb +139 -0
  109. data/lib/mail/parsers/content_location.treetop +20 -0
  110. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  111. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  112. data/lib/mail/parsers/content_type.rb +967 -0
  113. data/lib/mail/parsers/content_type.treetop +68 -0
  114. data/lib/mail/parsers/date_time.rb +114 -0
  115. data/lib/mail/parsers/date_time.treetop +11 -0
  116. data/lib/mail/parsers/envelope_from.rb +194 -0
  117. data/lib/mail/parsers/envelope_from.treetop +32 -0
  118. data/lib/mail/parsers/message_ids.rb +45 -0
  119. data/lib/mail/parsers/message_ids.treetop +15 -0
  120. data/lib/mail/parsers/mime_version.rb +144 -0
  121. data/lib/mail/parsers/mime_version.treetop +19 -0
  122. data/lib/mail/parsers/phrase_lists.rb +45 -0
  123. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  124. data/lib/mail/parsers/received.rb +71 -0
  125. data/lib/mail/parsers/received.treetop +11 -0
  126. data/lib/mail/parsers/rfc2045.rb +464 -0
  127. data/lib/mail/parsers/rfc2045.treetop +36 -0
  128. data/lib/mail/parsers/rfc2822.rb +5341 -0
  129. data/lib/mail/parsers/rfc2822.treetop +410 -0
  130. data/lib/mail/parsers/rfc2822_obsolete.rb +3768 -0
  131. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  132. data/lib/mail/part.rb +116 -0
  133. data/lib/mail/parts_list.rb +55 -0
  134. data/lib/mail/patterns.rb +34 -0
  135. data/lib/mail/utilities.rb +215 -0
  136. data/lib/mail/version.rb +24 -0
  137. data/lib/mail/version_specific/ruby_1_8.rb +98 -0
  138. data/lib/mail/version_specific/ruby_1_9.rb +113 -0
  139. data/lib/tasks/corpus.rake +125 -0
  140. data/lib/tasks/treetop.rake +10 -0
  141. metadata +253 -0
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ begin
2
+ require "rubygems"
3
+ require "bundler"
4
+ rescue LoadError
5
+ raise "Could not load the bundler gem. Install it with `gem install bundler`."
6
+ end
7
+
8
+ if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("1.0.0")
9
+ raise RuntimeError, "Your bundler version is too old for Mail" +
10
+ "Run `gem install bundler` to upgrade."
11
+ end
12
+
13
+ begin
14
+ # Set up load paths for all bundled gems
15
+ ENV["BUNDLE_GEMFILE"] = File.expand_path("../Gemfile", __FILE__)
16
+ Bundler.setup
17
+ rescue Bundler::GemNotFound
18
+ raise RuntimeError, "Bundler couldn't find some gems." +
19
+ "Did you run `bundle install`?"
20
+ end
21
+
22
+ require File.expand_path('../spec/environment', __FILE__)
23
+
24
+ require 'rake/testtask'
25
+ require 'rspec/core/rake_task'
26
+
27
+ desc "Build a gem file"
28
+ task :build do
29
+ system "gem build mail.gemspec"
30
+ end
31
+
32
+ task :default => :spec
33
+
34
+ RSpec::Core::RakeTask.new(:spec) do |t|
35
+ t.ruby_opts = '-w'
36
+ t.rspec_opts = %w(--backtrace --color)
37
+ end
38
+
39
+ # load custom rake tasks
40
+ Dir["#{File.dirname(__FILE__)}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
data/TODO.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ == Not really in any order:
2
+
3
+ * Add multibyte handling to fields, if they get a multibyte string, try encoding it into
4
+ UTF-8 B first, if this fails, throw an error.
5
+
6
+ * Cleanup the treetop parsers......... do I _really_ need that many entrance files?
7
+
8
+ * Simplify the relationship of Headers and Fields. Doing too much of the Field work
9
+ in the Header class on instantiating fields. Header should just say "Field, do it!"
data/lib/VERSION ADDED
@@ -0,0 +1,4 @@
1
+ major:2
2
+ minor:4
3
+ patch:4
4
+ build:
data/lib/mail.rb ADDED
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+ module Mail # :doc:
3
+
4
+ require 'date'
5
+ require 'shellwords'
6
+
7
+ require 'uri'
8
+ require 'net/smtp'
9
+ require 'mime/types'
10
+
11
+ if RUBY_VERSION <= '1.8.6'
12
+ begin
13
+ require 'tlsmail'
14
+ rescue LoadError
15
+ raise "You need to install tlsmail if you are using ruby <= 1.8.6"
16
+ end
17
+ end
18
+
19
+ if RUBY_VERSION >= "1.9.0"
20
+ require 'mail/version_specific/ruby_1_9'
21
+ RubyVer = Ruby19
22
+ else
23
+ require 'mail/version_specific/ruby_1_8'
24
+ RubyVer = Ruby18
25
+ end
26
+
27
+ require 'mail/version'
28
+
29
+ require 'mail/core_extensions/nil'
30
+ require 'mail/core_extensions/object'
31
+ require 'mail/core_extensions/string'
32
+ require 'mail/core_extensions/shell_escape'
33
+ require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
34
+ require 'mail/indifferent_hash'
35
+
36
+ # Only load our multibyte extensions if AS is not already loaded
37
+ if defined?(ActiveSupport)
38
+ require 'active_support/inflector'
39
+ else
40
+ require 'mail/core_extensions/string/access'
41
+ require 'mail/core_extensions/string/multibyte'
42
+ require 'mail/multibyte'
43
+ end
44
+
45
+ require 'mail/patterns'
46
+ require 'mail/utilities'
47
+ require 'mail/configuration'
48
+
49
+ @@autoloads = {}
50
+ def self.register_autoload(name, path)
51
+ @@autoloads[name] = path
52
+ autoload(name, path)
53
+ end
54
+
55
+ def self.eager_autoload!
56
+ @@autoloads.each { |_,path| require(path) }
57
+ end
58
+
59
+ # Autoload mail send and receive classes.
60
+ require 'mail/network'
61
+
62
+ require 'mail/message'
63
+ require 'mail/part'
64
+ require 'mail/header'
65
+ require 'mail/parts_list'
66
+ require 'mail/attachments_list'
67
+ require 'mail/body'
68
+ require 'mail/field'
69
+ require 'mail/field_list'
70
+
71
+ require 'mail/envelope'
72
+
73
+ parsers = %w[ rfc2822_obsolete rfc2822 address_lists phrase_lists
74
+ date_time received message_ids envelope_from rfc2045
75
+ mime_version content_type content_disposition
76
+ content_transfer_encoding content_location ]
77
+
78
+ parsers.each do |parser|
79
+ begin
80
+ # Try requiring the pre-compiled ruby version first
81
+ require 'treetop/runtime'
82
+ require "mail/parsers/#{parser}"
83
+ rescue LoadError
84
+ # Otherwise, get treetop to compile and load it
85
+ require 'treetop/runtime'
86
+ require 'treetop/compiler'
87
+ Treetop.load(File.join(File.dirname(__FILE__)) + "/mail/parsers/#{parser}")
88
+ end
89
+ end
90
+
91
+ # Autoload header field elements and transfer encodings.
92
+ require 'mail/elements'
93
+ require 'mail/encodings'
94
+ require 'mail/encodings/base64'
95
+ require 'mail/encodings/quoted_printable'
96
+
97
+ require 'mail/matchers/has_sent_mail'
98
+
99
+ # Finally... require all the Mail.methods
100
+ require 'mail/mail'
101
+ end
@@ -0,0 +1,104 @@
1
+ module Mail
2
+ class AttachmentsList < Array
3
+
4
+ def initialize(parts_list)
5
+ @parts_list = parts_list
6
+ @content_disposition_type = 'attachment'
7
+ parts_list.map { |p|
8
+ if p.content_type == "message/rfc822"
9
+ Mail.new(p.body).attachments
10
+ elsif p.parts.empty?
11
+ p if p.attachment?
12
+ else
13
+ p.attachments
14
+ end
15
+ }.flatten.compact.each { |a| self << a }
16
+ self
17
+ end
18
+
19
+ def inline
20
+ @content_disposition_type = 'inline'
21
+ self
22
+ end
23
+
24
+ # Returns the attachment by filename or at index.
25
+ #
26
+ # mail.attachments['test.png'] = File.read('test.png')
27
+ # mail.attachments['test.jpg'] = File.read('test.jpg')
28
+ #
29
+ # mail.attachments['test.png'].filename #=> 'test.png'
30
+ # mail.attachments[1].filename #=> 'test.jpg'
31
+ def [](index_value)
32
+ if index_value.is_a?(Fixnum)
33
+ self.fetch(index_value)
34
+ else
35
+ self.select { |a| a.filename == index_value }.first
36
+ end
37
+ end
38
+
39
+ def []=(name, value)
40
+ encoded_name = Mail::Encodings.decode_encode name, :encode
41
+ default_values = { :content_type => "#{set_mime_type(name)}; filename=\"#{encoded_name}\"",
42
+ :content_transfer_encoding => "#{guess_encoding}",
43
+ :content_disposition => "#{@content_disposition_type}; filename=\"#{encoded_name}\"" }
44
+
45
+ if value.is_a?(Hash)
46
+
47
+ default_values[:body] = value.delete(:content) if value[:content]
48
+
49
+ default_values[:body] = value.delete(:data) if value[:data]
50
+
51
+ encoding = value.delete(:transfer_encoding) || value.delete(:encoding)
52
+ if encoding
53
+ if Mail::Encodings.defined? encoding
54
+ default_values[:content_transfer_encoding] = encoding
55
+ else
56
+ raise "Do not know how to handle Content Transfer Encoding #{encoding}, please choose either quoted-printable or base64"
57
+ end
58
+ end
59
+
60
+ if value[:mime_type]
61
+ default_values[:content_type] = value.delete(:mime_type)
62
+ @mime_type = MIME::Types[default_values[:content_type]].first
63
+ default_values[:content_transfer_encoding] = guess_encoding
64
+ end
65
+
66
+ hash = default_values.merge(value)
67
+ else
68
+ default_values[:body] = value
69
+ hash = default_values
70
+ end
71
+
72
+ if hash[:body].respond_to? :force_encoding and hash[:body].respond_to? :valid_encoding?
73
+ if not hash[:body].valid_encoding? and default_values[:content_transfer_encoding].downcase == "binary"
74
+ hash[:body].force_encoding("BINARY")
75
+ end
76
+ end
77
+
78
+ attachment = Part.new(hash)
79
+ attachment.add_content_id(hash[:content_id])
80
+
81
+ @parts_list << attachment
82
+ end
83
+
84
+ # Uses the mime type to try and guess the encoding, if it is a binary type, or unknown, then we
85
+ # set it to binary, otherwise as set to plain text
86
+ def guess_encoding
87
+ if @mime_type && !@mime_type.binary?
88
+ "7bit"
89
+ else
90
+ "binary"
91
+ end
92
+ end
93
+
94
+ def set_mime_type(filename)
95
+ # Have to do this because MIME::Types is not Ruby 1.9 safe yet
96
+ if RUBY_VERSION >= '1.9'
97
+ filename = filename.encode(Encoding::UTF_8) if filename.respond_to?(:encode)
98
+ end
99
+
100
+ @mime_type = MIME::Types.type_for(filename).first
101
+ end
102
+
103
+ end
104
+ end
data/lib/mail/body.rb ADDED
@@ -0,0 +1,291 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # = Body
5
+ #
6
+ # The body is where the text of the email is stored. Mail treats the body
7
+ # as a single object. The body itself has no information about boundaries
8
+ # used in the MIME standard, it just looks at it's content as either a single
9
+ # block of text, or (if it is a multipart message) as an array of blocks of text.
10
+ #
11
+ # A body has to be told to split itself up into a multipart message by calling
12
+ # #split with the correct boundary. This is because the body object has no way
13
+ # of knowing what the correct boundary is for itself (there could be many
14
+ # boundaries in a body in the case of a nested MIME text).
15
+ #
16
+ # Once split is called, Mail::Body will slice itself up on this boundary,
17
+ # assigning anything that appears before the first part to the preamble, and
18
+ # anything that appears after the closing boundary to the epilogue, then
19
+ # each part gets initialized into a Mail::Part object.
20
+ #
21
+ # The boundary that is used to split up the Body is also stored in the Body
22
+ # object for use on encoding itself back out to a string. You can
23
+ # overwrite this if it needs to be changed.
24
+ #
25
+ # On encoding, the body will return the preamble, then each part joined by
26
+ # the boundary, followed by a closing boundary string and then the epilogue.
27
+ class Body
28
+
29
+ def initialize(string = '')
30
+ @boundary = nil
31
+ @preamble = nil
32
+ @epilogue = nil
33
+ @charset = nil
34
+ @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
35
+ @parts = Mail::PartsList.new
36
+ if string.blank?
37
+ @raw_source = ''
38
+ else
39
+ # Do join first incase we have been given an Array in Ruby 1.9
40
+ if string.respond_to?(:join)
41
+ @raw_source = string.join('')
42
+ elsif string.respond_to?(:to_s)
43
+ @raw_source = string.to_s
44
+ else
45
+ raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
46
+ end
47
+ end
48
+ @encoding = (only_us_ascii? ? '7bit' : '8bit')
49
+ set_charset
50
+ end
51
+
52
+ # Matches this body with another body. Also matches the decoded value of this
53
+ # body with a string.
54
+ #
55
+ # Examples:
56
+ #
57
+ # body = Mail::Body.new('The body')
58
+ # body == body #=> true
59
+ #
60
+ # body = Mail::Body.new('The body')
61
+ # body == 'The body' #=> true
62
+ #
63
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
64
+ # body.encoding = 'base64'
65
+ # body == "The body" #=> true
66
+ def ==(other)
67
+ if other.class == String
68
+ self.decoded == other
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ # Accepts a string and performs a regular expression against the decoded text
75
+ #
76
+ # Examples:
77
+ #
78
+ # body = Mail::Body.new('The body')
79
+ # body =~ /The/ #=> 0
80
+ #
81
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
82
+ # body.encoding = 'base64'
83
+ # body =~ /The/ #=> 0
84
+ def =~(regexp)
85
+ self.decoded =~ regexp
86
+ end
87
+
88
+ # Accepts a string and performs a regular expression against the decoded text
89
+ #
90
+ # Examples:
91
+ #
92
+ # body = Mail::Body.new('The body')
93
+ # body.match(/The/) #=> #<MatchData "The">
94
+ #
95
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
96
+ # body.encoding = 'base64'
97
+ # body.match(/The/) #=> #<MatchData "The">
98
+ def match(regexp)
99
+ self.decoded.match(regexp)
100
+ end
101
+
102
+ # Accepts anything that responds to #to_s and checks if it's a substring of the decoded text
103
+ #
104
+ # Examples:
105
+ #
106
+ # body = Mail::Body.new('The body')
107
+ # body.include?('The') #=> true
108
+ #
109
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
110
+ # body.encoding = 'base64'
111
+ # body.include?('The') #=> true
112
+ def include?(other)
113
+ self.decoded.include?(other.to_s)
114
+ end
115
+
116
+ # Allows you to set the sort order of the parts, overriding the default sort order.
117
+ # Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
118
+ # type coming after.
119
+ def set_sort_order(order)
120
+ @part_sort_order = order
121
+ end
122
+
123
+ # Allows you to sort the parts according to the default sort order, or the sort order you
124
+ # set with :set_sort_order.
125
+ #
126
+ # sort_parts! is also called from :encode, so there is no need for you to call this explicitly
127
+ def sort_parts!
128
+ @parts.each do |p|
129
+ p.body.set_sort_order(@part_sort_order)
130
+ @parts.sort!(@part_sort_order)
131
+ p.body.sort_parts!
132
+ end
133
+ end
134
+
135
+ # Returns the raw source that the body was initialized with, without
136
+ # any tampering
137
+ def raw_source
138
+ @raw_source
139
+ end
140
+
141
+ def get_best_encoding(target)
142
+ target_encoding = Mail::Encodings.get_encoding(target)
143
+ target_encoding.get_best_compatible(encoding, raw_source)
144
+ end
145
+
146
+ # Returns a body encoded using transfer_encoding. Multipart always uses an
147
+ # identiy encoding (i.e. no encoding).
148
+ # Calling this directly is not a good idea, but supported for compatibility
149
+ # TODO: Validate that preamble and epilogue are valid for requested encoding
150
+ def encoded(transfer_encoding = '8bit')
151
+ if multipart?
152
+ self.sort_parts!
153
+ encoded_parts = parts.map { |p| p.encoded }
154
+ ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
155
+ else
156
+ be = get_best_encoding(transfer_encoding)
157
+ dec = Mail::Encodings::get_encoding(encoding)
158
+ enc = Mail::Encodings::get_encoding(be)
159
+ if transfer_encoding == encoding and dec.nil?
160
+ # Cannot decode, so skip normalization
161
+ raw_source
162
+ else
163
+ # Decode then encode to normalize and allow transforming
164
+ # from base64 to Q-P and vice versa
165
+ decoded = dec.decode(raw_source)
166
+ if defined?(Encoding) && charset && charset != "US-ASCII"
167
+ decoded.encode!(charset)
168
+ decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
169
+ end
170
+ enc.encode(decoded)
171
+ end
172
+ end
173
+ end
174
+
175
+ def decoded
176
+ if !Encodings.defined?(encoding)
177
+ raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
178
+ else
179
+ Encodings.get_encoding(encoding).decode(raw_source)
180
+ end
181
+ end
182
+
183
+ def to_s
184
+ decoded
185
+ end
186
+
187
+ def charset
188
+ @charset
189
+ end
190
+
191
+ def charset=( val )
192
+ @charset = val
193
+ end
194
+
195
+ def encoding(val = nil)
196
+ if val
197
+ self.encoding = val
198
+ else
199
+ @encoding
200
+ end
201
+ end
202
+
203
+ def encoding=( val )
204
+ @encoding = if val == "text" || val.blank?
205
+ (only_us_ascii? ? '7bit' : '8bit')
206
+ else
207
+ val
208
+ end
209
+ end
210
+
211
+ # Returns the preamble (any text that is before the first MIME boundary)
212
+ def preamble
213
+ @preamble
214
+ end
215
+
216
+ # Sets the preamble to a string (adds text before the first MIME boundary)
217
+ def preamble=( val )
218
+ @preamble = val
219
+ end
220
+
221
+ # Returns the epilogue (any text that is after the last MIME boundary)
222
+ def epilogue
223
+ @epilogue
224
+ end
225
+
226
+ # Sets the epilogue to a string (adds text after the last MIME boundary)
227
+ def epilogue=( val )
228
+ @epilogue = val
229
+ end
230
+
231
+ # Returns true if there are parts defined in the body
232
+ def multipart?
233
+ true unless parts.empty?
234
+ end
235
+
236
+ # Returns the boundary used by the body
237
+ def boundary
238
+ @boundary
239
+ end
240
+
241
+ # Allows you to change the boundary of this Body object
242
+ def boundary=( val )
243
+ @boundary = val
244
+ end
245
+
246
+ def parts
247
+ @parts
248
+ end
249
+
250
+ def <<( val )
251
+ if @parts
252
+ @parts << val
253
+ else
254
+ @parts = Mail::PartsList.new[val]
255
+ end
256
+ end
257
+
258
+ def split!(boundary)
259
+ self.boundary = boundary
260
+ parts = raw_source.split("--#{boundary}")
261
+ # Make the preamble equal to the preamble (if any)
262
+ self.preamble = parts[0].to_s.strip
263
+ # Make the epilogue equal to the epilogue (if any)
264
+ self.epilogue = parts[-1].to_s.sub('--', '').strip
265
+ parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
266
+ self
267
+ end
268
+
269
+ def only_us_ascii?
270
+ !(raw_source =~ /[^\x01-\x7f]/)
271
+ end
272
+
273
+ def empty?
274
+ !!raw_source.to_s.empty?
275
+ end
276
+
277
+ private
278
+
279
+ def crlf_boundary
280
+ "\r\n\r\n--#{boundary}\r\n"
281
+ end
282
+
283
+ def end_boundary
284
+ "\r\n\r\n--#{boundary}--\r\n"
285
+ end
286
+
287
+ def set_charset
288
+ only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
289
+ end
290
+ end
291
+ end