Sample code for displaying a MIME email message on a web page?

Can anyone point me to a good source of sample code for taking a raw
email message (multipart, with attachments) and displaying it on a web
page? Also same thing for composing a new message with attachments?

~Josh

Josh, I found the following very helpful:

http://blog.mondragon.cc/articles/2006/12/30/decoding-email-attachments-with-actionmailer-base-receive
http://www.ruby-doc.org/stdlib/libdoc/net/pop/rdoc/classes/Net/POP3.html

Cheers, --Kip

Thanks for the pointer! That looks useful, but doesn't address what
I'm looking for. I want to be able to read in an mbox file, parse off
a message, and display it on a web page similar to how you would view
it in Hotmail or Gmail. That means decoding the MIME but also
"sanitizing" the HTML.

~Josh

Josh, I have done a *very* simple wrapper around this message parsing
bit. This will let you get at the parts of a mime multipart message.
I guess you could use sanitize() strip_tags() and strip_links() to
help make the html more displayable. Note I just put this together
for a proof-of-concept for emailing articles and images to a content
system I'm writing so i sure wouldn't cut and paste!

  require 'hermes_messages.rb'
  my_messages.HermesMessages.new
  my_messages.get_messages(pop server parameters)
  my_messages.message.each do |m|
    # Each message has many parts, but we simplifiy with some helper
methods
    puts m.subject
    puts m.from
    puts m.body # Note the 'body' method retrieves the text/
plain part for my needs - modify as required to get the html part
  end

Hope this helps a little more,

--Kip

class HermesMessages
  require 'net/pop'
  require 'action_mailer'

  EMPTY_PART = " \r\n\r\n"
  TEXT_PLAIN = 'text/plain'
  MULTIPART_ALTERNATIVE = 'multipart/alternative'

  attr_reader :messages

  def get_messages(host, user, password, port, *args)
    options = args.last.is_a?(Hash) ? args.pop : {}
    @messages = []
    begin
      Net::POP3.start(host, port, user, password) do |pop|
        if pop.mails.empty?
          puts 'No mail.'
        else
          puts "#{pop.mails.size} messages will be processed."
          pop.each_mail do |m|
           @messages << IncomingImageHandler.receive(m.pop)
           m.delete if options[:delete] == true
          end
        end
        puts "#{pop.mails.size} mails popped."
        return pop.mails.size
      end
    rescue
      puts "Exception detected - could not retrieve email"
      return -1
    end
  end

  class Message
    attr_accessor :subject, :from
    attr_reader :parts

    def initialize
      @parts = []
    end

    def add_part(p)
      @parts << p
    end

    def has_jpeg?
      parts.each {|p| return true if p.sub_type == "jpeg"}
      return false
    end

    def has_body?
      parts.each {|p| return true if p.main_type == "text" &&
p.sub_type == "plain"}
      return false
    end

    def body
      parts.each {|p| return p.body if p.main_type == "text" &&
p.sub_type == "plain"}
      return ""
    end

    def jpeg
      parts.each {|p| return p.body if p.main_type == "image" &&
p.sub_type == "jpeg"}
      return ""
    end

    def image_filename
      parts.each {|p| return p.filename if p.main_type == "image" &&
p.sub_type == "jpeg"}
      return ""
    end

  end

  class Content

attr_accessor :main_type, :sub_type, :body, :filename, :file_extension, :index
  end

  class IncomingImageHandler < ActionMailer::Base
    # email is a TMail::Mail
    def receive(email)
      @message = Message.new
      @message.from = email.from[0]
      @message.subject = email.subject
      puts "Processing message: #{@message.subject}"
      process(email)
    end

  protected

    def process(part)
      if part.multipart? then
        part.parts.each {|p| process(p) } unless
          part.content_type == MULTIPART_ALTERNATIVE &&
          plain_part_is_empty(part)
      else
        process_part(part)
      end
      return @message
    end

    def process_part(part)
      puts " part: #{part.content_type} with size
#{part.body.length}"
      pp " content is: '#{part.body}'" if part.body.length <= 10
      content = Content.new
      content.main_type = part.main_type
      content.sub_type = part.sub_type
      content.body = part.body
      content.filename = part_filename(part)
      content.file_extension = ext(part)
      @message.add_part(content)
    end

  private

    def plain_part_is_empty(part)
      part.parts.each do |p|
        return true if p.content_type == TEXT_PLAIN && p.body ==
EMPTY_PART
      end
      return false
    end

    def part_filename(part)
      # get filename
      if part['content-location'] != nil && part['content-
location'].body.length != 0
        filename = part['content-location'].body
      elsif part.type_param('name') != nil &&
part.type_param('name').length != 0
        filename = part.type_param('name')
      elsif part.disposition_param('filename') != nil &&
part.disposition_param('filename').length != 0
        filename = part.disposition_param('filename')
      else
        filename = nil
      end
    end

    CTYPE_TO_EXT = {
      'image/jpeg' => 'jpg',
      'image/gif' => 'gif',
      'image/png' => 'png',
      'image/tiff' => 'tif'
    }

    def ext( mail )
      CTYPE_TO_EXT[mail.content_type] || 'txt'
    end
  end #class
end

Thanks Kip!

I just found sanitize() and that seems to be a big part of what I'm
looking for. I'll check out strip_links and strip_tags also!

~Josh

What's the best place to find documentation on sanitize, strip_links,
etc. and find other functions like this? I'm looking through Ruby Core
and Ruby Standard Libs and don't see it.

~Josh

Josh, here: http://rails.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html

Cheers, --Kip

That was exactly what I was looking for. Thanks!

~Josh

Sorry one last question on TextHelper... I can call sanitize from
within a view (.rhtml file) no problem, but when I try to use it
within a controller or model it complains that the method is unknown.
What do I need to require/include to reference sanitize from within a
controller or model?

~Josh