Module: Binascii::Qp

Included in:
Binascii
Defined in:
lib/binascii/qp.rb

Constant Summary collapse

LINE_MAX =
76

Instance Method Summary collapse

Instance Method Details

#a2b_qp(str, header: false) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/binascii/qp.rb', line 9

def a2b_qp(str, header: false)
  return '' if str == '='
  str = Utils.render_string(str) if str.index("\r")
  first_byte = str.getbyte(0)

  str = if first_byte == 95 || first_byte == 61
    str
  else
    idx = str.index("\n")
    idx ? str[(idx + 1)..-1] : ''
  end

  str = str.unpack('M').first

  str.gsub!('==', '=')
  str.gsub!('_', ' ') if header

  str
end

#b2a_qp(data, header: false, quote_tabs: false, is_text: true) ⇒ Object



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
# File 'lib/binascii/qp.rb', line 29

def b2a_qp(data, header: false, quote_tabs: false, is_text: true)
  @byte_cache ||= {}

  String.new.tap do |result|
    data_size = data.bytesize
    line_len = 0
    cr_lf = false

    # This code is obtusely procedural for performance reasons.
    # The python C version isn't much better.
    Utils.each_byte_quad(data) do |leading, byte, trailing1, trailing2|
      repl = nil
      repl_size = 1

      if byte == 13  # linefeed, \r
        if is_text
          repl = byte
        else
          repl = '=0D'
        end
      elsif byte == 10  # carriage return, \n
        if is_text
          cr_lf = true if leading == 13

          if cr_lf && leading != 13
            repl = "\r\n"
            repl_size = 2
          else
            repl = byte
          end
        else
          repl = '=0A'
        end
      elsif byte == 95  # underscore
        repl = header ? '=5F' : '_'
        repl_size = repl.bytesize
      elsif byte == 32  # space
        if (!trailing1 || trailing1 == 10 || (trailing1 == 13 && trailing2 == 10) || quote_tabs) && is_text
          repl = "=20"
          repl_size = 3
        else
          repl = header ? '_' : ' '
        end
      elsif byte == 9  # tab
        if (!trailing1 || trailing1 == 10 || (trailing1 == 13 && trailing2 == 10) || quote_tabs) && is_text
          repl = "=09"
          repl_size = 3
        else
          repl = "\t"
        end
      elsif byte == 46  # period
        # I don't understand these rules, but whatever
        if line_len == 0 && (!trailing1 || trailing1 == 10 || trailing1 == 13 || trailing1 == 0)
          repl = '=2E'
          repl_size = 3
        else
          repl = '.'
        end
      elsif (byte >= 33 && byte <= 126) && byte != 61  # all printable ascii characters except '='
        repl = byte
      else
        repl = (@byte_cache[byte] ||= "=#{byte.to_s(16).rjust(2, '0').upcase}")
        repl_size = 3
      end

      if (line_len + repl_size) > LINE_MAX - 1
        line_len = 0
        result << "=\r\n"
        result << repl
      else
        result << repl
      end

      line_len += repl_size
    end
  end
end