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
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://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