From ac2630ae87706383764088572530b83efb477bfe Mon Sep 17 00:00:00 2001 From: wmorgan Date: Mon, 27 Aug 2007 18:18:33 +0000 Subject: [PATCH] mime-decode hook for plugging in external decoding programs git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@547 5c8cc53c-5e98-4d25-b20a-d8db53a31250 --- lib/sup/hook.rb | 18 ++++++--- lib/sup/message.rb | 93 +++++++++++++++++++++++++++++----------------- lib/sup/util.rb | 6 +++ 3 files changed, 77 insertions(+), 40 deletions(-) diff --git a/lib/sup/hook.rb b/lib/sup/hook.rb index 4c3f30e..8c4a2ae 100644 --- a/lib/sup/hook.rb +++ b/lib/sup/hook.rb @@ -19,7 +19,14 @@ class HookManager attr_writer :__locals def method_missing m, *a - @__locals[m] || super + case @__locals[m] + when Proc + @__locals[m].call(*a) + when nil + super + else + @__locals[m] + end end def say s @@ -61,19 +68,17 @@ class HookManager hook = hook_for(name) or return context = @contexts[hook] ||= HookContext.new(name) context.__locals = locals - + + result = nil begin result = context.instance_eval @hooks[name], fn_for(name) - if result.is_a? String - log "got return value: #{result.inspect}" - BufferManager.flash result - end rescue Exception => e log "error running hook: #{e.message}" log e.backtrace.join("\n") BufferManager.flash "Error running hook: #{e.message}" end context.__cleanup + result end def register name, desc @@ -106,6 +111,7 @@ private log "read '#{name}' from #{fn_for(name)}" end rescue SystemCallError => e + log "disabled hook for '#{name}': #{e.message}" nil end end diff --git a/lib/sup/message.rb b/lib/sup/message.rb index d1aded0..ffcde9e 100644 --- a/lib/sup/message.rb +++ b/lib/sup/message.rb @@ -17,7 +17,18 @@ class Message SNIPPET_LEN = 80 WRAP_LEN = 80 # wrap at this width RE_PATTERN = /^((re|re[\[\(]\d[\]\)]):\s*)+/i - + + HookManager.register "mime-decode", < content_type, + :filename => lambda { write_to_disk } + @lines = text.split("\n") if text end end - def view! - file = Tempfile.new "redwood.attachment" - file.print raw_content - file.close + def inlineable?; !@lines.nil? end - system "/usr/bin/run-mailcap --action=view #{@content_type}:#{file.path} >& /dev/null" + def view! + path = write_to_disk + system "/usr/bin/run-mailcap --action=view #{@content_type}:#{path} >& /dev/null" $? == 0 end + + private - def to_s; Message.decode_and_convert @content; end - def raw_content; @content.decode end - - def inlineable?; @content_type =~ /^text\/plain/ end + def write_to_disk + file = Tempfile.new "redwood.attachment" + file.print @raw_content + file.close + file.path + end end class Text @@ -283,10 +310,14 @@ private ## the general behavior i want is: ignore content-disposition, at ## least in so far as it suggests something being inline vs being an ## attachment. (because really, that should be the recipient's - ## decision to make.) if a mime part is text/plain, then decode it - ## and display it inline. if it has associated filename, then make - ## it collapsable and individually saveable; otherwise, treat it as - ## regular body text. + ## decision to make.) if a mime part is text/plain, OR if the user + ## decoding hook converts it, then decode it and display it + ## inline. for these decoded attachments, if it has associated + ## filename, then make it collapsable and individually saveable; + ## otherwise, treat it as regular body text. + ## + ## everything else is just an attachment and is not displayed + ## inline. ## ## so, in contrast to mutt, the user is not exposed to the workings ## of the gruesome slaughterhouse and sausage factory that is a @@ -317,28 +348,22 @@ private ## otherwise, it's body text else - body = Message.decode_and_convert m + body = Message.convert_from m.body, m.charset text_to_chunks body.normalize_whitespace.split("\n") end end end - def self.decode_and_convert m - charset = - if m.header.field?("content-type") && m.header.fetch("content-type") =~ /charset="?(.*?)"?(;|$)/ - $1 - end - - m.body && body = m.decode or raise MessageFormatError, "For some bizarre reason, RubyMail was unable to parse this message." + def self.convert_from body, charset + return body unless charset - if charset - begin - body = Iconv.iconv($encoding, charset, body).join - rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e - Redwood::log "warning: error decoding message body from #{charset}: #{e.message}" - end + begin + Iconv.iconv($encoding, charset, body).join + rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e + Redwood::log "warning: error (#{e.class.name}) decoding message body from #{charset}: #{e.message}" + File.open("sup-unable-to-decode.txt", "w") { |f| f.write body } + body end - body end ## parse the lines of text into chunk objects. the heuristics here diff --git a/lib/sup/util.rb b/lib/sup/util.rb index cf78dac..6ddd758 100644 --- a/lib/sup/util.rb +++ b/lib/sup/util.rb @@ -85,6 +85,12 @@ module RMail add_part a end + + def charset + if header.field?("content-type") && header.fetch("content-type") =~ /charset="?(.*?)"?(;|$)/ + $1 + end + end end end -- 2.45.2