脳ざらし紀行


2006-08-19

_ Linux で mhtml を見たい

Linux で mhtml(.mht ファイル) を見たくて検索したけど適当な方法がなかった。mhtml の仕様を調べたら、基本的にメールのフォーマットなんで、ruby で何とかできるだろうという事でスクリプトを書いてみた。

~/.rmhtml/ ディレクトリを作ってそこに適当にファイルを展開して、firefox を呼び出すというスクリプト。これを .mht ファイルと関連付ければ、mhtml ファイルを firefox から閲覧することが出来る。要 TMailhtree

#!/usr/local/bin/ruby-1.8
require 'pp'
require 'kconv'
require 'digest/md5'
require 'tmail'
require 'htree'
require "optparse"

Rmhtml_dir = File.join ENV['HOME'], '.rmhtml'

if File.exist?(Rmhtml_dir)
  unless File.directory?(Rmhtml_dir)
    raise RmhtmlError, "#{Rmhtml_dir} exists and is not a directory."
  end
else
  Dir.mkdir Rmhtml_dir
end

class RmhtmlError < StandardError
end

class Rmhtml
  Config = {}
  def initialize(filename)
    @mail = TMail::Mail.load(filename)
    
    if  /text\/html/ =~ @mail.parts[0].content_type
      @main_part = @mail.parts[0]
    else
      each_part(@mail){|e|
        if /text\/html/ =~ e.content_type
          @main_part = e
          break
        end
      }
      raise RmhtmlError, 'main html part does not exist.' unless @main_part
    end
    @mail.parts.delete(@main_part)
  end
  
  def each_part(mail, &b)
    mail.parts.each{|e|
      unless /multipart\/alternative/i =~ e.content_type 
        yield e
      else
        each_part(e, &b)
      end
    }
  end
  
  def get_objects
    @main_html = decode(@main_part)
    ret = []
    @obj_locs = {}
    each_part(@mail){|e|
      filename = filename(e)
      obj = decode(e)
      @obj_locs[location(e)] = true
      ret << [filename, obj]
    }
    return ret
  end
  
  def location(mail)
    mail['Content-Location'].to_s.toeuc
  end
  
  def filename(mail)
    case mail
    when String
      Digest::MD5.hexdigest(mail.toeuc)
    else
      Digest::MD5.hexdigest(location(mail))
    end
  end

  def md5(s)
    Digest::MD5.hexdigest(s.toeuc)
  end
  
  def dir_name
    File.join(Rmhtml_dir, filename(@main_part) )    
  end
  
  def mkdir
    if File.exist? dir_name
      return :exist
    else
      Dir.mkdir dir_name
    end
  end
  
  def decode(mail)
    case mail.transfer_encoding
    when /quoted-printable/i
      mail.body.unpack('M')[0]
    when /Base64/i
      mail.body.unpack('m')[0]
    end
  end
  
  def do_store
    d = mkdir()
    if d != :exist or Config[:f]
      Dir.chdir(dir_name)
      objs = get_objects()
      objs.each{|filename, obj|
        File.open(filename, 'w').write(obj)
      }
      html = change_src_in_main_html()
      File.open(filename(@main_part), 'w').write(html)
    end
    return File.join(dir_name, filename(@main_part))
  end
  
  def change_src_in_main_html
    htr = HTree(@main_html).root
    objs = {}
    htr = change_src(htr)
    ret = ''
    htr.display_xml(ret)
    return ret
  end

  def is_stylesheet_link_tag?(e)
     /link/i =~ e.element_name.to_s and
      /stylesheet/i =~ e.get_attr('rel').to_s and
      /text\/css/i =~ e.get_attr('type').to_s
  end
    
  def change_src(elms)
    objs = {}
    elms.each_child_with_index{|e, ind|
      next unless e.is_a? HTree::Elem
      if src = e.get_attr('src') and @obj_locs[src.to_s.toeuc]
        e = e.subst_subnode( {'src' => filename(src.to_s) } )
      elsif is_stylesheet_link_tag?(e) and href = e.get_attr('href') and @obj_locs[href.to_s.toeuc]
        e = e.subst_subnode( {'href' => filename(href.to_s) } )
      end
      e2 = change_src(e)
      objs[ind] = e2
    }
    elms.subst_subnode(objs)
  end

end

opts = OptionParser.new
opts.on("-f"){|v| Rmhtml::Config[:f] = true }
opts.parse!(ARGV)

rmhtml = Rmhtml.new(ARGV[0])
html = rmhtml.do_store()
system "firefox #{html.dump}"

ちょっと改良。 既存の html ファイルの特定の条件に合う要素だけ入替えるというのは、htree ではやりにくい。他に何かやり方があるのだろうか。

お名前:
E-mail:
コメント:
本日のリンク元

最近のコメント

2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|04|05|
2011|04|
2012|03|07|
2013|01|02|07|
トップ «前の日記(2006-08-17) 最新 次の日記(2006-08-20)» 編集