Skip to content

Commit

Permalink
Merge remote branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Cormier committed Apr 18, 2011
2 parents 585b2c9 + 13726e9 commit 3b9c58d
Show file tree
Hide file tree
Showing 33 changed files with 497 additions and 192 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -11,3 +11,4 @@ spec/fixtures/emails/failed_emails/
*.swp
.idea
tmp
tags
16 changes: 16 additions & 0 deletions CHANGELOG.rdoc
@@ -1,3 +1,19 @@
== Sat Apr 16 12:57:27 UTC 2011 Mikel Lindsaar <mikel@rubyx.com>

* Added support for open SMTP connections and returning the Mail server's response - https://github.com/spiegela
* RE: not appended to subject when replying to a reply - https://github.com/prateekdayal
* Support not ascii compatible charset mail send - https://github.com/yalab
* Fix for issue 208 "mail.body after mail.add_file truncates message body" - https://github.com/glongman
* Handle bad subject encoding (or ":invalid => :replace" is ineffective for utf-8 to utf-8 encoding) - https://github.com/choonkeat
* Handle blank Received header field - https://github.com/bcantin
* Handle part with missing content type - https://github.com/bcantin
* Handle a "<>" Return field - https://github.com/bcantin
* Performance improvements for 1.9 - https://github.com/nobu
* Fix heavy CPU issues when messages are missing a space - https://github.com/scsmith
* Tighten up allowed encodings - https://github.com/scsmith
* Added to_yaml & from_yaml (as well as to_hash & from_hash) - https://github.com/srushti
* Fix up some comments - https://github.com/takahashim

== Wed 26 Jan 2011 02:23:09 UTC Mikel Lindsaar <mikel@rubyx.com>

* Update addresses passed into sendmail to escape them (Andy Lindeman)
Expand Down
1 change: 0 additions & 1 deletion Gemfile
Expand Up @@ -8,7 +8,6 @@ gem "i18n", ">= 0.4.0"

group :test do
gem "ZenTest", "~> 4.4.0"
gem "rcov", "~> 0.9.8"
gem "rake", "~> 0.8.7"
gem "bundler"
gem "rspec", "~> 1.3.0"
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -59,7 +59,7 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb')
rdoc.rdoc_files.include('lib/network/**/*.rb')
rdoc.rdoc_files.exclude('lib/parsers/*')

end

# load custom rake tasks
Expand Down
2 changes: 1 addition & 1 deletion lib/VERSION
@@ -1,4 +1,4 @@
patch:15
patch:16
major:2
build:
minor:2
7 changes: 6 additions & 1 deletion lib/mail/body.rb
Expand Up @@ -162,7 +162,12 @@ def encoded(transfer_encoding = '8bit')
else
# Decode then encode to normalize and allow transforming
# from base64 to Q-P and vice versa
enc.encode(dec.decode(raw_source))
decoded = dec.decode(raw_source)
if defined?(Encoding) && charset && charset != "US-ASCII"
decoded.encode!(charset)
decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
end
enc.encode(decoded)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/mail/configuration.rb
Expand Up @@ -35,6 +35,8 @@ def lookup_delivery_method(method)
Mail::Sendmail
when :file
Mail::FileDelivery
when :smtp_connection
Mail::SMTPConnection
when :test
Mail::TestMailer
else
Expand Down
86 changes: 43 additions & 43 deletions lib/mail/encodings.rb
Expand Up @@ -6,12 +6,12 @@ class UnknownEncodingType < StandardError #:nodoc:
end

module Encodings

include Mail::Patterns
extend Mail::Utilities

@transfer_encodings = {}

# Register transfer encoding
#
# Example
Expand All @@ -20,23 +20,23 @@ module Encodings
def Encodings.register(name, cls)
@transfer_encodings[get_name(name)] = cls
end

# Is the encoding we want defined?
#
#
# Example:
#
#
# Encodings.defined?(:base64) #=> true
def Encodings.defined?( str )
@transfer_encodings.include? get_name(str)
end

# Gets a defined encoding type, QuotedPrintable or Base64 for now.
#
# Each encoding needs to be defined as a Mail::Encodings::ClassName for
#
# Each encoding needs to be defined as a Mail::Encodings::ClassName for
# this to work, allows us to add other encodings in the future.
#
#
# Example:
#
#
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
def Encodings.get_encoding( str )
@transfer_encodings[get_name(str)]
Expand All @@ -52,16 +52,16 @@ def Encodings.get_name(enc)

# Encodes a parameter value using URI Escaping, note the language field 'en' can
# be set using Mail::Configuration, like so:
#
#
# Mail.defaults.do
# param_encode_language 'jp'
# end
#
# The character set used for encoding will either be the value of $KCODE for
# The character set used for encoding will either be the value of $KCODE for
# Ruby < 1.9 or the encoding on the string passed in.
#
#
# Example:
#
#
# Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
def Encodings.param_encode(str)
case
Expand All @@ -75,9 +75,9 @@ def Encodings.param_encode(str)
end

# Decodes a parameter value using URI Escaping.
#
#
# Example:
#
#
# Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
#
# str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
Expand All @@ -86,14 +86,14 @@ def Encodings.param_encode(str)
def Encodings.param_decode(str, encoding)
RubyVer.param_decode(str, encoding)
end

# Decodes or encodes a string as needed for either Base64 or QP encoding types in
# the =?<encoding>?[QB]?<string>?=" format.
#
#
# The output type needs to be :decode to decode the input string or :encode to
# encode the input string. The character set used for encoding will either be
# the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
#
#
# On encoding, will only send out Base64 encoded strings.
def Encodings.decode_encode(str, output_type)
case
Expand All @@ -110,12 +110,12 @@ def Encodings.decode_encode(str, output_type)

# Decodes a given string as Base64 or Quoted Printable, depending on what
# type it is.
#
#
# String has to be of the format =?<encoding>?[QB]?<string>?=
def Encodings.value_decode(str)
# Optimization: If there's no encoded-words in the string, just return it
return str unless str.index("=?")

str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's

# Split on white-space boundaries with capture, so we capture the white-space as well
Expand All @@ -125,18 +125,18 @@ def Encodings.value_decode(str)
else
# Join QP encoded-words that are adjacent to avoid decoding partial chars
text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/

# Separate encoded-words with a space, so we can treat them one by one
text.gsub!(/\?\=\=\?/, '?= =?')
text.split(/ /).map do |word|
word.to_str.
gsub(/=\?.+\?[Bb]\?.+\?=/m) {|substr| b_value_decode(substr)}.
gsub(/=\?.+\?[Qq]\?.+\?=/m) {|substr| q_value_decode(substr)}
gsub( /=\?.+\?[Bb]\?.+\?=/m ) { |substr| b_value_decode(substr) }.
gsub( /=\?.+\?[Qq]\?.+\?=/m ) { |substr| q_value_decode(substr) }
end
end
end.join("")
end

# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
def Encodings.unquote_and_convert_to(str, to_encoding)
original_encoding, string = split_encoding_from_string( str )
Expand Down Expand Up @@ -166,7 +166,7 @@ def Encodings.unquote_and_convert_to(str, to_encoding)
output
end
end

def Encodings.address_encode(address, charset = 'utf-8')
if address.is_a?(Array)
# loop back through for each element
Expand Down Expand Up @@ -196,13 +196,13 @@ def Encodings.encode_non_usascii(address, charset)
end
end.join(' ')
end

# Encode a string with Base64 Encoding and returns it ready to be inserted
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
#
# Example:
#
# Encodings.b_value_encode('This is あ string', 'UTF-8')
#
# Encodings.b_value_encode('This is あ string', 'UTF-8')
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
def Encodings.b_value_encode(str, encoding = nil)
return str if str.to_s.ascii_only?
Expand All @@ -211,13 +211,13 @@ def Encodings.b_value_encode(str, encoding = nil)
"=?#{encoding}?B?#{str.chomp}?="
end.join(" ")
end

# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
#
# Example:
#
# Encodings.q_value_encode('This is あ string', 'UTF-8')
#
# Encodings.q_value_encode('This is あ string', 'UTF-8')
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
def Encodings.q_value_encode(str, encoding = nil)
return str if str.to_s.ascii_only?
Expand All @@ -227,29 +227,29 @@ def Encodings.q_value_encode(str, encoding = nil)
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
end.join(" ")
end

private

# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
#
#
# Example:
#
# Encodings.b_value_encode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
#
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
# #=> 'This is あ string'
def Encodings.b_value_decode(str)
RubyVer.b_value_decode(str)
end

# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
#
#
# Example:
#
# Encodings.b_value_encode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
#
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
# #=> 'This is あ string'
def Encodings.q_value_decode(str)
RubyVer.q_value_decode(str).gsub(/_/, ' ')
end

def Encodings.split_encoding_from_string( str )
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
if match
Expand All @@ -258,7 +258,7 @@ def Encodings.split_encoding_from_string( str )
nil
end
end

def Encodings.find_encoding(str)
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
end
Expand Down
14 changes: 11 additions & 3 deletions lib/mail/fields/received_field.rb
Expand Up @@ -52,15 +52,23 @@ def info
end

def formatted_date
date_time.strftime("%a, %d %b %Y %H:%M:%S ") + date_time.zone.delete(':')
date_time.strftime("%a, %d %b %Y %H:%M:%S ") + date_time.zone.delete(':')
end

def encoded
"#{CAPITALIZED_FIELD}: #{info}; #{formatted_date}\r\n"
if value.blank?
"#{CAPITALIZED_FIELD}: \r\n"
else
"#{CAPITALIZED_FIELD}: #{info}; #{formatted_date}\r\n"
end
end

def decoded
"#{info}; #{formatted_date}"
if value.blank?
""
else
"#{info}; #{formatted_date}"
end
end

end
Expand Down
1 change: 1 addition & 0 deletions lib/mail/fields/return_path_field.rb
Expand Up @@ -38,6 +38,7 @@ class ReturnPathField < StructuredField
CAPITALIZED_FIELD = 'Return-Path'

def initialize(value = nil, charset = 'utf-8')
value = nil if value == '<>'
self.charset = charset
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
self.parse
Expand Down
1 change: 1 addition & 0 deletions lib/mail/fields/unstructured_field.rb
Expand Up @@ -166,6 +166,7 @@ def fold(prepend = 0) # :nodoc:
end

def encode(value)
value.encode!(charset) if defined?(Encoding) && charset
(value.not_ascii_only? ? [value].pack("M").gsub("=\n", '') : value).gsub("\r", "=0D").gsub("\n", "=0A")
end

Expand Down
3 changes: 2 additions & 1 deletion lib/mail/header.rb
Expand Up @@ -73,7 +73,8 @@ def fields
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
def fields=(unfolded_fields)
@fields = Mail::FieldList.new
unfolded_fields.each do |field|
warn "Warning: more than 1000 header fields only using the first 1000" if unfolded_fields.length > 1000
unfolded_fields[0..1000].each do |field|

field = Field.new(field, nil, charset)
field.errors.each { |error| self.errors << error }
Expand Down

0 comments on commit 3b9c58d

Please sign in to comment.