require 'net/ssh' module Redwood module MBox ## this is slightly complicated because SSHFile (and thus @f or ## @loader) can throw a variety of exceptions, and we need to catch ## those, reraise them as SourceErrors, and set ourselves as broken. class SSHLoader < Source attr_reader_cloned :labels attr_accessor :username, :password def initialize uri, username=nil, password=nil, start_offset=nil, usual=true, archived=false, id=nil raise ArgumentError, "not an mbox+ssh uri: #{uri.inspect}" unless uri =~ %r!^mbox\+ssh://! super uri, start_offset, usual, archived, id @parsed_uri = URI(uri) @username = username @password = password @uri = uri @cur_offset = start_offset opts = {} opts[:username] = @username if @username opts[:password] = @password if @password @f = SSHFile.new host, filename, opts @loader = Loader.new @f, start_offset, usual, archived, id ## heuristic: use the filename as a label, unless the file ## has a path that probably represents an inbox. @labels = [:unread] @labels << :inbox unless archived? @labels << File.basename(filename).intern unless File.dirname(filename) =~ /\b(var|usr|spool)\b/ end def connect; safely { @f.connect }; end def host; @parsed_uri.host; end def filename; @parsed_uri.path[1..-1] end def next return if broken? safely do offset, labels = @loader.next self.cur_offset = @loader.cur_offset # superclass keeps @cur_offset which is used by yaml [offset, (labels + @labels).uniq] # add our labels end end def end_offset safely { @f.size } end def cur_offset= o; @cur_offset = @loader.cur_offset = o; @dirty = true; end def id; @loader.id; end def id= o; @id = @loader.id = o; end # def cur_offset; @loader.cur_offset; end # think we'll be ok without this def to_s; @parsed_uri.to_s; end def safely begin yield rescue Net::SSH::Exception, SocketError, SSHFileError, SystemCallError => e m = "error communicating with SSH server #{host} (#{e.class.name}): #{e.message}" Redwood::log m self.broken_msg = @loader.broken_msg = m raise SourceError, m end end [:start_offset, :load_header, :load_message, :raw_header, :raw_full_message].each do |meth| define_method(meth) { |*a| safely { @loader.send meth, *a } } end end Redwood::register_yaml(SSHLoader, %w(uri username password cur_offset usual archived id)) end end