aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib/action_mailer/vendor/tmail/quoting.rb
blob: a7e5c4471000028884664871ab3204f641876736 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
module TMail
  class Mail
    def subject(to_charset = 'utf-8')
      Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
    end

    def unquoted_body(to_charset = 'utf-8')
      from_charset = sub_header("content-type", "charset")
      case (content_transfer_encoding || "7bit").downcase
        when "quoted-printable"
          Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
            to_charset, from_charset, true)
        when "base64"
          Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
            from_charset)
        when "7bit", "8bit"
          Unquoter.convert_to(quoted_body, to_charset, from_charset)
        when "binary"
          quoted_body
        else
          quoted_body
      end
    end

    def body(to_charset = 'utf-8', &block)
      attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
    
      if multipart?
        parts.collect { |part| 
          header = part["content-type"]

          if part.multipart?
            part.body(to_charset, &attachment_presenter)
          elsif header.nil?
            ""
          elsif !attachment?(part)
            part.unquoted_body(to_charset)
          else
            attachment_presenter.call(header["name"] || "(unnamed)")
          end
        }.join
      else
        unquoted_body(to_charset)
      end
    end
  end

  class Unquoter
    class << self
      def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1", preserve_underscores=false)
        return "" if text.nil?
        text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do
          before = $1
          from_charset = $2
          quoting_method = $3
          text = $4

          before = convert_to(before, to_charset, from_charset) if before.length > 0
          before + case quoting_method
              when "q", "Q" then
                unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
              when "b", "B" then
                unquote_base64_and_convert_to(text, to_charset, from_charset)
              when nil then
                # will be nil at the end of the string, due to the nature of
                # the regex used.
                ""
              else
                raise "unknown quoting method #{quoting_method.inspect}"
            end
        end
      end
 
      def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
        text = text.gsub(/_/, " ") unless preserve_underscores
        text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
        convert_to(text.unpack("M*").first, to, from)
      end
 
      def unquote_base64_and_convert_to(text, to, from)
        convert_to(Base64.decode(text), to, from)
      end

      begin
        require 'iconv'
        def convert_to(text, to, from)
          return text unless to && from
          text ? Iconv.iconv(to, from, text).first : ""
        rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
          # the 'from' parameter specifies a charset other than what the text
          # actually is...not much we can do in this case but just return the
          # unconverted text.
          #
          # Ditto if either parameter represents an unknown charset, like
          # X-UNKNOWN.
          text
        end
      rescue LoadError
        # Not providing quoting support
        def convert_to(text, to, from)
          warn "Action Mailer: iconv not loaded; ignoring conversion from #{from} to #{to} (#{__FILE__}:#{__LINE__})"
          text
        end
      end
    end
  end
end

if __FILE__ == $0
  require 'test/unit'

  class TC_Unquoter < Test::Unit::TestCase
    def test_unquote_quoted_printable
      a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?=" 
      b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
      assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
    end

    def test_unquote_base64
      a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
      b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
      assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
    end

    def test_unquote_without_charset
      a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber" 
      b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
      assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
    end
  end
end