mail-portertech 2.6.2.edge

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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +753 -0
  3. data/CONTRIBUTING.md +60 -0
  4. data/Dependencies.txt +2 -0
  5. data/Gemfile +15 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +683 -0
  8. data/Rakefile +29 -0
  9. data/TODO.rdoc +9 -0
  10. data/lib/mail.rb +91 -0
  11. data/lib/mail/attachments_list.rb +104 -0
  12. data/lib/mail/body.rb +291 -0
  13. data/lib/mail/check_delivery_params.rb +20 -0
  14. data/lib/mail/configuration.rb +75 -0
  15. data/lib/mail/core_extensions/nil.rb +19 -0
  16. data/lib/mail/core_extensions/object.rb +13 -0
  17. data/lib/mail/core_extensions/smtp.rb +24 -0
  18. data/lib/mail/core_extensions/string.rb +43 -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 +270 -0
  23. data/lib/mail/elements/address_list.rb +51 -0
  24. data/lib/mail/elements/content_disposition_element.rb +26 -0
  25. data/lib/mail/elements/content_location_element.rb +21 -0
  26. data/lib/mail/elements/content_transfer_encoding_element.rb +17 -0
  27. data/lib/mail/elements/content_type_element.rb +31 -0
  28. data/lib/mail/elements/date_time_element.rb +22 -0
  29. data/lib/mail/elements/envelope_from_element.rb +39 -0
  30. data/lib/mail/elements/message_ids_element.rb +24 -0
  31. data/lib/mail/elements/mime_version_element.rb +22 -0
  32. data/lib/mail/elements/phrase_list.rb +16 -0
  33. data/lib/mail/elements/received_element.rb +26 -0
  34. data/lib/mail/encodings.rb +304 -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 +39 -0
  40. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  41. data/lib/mail/envelope.rb +30 -0
  42. data/lib/mail/field.rb +247 -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 +135 -0
  50. data/lib/mail/fields/common/common_date.rb +35 -0
  51. data/lib/mail/fields/common/common_field.rb +57 -0
  52. data/lib/mail/fields/common/common_message_id.rb +48 -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 +70 -0
  56. data/lib/mail/fields/content_id_field.rb +62 -0
  57. data/lib/mail/fields/content_location_field.rb +42 -0
  58. data/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
  59. data/lib/mail/fields/content_type_field.rb +201 -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 +56 -0
  63. data/lib/mail/fields/keywords_field.rb +44 -0
  64. data/lib/mail/fields/message_id_field.rb +82 -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 +56 -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 +204 -0
  83. data/lib/mail/header.rb +274 -0
  84. data/lib/mail/indifferent_hash.rb +146 -0
  85. data/lib/mail/mail.rb +267 -0
  86. data/lib/mail/matchers/has_sent_mail.rb +157 -0
  87. data/lib/mail/message.rb +2160 -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 +400 -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 +52 -0
  95. data/lib/mail/network/delivery_methods/file_delivery.rb +45 -0
  96. data/lib/mail/network/delivery_methods/sendmail.rb +89 -0
  97. data/lib/mail/network/delivery_methods/smtp.rb +142 -0
  98. data/lib/mail/network/delivery_methods/smtp_connection.rb +61 -0
  99. data/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
  100. data/lib/mail/network/retriever_methods/base.rb +63 -0
  101. data/lib/mail/network/retriever_methods/imap.rb +173 -0
  102. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  103. data/lib/mail/network/retriever_methods/test_retriever.rb +43 -0
  104. data/lib/mail/parsers.rb +26 -0
  105. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  106. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  107. data/lib/mail/parsers/content_location_parser.rb +35 -0
  108. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  109. data/lib/mail/parsers/content_type_parser.rb +64 -0
  110. data/lib/mail/parsers/date_time_parser.rb +36 -0
  111. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  112. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  114. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  115. data/lib/mail/parsers/ragel.rb +17 -0
  116. data/lib/mail/parsers/ragel/common.rl +184 -0
  117. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  118. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  119. data/lib/mail/parsers/ragel/ruby.rb +39 -0
  120. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  121. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  122. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  123. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  124. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  125. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  126. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  127. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  128. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  129. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  130. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  131. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  132. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
  133. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  134. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  135. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  136. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  137. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  138. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  139. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  140. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  141. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  142. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  143. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  144. data/lib/mail/parsers/received_parser.rb +47 -0
  145. data/lib/mail/part.rb +120 -0
  146. data/lib/mail/parts_list.rb +57 -0
  147. data/lib/mail/patterns.rb +37 -0
  148. data/lib/mail/utilities.rb +225 -0
  149. data/lib/mail/values/unicode_tables.dat +0 -0
  150. data/lib/mail/version.rb +4 -0
  151. data/lib/mail/version_specific/ruby_1_8.rb +119 -0
  152. data/lib/mail/version_specific/ruby_1_9.rb +159 -0
  153. metadata +276 -0
@@ -0,0 +1,29 @@
1
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
3
+ end
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+
7
+ require 'rake/testtask'
8
+ require 'rspec/core/rake_task'
9
+
10
+ desc "Build a gem file"
11
+ task :build do
12
+ system "gem build mail.gemspec"
13
+ end
14
+
15
+ task :default => :spec
16
+
17
+ RSpec::Core::RakeTask.new(:spec) do |t|
18
+ t.ruby_opts = '-w'
19
+ t.rspec_opts = %w(--backtrace --color)
20
+ end
21
+
22
+ begin
23
+ require "appraisal"
24
+ rescue LoadError
25
+ warn "Appraisal is only available in test/development"
26
+ end
27
+
28
+ # load custom rake tasks
29
+ Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].sort.each { |ext| load ext }
@@ -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!"
@@ -0,0 +1,91 @@
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/smtp' if RUBY_VERSION < '1.9.3'
33
+ require 'mail/indifferent_hash'
34
+
35
+ # Only load our multibyte extensions if AS is not already loaded
36
+ if defined?(ActiveSupport)
37
+ require 'active_support/inflector'
38
+ else
39
+ require 'mail/core_extensions/string/access'
40
+ require 'mail/core_extensions/string/multibyte'
41
+ require 'mail/multibyte'
42
+ end
43
+
44
+ require 'mail/patterns'
45
+ require 'mail/utilities'
46
+ require 'mail/configuration'
47
+
48
+ @@autoloads = {}
49
+ def self.register_autoload(name, path)
50
+ @@autoloads[name] = path
51
+ autoload(name, path)
52
+ end
53
+
54
+ # This runs through the autoload list and explictly requires them for you.
55
+ # Useful when running mail in a threaded process.
56
+ #
57
+ # Usage:
58
+ #
59
+ # require 'mail'
60
+ # Mail.eager_autoload!
61
+ def self.eager_autoload!
62
+ @@autoloads.each { |_,path| require(path) }
63
+ end
64
+
65
+ # Autoload mail send and receive classes.
66
+ require 'mail/network'
67
+
68
+ require 'mail/message'
69
+ require 'mail/part'
70
+ require 'mail/header'
71
+ require 'mail/parts_list'
72
+ require 'mail/attachments_list'
73
+ require 'mail/body'
74
+ require 'mail/field'
75
+ require 'mail/field_list'
76
+
77
+ require 'mail/envelope'
78
+
79
+ require 'mail/parsers'
80
+
81
+ # Autoload header field elements and transfer encodings.
82
+ require 'mail/elements'
83
+ require 'mail/encodings'
84
+ require 'mail/encodings/base64'
85
+ require 'mail/encodings/quoted_printable'
86
+
87
+ require 'mail/matchers/has_sent_mail'
88
+
89
+ # Finally... require all the Mail.methods
90
+ require 'mail/mail'
91
+ 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
@@ -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 its 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
+ p.body.sort_parts!
131
+ end
132
+ @parts.sort!(@part_sort_order)
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(/(?:\A|\r\n)--#{Regexp.escape(boundary || "")}(?=(?:--)?\s*$)/)
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--#{boundary}\r\n"
281
+ end
282
+
283
+ def end_boundary
284
+ "\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