lib/mail/fields/unstructured_field.rb



# encoding: utf-8
module Mail
  # Provides access to an unstructured header field
  #
  # ===Per RFC 2822:
  #  2.2.1. Unstructured Header Field Bodies
  #  
  #     Some field bodies in this standard are defined simply as
  #     "unstructured" (which is specified below as any US-ASCII characters,
  #     except for CR and LF) with no further restrictions.  These are
  #     referred to as unstructured field bodies.  Semantically, unstructured
  #     field bodies are simply to be treated as a single line of characters
  #     with no further processing (except for header "folding" and
  #     "unfolding" as described in section 2.2.3).
  class UnstructuredField
    
    include Mail::CommonField
    include Mail::Utilities
    
    def initialize(*args)
      self.name = args.first
      self.value = args.last
      self
    end
    
    def encoded
      do_encode(self.name)
    end
    
    def decoded
      do_decode
    end

    def default
      decoded
    end
    
    def parse # An unstructured field does not parse
      self
    end

    private
    
    def do_encode(name)
      value.nil? ? '' : "#{wrapped_value}\r\n"
    end
    
    def do_decode
      value.blank? ? nil : Encodings.decode_encode(value, :decode)
    end
    
    # 2.2.3. Long Header Fields
    # 
    #  Each header field is logically a single line of characters comprising
    #  the field name, the colon, and the field body.  For convenience
    #  however, and to deal with the 998/78 character limitations per line,
    #  the field body portion of a header field can be split into a multiple
    #  line representation; this is called "folding".  The general rule is
    #  that wherever this standard allows for folding white space (not
    #  simply WSP characters), a CRLF may be inserted before any WSP.  For
    #  example, the header field:
    #  
    #          Subject: This is a test
    #  
    #  can be represented as:
    #  
    #          Subject: This
    #           is a test
    #  
    #  Note: Though structured field bodies are defined in such a way that
    #  folding can take place between many of the lexical tokens (and even
    #  within some of the lexical tokens), folding SHOULD be limited to
    #  placing the CRLF at higher-level syntactic breaks.  For instance, if
    #  a field body is defined as comma-separated values, it is recommended
    #  that folding occur after the comma separating the structured items in
    #  preference to other places where the field could be folded, even if
    #  it is allowed elsewhere.
    def wrapped_value # :nodoc:
      case
      when decoded.to_s.ascii_only? && field_length <= 78
        "#{name}: #{value}"
      when field_length <= 40 # Allow for =?ISO-8859-1?B?=...=
        "#{name}: #{encode(value)}"
      else
        @folded_line = []
        @unfolded_line = value.clone
        fold("#{name}: ".length)
        folded = @folded_line.map { |l| l unless l.blank? }.compact.join("\r\n\t")
        "#{name}: #{folded}"
      end
    end

    def fold(prepend = 0) # :nodoc:
      # Get the last whitespace character, OR we'll just choose 
      # 78 if there is no whitespace
      @unfolded_line.ascii_only? ? (limit = 78 - prepend) : (limit = 40 - prepend)
      wspp = @unfolded_line.slice(0..limit) =~ /[ \t][^ \T]*$/ || limit
      wspp = limit if wspp == 0
      @folded_line << encode(@unfolded_line.slice!(0...wspp))
      if @unfolded_line.length > limit
        fold
      else
        @folded_line << encode(@unfolded_line)
      end
    end

    def encode(value)
      if value.ascii_only?
        value
      else
        RUBY_VERSION < '1.9' ? encoding = $KCODE : encoding = @value.encoding
        Encodings.b_value_encode(value, encoding)
      end
    end

  end
end