Skip to content

Commit

Permalink
[ruby/net-imap] Move UTF7 & datetime formatting to net/imap/data_enco…
Browse files Browse the repository at this point in the history
…ding

Partially implements #10.

ruby/net-imap@0d43c5e856
  • Loading branch information
nevans authored and hsbt committed May 6, 2021
1 parent 4dc7b82 commit 337c0e3
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 78 deletions.
41 changes: 1 addition & 40 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
end

require_relative "imap/command_data"
require_relative "imap/data_encoding"
require_relative "imap/response_data"
require_relative "imap/response_parser"

Expand Down Expand Up @@ -1054,46 +1055,6 @@ def idle_done
end
end

# Decode a string from modified UTF-7 format to UTF-8.
#
# UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
# slightly modified version of this to encode mailbox names
# containing non-ASCII characters; see [IMAP] section 5.1.3.
#
# Net::IMAP does _not_ automatically encode and decode
# mailbox names to and from UTF-7.
def self.decode_utf7(s)
return s.gsub(/&([^-]+)?-/n) {
if $1
($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
else
"&"
end
}
end

# Encode a string from UTF-8 format to modified UTF-7.
def self.encode_utf7(s)
return s.gsub(/(&)|[^\x20-\x7e]+/) {
if $1
"&-"
else
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
"&" + base64.delete("=").tr("/", ",") + "-"
end
}.force_encoding("ASCII-8BIT")
end

# Formats +time+ as an IMAP-style date.
def self.format_date(time)
return time.strftime('%d-%b-%Y')
end

# Formats +time+ as an IMAP-style date-time.
def self.format_datetime(time)
return time.strftime('%d-%b-%Y %H:%M %z')
end

private

CRLF = "\r\n" # :nodoc:
Expand Down
47 changes: 47 additions & 0 deletions lib/net/imap/data_encoding.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

module Net
class IMAP < Protocol

# Decode a string from modified UTF-7 format to UTF-8.
#
# UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
# slightly modified version of this to encode mailbox names
# containing non-ASCII characters; see [IMAP] section 5.1.3.
#
# Net::IMAP does _not_ automatically encode and decode
# mailbox names to and from UTF-7.
def self.decode_utf7(s)
return s.gsub(/&([^-]+)?-/n) {
if $1
($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
else
"&"
end
}
end

# Encode a string from UTF-8 format to modified UTF-7.
def self.encode_utf7(s)
return s.gsub(/(&)|[^\x20-\x7e]+/) {
if $1
"&-"
else
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
"&" + base64.delete("=").tr("/", ",") + "-"
end
}.force_encoding("ASCII-8BIT")
end

# Formats +time+ as an IMAP-style date.
def self.format_date(time)
return time.strftime('%d-%b-%Y')
end

# Formats +time+ as an IMAP-style date-time.
def self.format_datetime(time)
return time.strftime('%d-%b-%Y %H:%M %z')
end

end
end
38 changes: 0 additions & 38 deletions test/net/imap/test_imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,6 @@ def teardown
Socket.do_not_reverse_lookup = @do_not_reverse_lookup
end

def test_encode_utf7
assert_equal("foo", Net::IMAP.encode_utf7("foo"))
assert_equal("&-", Net::IMAP.encode_utf7("&"))

utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
s = Net::IMAP.encode_utf7(utf8)
assert_equal("&,yH,Iv8j-", s)
s = Net::IMAP.encode_utf7("foo&#{utf8}-bar".encode("EUC-JP"))
assert_equal("foo&-&,yH,Iv8j--bar", s)

utf8 = "\343\201\202&".dup.force_encoding("UTF-8")
s = Net::IMAP.encode_utf7(utf8)
assert_equal("&MEI-&-", s)
s = Net::IMAP.encode_utf7(utf8.encode("EUC-JP"))
assert_equal("&MEI-&-", s)
end

def test_decode_utf7
assert_equal("&", Net::IMAP.decode_utf7("&-"))
assert_equal("&-", Net::IMAP.decode_utf7("&--"))

s = Net::IMAP.decode_utf7("&,yH,Iv8j-")
utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
assert_equal(utf8, s)
end

def test_format_date
time = Time.mktime(2009, 7, 24)
s = Net::IMAP.format_date(time)
assert_equal("24-Jul-2009", s)
end

def test_format_datetime
time = Time.mktime(2009, 7, 24, 1, 23, 45)
s = Net::IMAP.format_datetime(time)
assert_match(/\A24-Jul-2009 01:23 [+\-]\d{4}\z/, s)
end

if defined?(OpenSSL::SSL::SSLError)
def test_imaps_unknown_ca
assert_raise(OpenSSL::SSL::SSLError) do
Expand Down
46 changes: 46 additions & 0 deletions test/net/imap/test_imap_data_encoding.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

require "net/imap"
require "test/unit"

class IMAPDataEncodingTest < Test::Unit::TestCase

def test_encode_utf7
assert_equal("foo", Net::IMAP.encode_utf7("foo"))
assert_equal("&-", Net::IMAP.encode_utf7("&"))

utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
s = Net::IMAP.encode_utf7(utf8)
assert_equal("&,yH,Iv8j-", s)
s = Net::IMAP.encode_utf7("foo&#{utf8}-bar".encode("EUC-JP"))
assert_equal("foo&-&,yH,Iv8j--bar", s)

utf8 = "\343\201\202&".dup.force_encoding("UTF-8")
s = Net::IMAP.encode_utf7(utf8)
assert_equal("&MEI-&-", s)
s = Net::IMAP.encode_utf7(utf8.encode("EUC-JP"))
assert_equal("&MEI-&-", s)
end

def test_decode_utf7
assert_equal("&", Net::IMAP.decode_utf7("&-"))
assert_equal("&-", Net::IMAP.decode_utf7("&--"))

s = Net::IMAP.decode_utf7("&,yH,Iv8j-")
utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
assert_equal(utf8, s)
end

def test_format_date
time = Time.mktime(2009, 7, 24)
s = Net::IMAP.format_date(time)
assert_equal("24-Jul-2009", s)
end

def test_format_datetime
time = Time.mktime(2009, 7, 24, 1, 23, 45)
s = Net::IMAP.format_datetime(time)
assert_match(/\A24-Jul-2009 01:23 [+\-]\d{4}\z/, s)
end

end

0 comments on commit 337c0e3

Please sign in to comment.