module Binascii::Qp

Constants

LINE_MAX

Public Instance Methods

a2b_qp(str, header: false) click to toggle source
# 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) click to toggle source
# 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