From: wmorgan Date: Tue, 2 Jan 2007 16:00:44 +0000 (+0000) Subject: many changes (while on the airplane). X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=7105b6ea2722b94d9a53f69f986aaf3dac62c9a4;p=sup many changes (while on the airplane). - reworked source error handling so that it finally works well. - reworked ssh and imap to conform to that. - cleaned up logging and comments all over. - made sup-import use highline and allow the user to use previously-defined account information. - made the buffer line expandable git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@135 5c8cc53c-5e98-4d25-b20a-d8db53a31250 --- diff --git a/bin/sup b/bin/sup index 4cc1ecf..3066d6d 100644 --- a/bin/sup +++ b/bin/sup @@ -224,8 +224,8 @@ EOS I'm very sorry, but it seems that an error occurred in Sup. Please accept my sincere apologies. If you don't mind, please send the backtrace below and a brief report of the circumstances -to user wmorgan-sup at site masanjin dot net so that I might -address this problem. Thank you! +to wmorgan-sup at masanjin dot nets so that I might address this +problem. Thank you! Sincerely, William diff --git a/bin/sup-import b/bin/sup-import index a31387d..d4fbb27 100644 --- a/bin/sup-import +++ b/bin/sup-import @@ -1,9 +1,11 @@ #!/usr/bin/env ruby +require 'uri' require 'rubygems' -require 'highline' +require 'highline/import' require "sup" + Thread.abort_on_exception = true # make debugging possible class Float @@ -61,6 +63,37 @@ EOS exit end +## for sources that require login information, prompt the user for +## that. also provide a list of previously-defined login info to +## choose from, if any. +def get_login_info uri, sources + uri = URI(uri) + accounts = sources.map do |s| + next unless s.respond_to?(:username) + suri = URI(s.uri) + [suri.host, s.username, s.password] + end.compact.uniq.sort_by { |h, u, p| h == uri.host ? 0 : 1 } + + username, password = nil, nil + unless accounts.empty? + say "Would you like to use the same account as for a previous source?" + choose do |menu| + accounts.each do |host, olduser, oldpw| + menu.choice("Use the account info for #{olduser}@#{host}") { username, password = olduser, oldpw } + end + menu.choice("Use a new account") { } + end + end + + unless username && password + username = ask("Username for #{uri.host}: "); + password = ask("Password for #{uri.host}: ") { |q| q.echo = false } + end + + [username, password] +end + + educate_user if ARGV.member? '--help' archive = ARGV.delete "--archive" @@ -83,31 +116,27 @@ if(o = ARGV.find { |x| x =~ /^--/ }) educate_user end +$terminal.wrap_at = :auto Redwood::start - index = Redwood::Index.new index.load -h = HighLine.new - -sources = ARGV.map do |fn| - fn = "mbox://#{fn}" unless fn =~ %r!://! - source = index.source_for fn +sources = ARGV.map do |uri| + uri = "mbox://#{uri}" unless uri =~ %r!://! + source = index.source_for uri unless source source = - case fn + case uri when %r!^mbox\+ssh://! - username = h.ask("Username for #{fn}: "); - password = h.ask("Password for #{fn}: ") { |q| q.echo = false } - puts # why? - Redwood::MBox::SSHLoader.new(fn, username, password, nil, !unusual, !!archive) + say "For SSH connections, if you will use public key authentication, you may leave the username and password blank." + say "\n" + username, password = get_login_info uri, index.sources + Redwood::MBox::SSHLoader.new(uri, username, password, nil, !unusual, !!archive) when %r!^imaps?://! - username = h.ask("Username for #{fn}: "); - password = h.ask("Password for #{fn}: ") { |q| q.echo = false } - puts # why? - Redwood::IMAP.new(fn, username, password, nil, !unusual, !!archive) + username, password = get_login_info uri, sources + Redwood::IMAP.new(uri, username, password, nil, !unusual, !!archive) else - Redwood::MBox::Loader.new(fn, nil, !unusual, !!archive) + Redwood::MBox::Loader.new(uri, nil, !unusual, !!archive) end index.add_source source end @@ -128,7 +157,7 @@ start = Time.now begin sources.each do |source| if source.broken? - puts "error loading messages from #{source}: #{source.broken_msg}" + $stderr.puts "error loading messages from #{source}: #{source.broken_msg}" next end next if source.done? diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb index 1d5ec22..d672bfe 100644 --- a/lib/sup/buffer.rb +++ b/lib/sup/buffer.rb @@ -44,6 +44,16 @@ end module Redwood +## could be a bottleneck, but doesn't seem to significantly slow +## things down. + +class SafeNcurses + def self.method_missing meth, *a, &b + @mutex ||= Mutex.new + @mutex.synchronize { Ncurses.send meth, *a, &b } + end +end + class Buffer attr_reader :mode, :x, :y, :width, :height, :title bool_reader :dirty @@ -60,7 +70,8 @@ class Buffer def content_height; @height - 1; end def content_width; @width; end - def resize rows, cols + def resize rows, cols + return if rows == @width && cols == @height @width = cols @height = rows mode.resize rows, cols @@ -179,15 +190,15 @@ class BufferManager def completely_redraw_screen return if @freeze - Ncurses.clear + SafeNcurses.clear @dirty = true draw_screen end def handle_resize return if @freeze - rows, cols = Ncurses.rows, Ncurses.cols - @buffers.each { |b| b.resize rows - 1, cols } + rows, cols = SafeNcurses.rows, SafeNcurses.cols + @buffers.each { |b| b.resize rows - minibuf_lines, cols } completely_redraw_screen flash "resized to #{rows}x#{cols}" end @@ -199,15 +210,19 @@ class BufferManager ## (currently we only have one buffer visible at a time). ## TODO: reenable this if we allow multiple buffers false && @buffers.inject(@dirty) do |dirty, buf| - dirty ? buf.draw : buf.redraw - dirty || buf.dirty? + buf.resize SafeNcurses.rows - minibuf_lines, SafeNcurses.cols + @dirty ? buf.draw : buf.redraw end ## quick hack - true && (@dirty ? @buffers.last.draw : @buffers.last.redraw) - + if true + buf = @buffers.last + buf.resize SafeNcurses.rows - minibuf_lines, SafeNcurses.cols + @dirty ? buf.draw : buf.redraw + end + draw_minibuf unless skip_minibuf @dirty = false - Ncurses.doupdate + SafeNcurses.doupdate end ## gets the mode from the block, which is only called if the buffer @@ -228,13 +243,13 @@ class BufferManager realtitle = title num = 2 while @name_map.member? realtitle - realtitle = "#{title} #{num}" + realtitle = "#{title} <#{num}>" num += 1 end Redwood::log "spawning buffer \"#{realtitle}\"" - width = opts[:width] || Ncurses.cols - height = opts[:height] || Ncurses.rows - 1 + width = opts[:width] || SafeNcurses.cols + height = opts[:height] || SafeNcurses.rows - 1 ## since we are currently only doing multiple full-screen modes, ## use stdscr for each window. once we become more sophisticated, @@ -242,9 +257,7 @@ class BufferManager ## ## w = Ncurses::WINDOW.new(height, width, (opts[:top] || 0), ## (opts[:left] || 0)) - w = Ncurses.stdscr - raise "nil window" unless w - + w = SafeNcurses.stdscr b = Buffer.new w, mode, width, height, :title => realtitle mode.buffer = b @name_map[realtitle] = b @@ -279,8 +292,8 @@ class BufferManager end def ask domain, question, default=nil - @textfields[domain] ||= TextField.new Ncurses.stdscr, Ncurses.rows - 1, 0, - Ncurses.cols + @textfields[domain] ||= TextField.new SafeNcurses.stdscr, SafeNcurses.rows - 1, 0, + SafeNcurses.cols tf = @textfields[domain] ## this goddamn ncurses form shit is a fucking 1970's @@ -295,8 +308,8 @@ class BufferManager ret = nil @freeze = true tf.position_cursor - Ncurses.refresh - while tf.handle_input(Ncurses.nonblocking_getch); end + SafeNcurses.refresh + while tf.handle_input(SafeNcurses.nonblocking_getch); end @freeze = false ret = tf.value @@ -311,15 +324,15 @@ class BufferManager accept = accept.split(//).map { |x| x[0] } if accept flash question - Ncurses.curs_set 1 - Ncurses.move Ncurses.rows - 1, question.length + 1 - Ncurses.refresh + SafeNcurses.curs_set 1 + SafeNcurses.move SafeNcurses.rows - 1, question.length + 1 + SafeNcurses.refresh ret = nil done = false @freeze = true until done - key = Ncurses.nonblocking_getch + key = SafeNcurses.nonblocking_getch if key == Ncurses::KEY_CANCEL done = true elsif (accept && accept.member?(key)) || !accept @@ -328,10 +341,10 @@ class BufferManager end end @freeze = false - Ncurses.curs_set 0 + SafeNcurses.curs_set 0 erase_flash draw_screen - Ncurses.curs_set 0 + SafeNcurses.curs_set 0 ret end @@ -348,12 +361,16 @@ class BufferManager end end + def minibuf_lines; [(@flash ? 1 : 0) + @minibuf_stack.compact.size, 1].max; end + def draw_minibuf - s = @flash || @minibuf_stack.reverse.find { |x| x } || "" - - Ncurses.attrset Colormap.color_for(:none) - Ncurses.mvaddstr Ncurses.rows - 1, 0, s + (" " * [Ncurses.cols - s.length, - 0].max) + SafeNcurses.attrset Colormap.color_for(:none) + m = @minibuf_stack.compact + m << @flash if @flash + m << "" if m.empty? + m.each_with_index do |s, i| + SafeNcurses.mvaddstr SafeNcurses.rows - i - 1, 0, s + (" " * [SafeNcurses.cols - s.length, 0].max) + end end def say s, id=nil @@ -361,12 +378,14 @@ class BufferManager @minibuf_stack[id] = s unless @freeze draw_screen - Ncurses.refresh + SafeNcurses.refresh end if block_given? - yield - clear id - return + begin + yield + ensure + clear id + end end id end @@ -377,10 +396,12 @@ class BufferManager @flash = s unless @freeze draw_screen - Ncurses.refresh + SafeNcurses.refresh end end + ## a little tricky because we can't just delete_at id because ids + ## are relative (they're positions into the array). def clear id @minibuf_stack[id] = nil if id == @minibuf_stack.length - 1 @@ -389,18 +410,19 @@ class BufferManager @minibuf_stack.delete_at i end end + unless @freeze draw_screen - Ncurses.refresh + SafeNcurses.refresh end end def shell_out command @freeze = true - Ncurses.endwin + SafeNcurses.endwin system command - Ncurses.refresh - Ncurses.curs_set 0 + SafeNcurses.refresh + SafeNcurses.curs_set 0 @freeze = false end end diff --git a/lib/sup/imap.rb b/lib/sup/imap.rb index d1c6b04..06ade31 100644 --- a/lib/sup/imap.rb +++ b/lib/sup/imap.rb @@ -1,37 +1,39 @@ require 'uri' require 'net/imap' require 'stringio' +require 'time' ## fucking imap fucking sucks. what the FUCK kind of committee of ## dunces designed this shit. -## you see, imap touts 'unique ids' for messages, which are to be used -## for cross-session identification. great, just what sup needs! only, -## it turns out the uids can be invalidated every time some arbitrary -## 'uidvalidity' value changes on the server, and 'uidvalidity' has no -## restrictions. it can change any time you log in. it can change -## EVERY time you log in. of course the imap spec "strongly +## imap talks about 'unique ids' for messages, to be used for +## cross-session identification. great---just what sup needs! except +## it turns out the uids can be invalidated every time the +## 'uidvalidity' value changes on the server, and 'uidvalidity' can +## change without restriction. it can change any time you log in. it +## can change EVERY time you log in. of course the imap spec "strongly ## recommends" that it never change, but there's nothing to stop -## people from just setting it to the current time, and in fact that's -## exactly what the one imap server i have at my disposal does. thus -## the so-called uids are absolutely useless and imap provides no -## cross-session way of uniquely identifying a message. but thanks for -## the "strong recommendation", guys! - -## right now i'm using the 'internal date' and the size of each -## message to uniquely identify it, and i have to scan over the entire -## mailbox each time i open it to map those things to message ids, and -## we'll just hope that there are no collisions. ho ho! that's a -## perfectly reasonable solution! - -## fuck you imap committee. you managed to design something as shitty +## people from just setting it to the current timestamp, and in fact +## that's exactly what the one imap server i have at my disposal +## does. thus the so-called uids are absolutely useless and imap +## provides no cross-session way of uniquely identifying a +## message. but thanks for the "strong recommendation", guys! + +## so right now i'm using the 'internal date' and the size of each +## message to uniquely identify it, and i scan over the entire mailbox +## each time i open it to map those things to message ids. that can be +## slow for large mailboxes, and we'll just have to hope that there +## are no collisions. ho ho! a perfectly reasonable solution! + +## fuck you, imap committee. you managed to design something as shitty ## as mbox but goddamn THIRTY YEARS LATER. module Redwood class IMAP < Source attr_reader_cloned :labels - + attr_accessor :username, :password + def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil raise ArgumentError, "username and password must be specified" unless username && password raise ArgumentError, "not an imap uri" unless uri =~ %r!imaps?://! @@ -47,6 +49,7 @@ class IMAP < Source @labels = [:unread] @labels << :inbox unless archived? @labels << mailbox.intern unless mailbox =~ /inbox/i || mailbox.nil? + @mutex = Mutex.new end def connect @@ -69,24 +72,28 @@ class IMAP < Source Redwood::log "connecting to #{@parsed_uri.host} port #{ssl? ? 993 : 143}, ssl=#{ssl?} ..." sid = BufferManager.say "Connecting to IMAP server #{host}..." if BufferManager.instantiated? - ::Thread.new do + Redwood::reporting_thread do begin #raise Net::IMAP::ByeResponseError, "simulated imap failure" - @imap = Net::IMAP.new host, ssl? ? 993 : 143, ssl? + # @imap = Net::IMAP.new host, ssl? ? 993 : 143, ssl? + sleep 3 BufferManager.say "Logging in...", sid if BufferManager.instantiated? - @imap.authenticate 'LOGIN', @username, @password + # @imap.authenticate 'LOGIN', @username, @password + sleep 3 BufferManager.say "Sizing mailbox...", sid if BufferManager.instantiated? - @imap.examine mailbox - last_id = @imap.responses["EXISTS"][-1] - + # @imap.examine mailbox + # last_id = @imap.responses["EXISTS"][-1] + sleep 1 + BufferManager.say "Reading headers (because IMAP sucks)...", sid if BufferManager.instantiated? - values = @imap.fetch(1 .. last_id, ['RFC822.SIZE', 'INTERNALDATE']) - + # values = @imap.fetch(1 .. last_id, ['RFC822.SIZE', 'INTERNALDATE']) + sleep 3 + + raise Net::IMAP::ByeResponseError, "simulated imap failure" Redwood::log "successfully connected to #{@parsed_uri}" - + values.each do |v| - msize, mdate = v.attr['RFC822.SIZE'], Time.parse(v.attr["INTERNALDATE"]) - id = sprintf("%d.%07d", mdate.to_i, msize).to_i + id = make_id v @ids << id @imap_ids[id] = v.seqno end @@ -99,10 +106,17 @@ class IMAP < Source end end.join + @mutex.unlock !!@imap end private :connect + def make_id imap_stuff + msize, mdate = imap_stuff.attr['RFC822.SIZE'], Time.parse(imap_stuff.attr["INTERNALDATE"]) + sprintf("%d.%07d", mdate.to_i, msize).to_i + end + private :make_id + def host; @parsed_uri.host; end def mailbox; @parsed_uri.path[1..-1] end ##XXXX TODO handle nil def ssl?; @parsed_uri.scheme == 'imaps' end @@ -117,31 +131,38 @@ class IMAP < Source ## load the full header text def raw_header id - connect or raise SourceError, broken_msg - get_imap_field(id, 'RFC822.HEADER').gsub(/\r\n/, "\n") + @mutex.synchronize do + connect or raise SourceError, broken_msg + get_imap_field(id, 'RFC822.HEADER').gsub(/\r\n/, "\n") + end end def raw_full_message id - connect or raise SourceError, broken_msg - get_imap_field(id, 'RFC822').gsub(/\r\n/, "\n") + @mutex.synchronize do + connect or raise SourceError, broken_msg + get_imap_field(id, 'RFC822').gsub(/\r\n/, "\n") + end end def get_imap_field id, field - imap_id = @imap_ids[id] or raise SourceError, "Unknown message id #{id}. It is likely that messages have been deleted from this IMAP mailbox. Please run sup-import --rebuild #{to_s} in order to correct this problem." - - f = + f = nil + @mutex.synchronize do + imap_id = @imap_ids[id] or raise SourceError, "Unknown message id #{id}. It is likely that messages have been deleted from this IMAP mailbox." begin - @imap.fetch imap_id, field + f = @imap.fetch imap_id, [field, 'RFC822.SIZE', 'INTERNALDATE'] + got_id = make_id f + raise SourceError, "IMAP message mismatch: requested #{id}, got #{got_id}. It is likely the IMAP mailbox has been modified." unless got_id == id rescue Net::IMAP::Error => e raise SourceError, e.message end - raise SourceError, "null IMAP field '#{field}' for message with id #{id} imap id #{imap_id}" if f.nil? + raise SourceError, "null IMAP field '#{field}' for message with id #{id} imap id #{imap_id}" if f.nil? + end f[0].attr[field] end private :get_imap_field def each - connect or raise SourceError, broken_msg + @mutex.synchronize { connect or raise SourceError, broken_msg } start = @ids.index(cur_offset || start_offset) start.upto(@ids.length - 1) do |i| @@ -152,11 +173,11 @@ class IMAP < Source end def start_offset - connect or raise SourceError, broken_msg + @mutex.synchronize { connect or raise SourceError, broken_msg } @ids.first end def end_offset - connect or raise SourceError, broken_msg + @mutex.synchronize { connect or raise SourceError, broken_msg } @ids.last end end diff --git a/lib/sup/index.rb b/lib/sup/index.rb index 9c0f056..c407894 100644 --- a/lib/sup/index.rb +++ b/lib/sup/index.rb @@ -19,8 +19,7 @@ end class Index include Singleton - attr_reader :index # debugging only - + attr_reader :index def initialize dir=BASE_DIR @dir = dir @sources = {} @@ -57,6 +56,7 @@ class Index def source_for name; @sources.values.find { |s| s.is_source_for? name }; end def usual_sources; @sources.values.find_all { |s| s.usual? }; end + def sources; @sources.values; end def load_index dir=File.join(@dir, "ferret") if File.exists? dir diff --git a/lib/sup/label.rb b/lib/sup/label.rb index 95c71fb..7b0bbdf 100644 --- a/lib/sup/label.rb +++ b/lib/sup/label.rb @@ -28,11 +28,8 @@ class LabelManager end def user_labels; @labels.keys; end - def << t; @labels[t] = true unless @labels.member?(t) || RESERVED_LABELS.member?(t); end - def delete t; @labels.delete t; end - def save File.open(@fn, "w") { |f| f.puts @labels.keys } end diff --git a/lib/sup/mbox/ssh-file.rb b/lib/sup/mbox/ssh-file.rb index e8290a6..9e91d70 100644 --- a/lib/sup/mbox/ssh-file.rb +++ b/lib/sup/mbox/ssh-file.rb @@ -13,11 +13,8 @@ class SSHFileError < StandardError; end ## straight through the mbox (an import) or we're reading a few ## messages at a time (viewing messages) so the latency is not a problem. -## all of the methods here catch SSHFileErrors, SocketErrors, and -## Net::SSH::Exceptions and reraise them as SourceErrors. due to this -## and to the logging, this class is somewhat tied to Sup, but it -## wouldn't be too difficult to remove those bits and make it more -## general-purpose. +## all of the methods here can throw SSHFileErrors, SocketErrors, +## Net::SSH::Exceptions and Errno::ENOENTs. ## debugging TODO: remove me def debug s @@ -92,30 +89,48 @@ class SSHFile @ssh_opts = ssh_opts @file_size = nil @offset = 0 + @say_id = nil + @broken_msg = nil + end + + def broken?; !@broken_msg.nil?; end + + def say s + @say_id = BufferManager.say s, @say_id if BufferManager.instantiated? + Redwood::log s + end + private :say + + def shutup + BufferManager.clear @say_id if BufferManager.instantiated? + @say_id = nil end def connect return if @session + raise SSHFileError, @broken_msg if broken? - Redwood::log "starting SSH session to #@host for #@fn..." - sid = BufferManager.say "Connecting to SSH host #{@host}..." if BufferManager.instantiated? + say "Opening SSH connection to #{@host}..." begin - @session = Net::SSH.start @host, @ssh_opts - MBox::debug "starting SSH shell..." - BufferManager.say "Starting SSH shell...", sid if BufferManager.instantiated? - @shell = @session.shell.sync - MBox::debug "checking for file existence..." + #raise SSHFileError, "simulated SSH file error" + #@session = Net::SSH.start @host, @ssh_opts + sleep 3 + say "Starting SSH shell..." + # @shell = @session.shell.sync + sleep 3 + say "Checking for #@fn..." + sleep 1 + raise Errno::ENOENT, @fn raise Errno::ENOENT, @fn unless @shell.test("-e #@fn").status == 0 - MBox::debug "SSH is ready" - ensure - BufferManager.clear sid if BufferManager.instantiated? + ensure + shutup end end - def eof?; raise "offset #@offset size #{size}" unless @offset && size; @offset >= size; end - def eof; eof?; end # lame but IO does this and rmail depends on it - def seek loc; raise "nil" unless loc; @offset = loc; end + def eof?; @offset >= size; end + def eof; eof?; end # lame but IO's method is named this and rmail calls that + def seek loc; @offset = loc; end def tell; @offset; end def total; size; end @@ -129,11 +144,9 @@ class SSHFile def gets return nil if eof? - make_buf_include @offset expand_buf_forward while @buf.index("\n", @offset).nil? && @buf.endd < size - - with(@buf[@offset .. (@buf.index("\n", @offset) || -1)]) { |line| @offset += line.length } + returning(@buf[@offset .. (@buf.index("\n", @offset) || -1)]) { |line| @offset += line.length } end def read n @@ -152,22 +165,18 @@ private begin result = @shell.send_command cmd raise SSHFileError, "Failure during remote command #{cmd.inspect}: #{result.stderr[0 .. 100]}" unless result.status == 0 - rescue Net::SSH::Exception # these happen occasionally for no apparent reason. gotta love that nondeterminism! retry if (retries += 1) < 3 raise end - result.stdout - rescue Net::SSH::Exception, SocketError, Errno::ENOENT => e - @session = nil - Redwood::log "error connecting to SSH server: #{e.message}" - raise SourceError, "error connecting to SSH server: #{e.message}" + rescue Net::SSH::Exception, SSHFileError, Errno::ENOENT => e + @broken_msg = e.message + raise end + result.stdout end def get_bytes offset, size - #MBox::debug "! request for [#{offset}, #{offset + size}); buf is #@buf" - raise "wtf: offset #{offset} size #{size}" if size == 0 || offset < 0 do_remote "tail -c +#{offset + 1} #@fn | head -c #{size}", size end diff --git a/lib/sup/mbox/ssh-loader.rb b/lib/sup/mbox/ssh-loader.rb index bfbce0e..33aac12 100644 --- a/lib/sup/mbox/ssh-loader.rb +++ b/lib/sup/mbox/ssh-loader.rb @@ -3,8 +3,13 @@ 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://! @@ -32,22 +37,49 @@ class SSHLoader < Source end def host; @parsed_uri.host; end - def filename; @parsed_uri.path[1..-1] end ##XXXX TODO handle nil + def filename; @parsed_uri.path[1..-1] end def next - offset, labels = @loader.next - self.cur_offset = @loader.cur_offset # only necessary because YAML is a PITA - [offset, (labels + @labels).uniq] + return if broken? + begin + 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 + rescue Net::SSH::Exception, SocketError, SSHFileError, Errno::ENOENT => e + recover_from e + end + end + + def end_offset + begin + @f.size + rescue Net::SSH::Exception, SocketError, SSHFileError, Errno::ENOENT => e + recover_from e + end end - def end_offset; @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 + # def cur_offset; @loader.cur_offset; end # think we'll be ok without this def to_s; @parsed_uri.to_s; end - defer_all_other_method_calls_to :loader + def recover_from 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 + + [:start_offset, :load_header, :load_message, :raw_header, :raw_full_message].each do |meth| + define_method meth do |*a| + begin + @loader.send meth, *a + rescue Net::SSH::Exception, SocketError, SSHFileError, Errno::ENOENT => e + recover_from e + end + end + end end Redwood::register_yaml(SSHLoader, %w(uri username password cur_offset usual archived id)) diff --git a/lib/sup/message.rb b/lib/sup/message.rb index 666da1e..efa2e73 100644 --- a/lib/sup/message.rb +++ b/lib/sup/message.rb @@ -189,12 +189,10 @@ class Message < e + self.broken_msg = e.message end end @@ -80,7 +81,7 @@ protected def broken_msg= m @broken_msg = m - Redwood::log "#{to_s}: #{m}" +# Redwood::log "#{to_s}: #{m}" end end diff --git a/lib/sup/util.rb b/lib/sup/util.rb index 0d94e14..ef2fdb1 100644 --- a/lib/sup/util.rb +++ b/lib/sup/util.rb @@ -42,7 +42,7 @@ class Object ## ## i'm sure there's pithy comment i could make here about the ## superiority of lisp, but fuck lisp. - def with x; yield x; x; end + def returning x; yield x; x; end end class String