class Hermes::HeaderExt

Header field contents (RFC 2047) encoding

Examples

HeaderExt.encode "Jörg Müller"
                              #=> "=?utf-8?Q?J=C3=B6rg_M=C3=BCller?="
HeaderExt.decode "=?UTF-8?Q?J=C3=B6rg_M=C3=BCller?="
                              #=> "Jörg Müller"

Public Class Methods

new( [ parameters] ) → con click to toggle source

Creates a HeaderExt converter.

See the encode method for an explanation of the parameters.

Examples

con = HeaderExt.new
con = HeaderExt.new :base64 => true, :limit => 32, :lower => true
con = HeaderExt.new :mask => /["'()]/
# File lib/hermes/escape.rb, line 551
def initialize params = nil
  if params then
    @base64 = params.delete :base64
    @limit  = params.delete :limit
    @lower  = params.delete :lower
    @mask   = params.delete :mask
    params.empty? or
      raise ArgumentError, "invalid parameter: #{params.keys.first}."
  end
end

Public Instance Methods

decode(str) click to toggle source
# File lib/hermes/escape.rb, line 694
def decode str
  self.class.decode str
end
encode( str) → str click to toggle source

Create a header field style encoded string. The following parameters will be evaluated:

:base64    # build ?B? instead of ?Q?
:limit     # break words longer than this
:lower     # build lower case ?b? and ?q?
:mask      # a regular expression detecting characters to mask

The result will not contain any 8-bit characters. The encoding will be kept although it won't have a meaning.

The parameter :mask will have no influence on the masking itself but will guarantee characters to be masked.

Examples

yodel = "Holleri du dödl di, diri diri dudl dö."

con = HeaderExt.new
con.encode yodel
  #=> "Holleri du =?UTF-8?Q?d=C3=B6dl?= di, diri diri dudl =?UTF-8?Q?d=C3=B6=2E?="

yodel.encode! "iso8859-1"
con.encode yodel
  #=> "Holleri du =?ISO8859-1?Q?d=F6dl?= di, diri diri dudl =?ISO8859-1?Q?d=F6=2E?="

e = "€"
e.encode! "utf-8"      ; con.encode e      #=> "=?UTF-8?Q?=E2=82=AC?="
e.encode! "iso8859-15" ; con.encode e      #=> "=?ISO8859-15?Q?=A4?="
e.encode! "ms-ansi"    ; con.encode e      #=> "=?MS-ANSI?Q?=80?="

con = HeaderExt.new :mask => /["'()]/
con.encode "'Stop!' said Fred."
  #=> "=?UTF-8?Q?=27Stop=21=27?= said Fred."
# File lib/hermes/escape.rb, line 609
def encode str
  do_encoding str do
    # I don't like this kind of programming style but it seems to work. BS
    r, enc = "", ""
    while str =~ /\S+/ do
      if needs? $& then
        (enc.notempty? || r) << $`
        enc << $&
      else
        if not enc.empty? then
          r << (mask enc)
          enc.clear
        end
        r << $` << $&
      end
      str = $'
    end
    if not enc.empty? then
      enc << str
      r << (mask enc)
    else
      r << str
    end
    r
  end
end
encode_whole( str) → str click to toggle source

The unlike encode the whole string as one piece will be encoded.

yodel = "Holleri du dödl di, diri diri dudl dö."
HeaderExt.encode_whole yodel
  #=> "=?UTF-8?Q?Holleri_du_d=C3=B6dl_di,_diri_diri_dudl_d=C3=B6=2E?="
# File lib/hermes/escape.rb, line 645
def encode_whole str
  do_encoding str do
    mask str
  end
end
lexer(str) { |:plain, $`| ... } click to toggle source
# File lib/hermes/escape.rb, line 777
def lexer str
  while str do
    str =~ /(\s+)|\B=\?(\S*?)\?([QB])\?(\S*?)\?=\B/i
    if $1 then
      yield :plain, $` unless $`.empty?
      yield :space, $&
    elsif $2 then
      yield :plain, $` unless $`.empty?
      d = unmask $2, $3, $4
      yield :decoded, d
    else
      yield :plain, str
    end
    str = $'.notempty?
  end
end
needs? str → true or false click to toggle source

Check whether a string needs encoding.

# File lib/hermes/escape.rb, line 567
def needs? str
  (not str.ascii_only? or str =~ @mask) and true or false
end
std() click to toggle source

The standard header content encoding has a word break limit of 64.

# File lib/hermes/escape.rb, line 702
def std
  @std ||= new :limit => 64
end

Private Instance Methods

base64(c) click to toggle source
# File lib/hermes/escape.rb, line 679
def base64 c
  c = [c].pack "m*"
  c.gsub! /\s/, ""
  c
end
do_encoding(str) { || ... } click to toggle source
# File lib/hermes/escape.rb, line 653
def do_encoding str
  @charset = str.encoding
  @type, @encoder = @base64 ? [ "B", :base64] : [ "Q", :quopri ]
  if @lower then
    @charset.downcase!
    @type.downcase!
  end
  yield.force_encoding str.encoding
ensure
  @charset = @type = @encoder = nil
end
mask(str) click to toggle source
# File lib/hermes/escape.rb, line 669
def mask str
  r, i = [], 0
  while i < str.length do
    l = @limit||str.length
    r.push "=?#@charset?#@type?#{send @encoder, str[ i, l]}?="
    i += l
  end
  r.join SPACE
end
quopri(c) click to toggle source
# File lib/hermes/escape.rb, line 685
def quopri c
  c.force_encoding Encoding::ASCII_8BIT
  c.gsub! /([^ a-zA-Z0-9])/ do |s| "=%02X" % s.ord end
  c.tr! " ", "_"
  c
end
unmask(cs, tp, txt) click to toggle source
# File lib/hermes/escape.rb, line 796
def unmask cs, tp, txt
  case tp.upcase
    when "B" then                    txt, = txt.unpack "m*"
    when "Q" then txt.tr! "_", " " ; txt, = txt.unpack "M*"
  end
  cs.slice! /\*\w+\z/    # language as in rfc2231, 5.
  case cs
    when /\Autf-?7\z/i then
      # Arrgh. Ruby 1.9 doesn't seem to do that.
      txt.force_encoding Encoding::US_ASCII
      txt.gsub! /\+([0-9a-zA-Z+\/]*)-?/ do
        if $1.empty? then
          "+"
        else
          s = ("#$1==".unpack "m*").join
          (s.unpack "S>*").map { |x| x.chr ENCODING }.join
        end
      end
    when /\Aunknown/i then
      txt.force_encoding Encoding::US_ASCII
    else
      txt.force_encoding cs
  end
  txt
end