require 'mail'
module Mail
  module Encodings
    # Handle windows-1258 as windows-1252 when decoding
    def Encodings.q_value_decode(str)
      str = str.sub(/\=\?windows-?1258\?/i, '\=?windows-1252?')
      RubyVer.q_value_decode(str)
    end
    def Encodings.b_value_decode(str)
      str = str.sub(/\=\?windows-?1258\?/i, '\=?windows-1252?')
      RubyVer.b_value_decode(str)
    end
  end

  class Message
    ## Extract plain text body of message
    def plain_body
      if self.multipart? and self.text_part
        self.text_part.decoded
      elsif self.mime_type == 'text/plain' || self.mime_type.nil?
        self.decoded
      else
        nil
      end
    end

    ## Extract HTML text body of message
    def html_body
      if self.multipart? and self.html_part
        self.html_part.decoded
      elsif self.mime_type == 'text/html'
        self.decoded
      else
        nil
      end
    end

    private

    ## Fix bug in basic parsing
    def parse_message
      self.header, self.body = raw_source.split(/\r?\n\r?\n/m, 2)
    end

    # Handle attached emails as attachments
    # Returns the filename of the attachment (if it exists) or returns nil
    # Make up a filename for rfc822 attachments if it isn't specified
    def find_attachment
      content_type_name = header[:content_type].filename rescue nil
      content_disp_name = header[:content_disposition].filename rescue nil
      content_loc_name = header[:content_location].location rescue nil

      if content_type && content_type_name
        filename = content_type_name
      elsif content_disposition && content_disp_name
        filename = content_disp_name
      elsif content_location && content_loc_name
        filename = content_loc_name
      elsif self.mime_type == "message/rfc822"
        filename = "#{rand(100000000)}.eml"
      else
        filename = nil
      end

      if filename
        # Normal decode
        filename = Mail::Encodings.decode_encode(filename, :decode) rescue filename
      end
      filename
    end

    def decode_body_as_text
      body_text = decode_body
      charset_tmp = Encoding.find(Ruby19.pick_encoding(charset)) rescue 'ASCII'
      charset_tmp = 'Windows-1252' if charset_tmp.to_s =~ /windows-?1258/i
      if charset_tmp == Encoding.find('UTF-7')
        body_text.force_encoding('UTF-8')
        decoded = body_text.gsub(/\+.*?\-/m) {|n|Base64.decode64(n[1..-2]+'===').force_encoding('UTF-16BE').encode('UTF-8')}
      else
        body_text.force_encoding(charset_tmp)
        decoded = body_text.encode("utf-8", :invalid => :replace, :undef => :replace)
      end
      decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :undef => :replace).encode("utf-8")
    end
  end

  # Handle attached emails as attachments
  class AttachmentsList < Array
    def initialize(parts_list)
      @parts_list = parts_list
      @content_disposition_type = 'attachment'
      parts_list.map { |p|
        (p.parts.empty? and p.attachment?) ? p : p.attachments
      }.flatten.compact.each { |a| self << a }
      self
    end
  end
end

class Array
  def decoded
    return nil if self.empty?
    return self.first.decoded
  end
end

class NilClass
  def decoded
    nil
  end
end
