module Redwood

fucking imap fucking sucks. what the FUCK kind of committee of dunces designed this shit.

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 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!

and here's another thing. check out RFC2060 2.2.2 paragraph 5:

A client MUST be prepared to accept any server response at all
times.  This includes server data that was not requested.

yeah. that totally makes a lot of sense. and once again, the idiocy of the spec actually happens in practice. you'll request flags for one message, and get it interspersed with a random bunch of flags for some other messages, including a different set of flags for the same message! totally ok by the imap spec. totally retarded by any other metric.

fuck you, imap committee. you managed to design something nearly as shitty as mbox but goddamn THIRTY YEARS LATER.

Constants

BASE_DIR
COLOR_FN
CONFIG_FN
CONTACT_FN
DEFAULT_NEW_INDEX_TYPE
DRAFT_DIR
HOOK_DIR
LABEL_FN
LOCK_FN
SENT_FN
SOURCE_FN
SUICIDE_FN
VERSION
YAML_DATE
YAML_DOMAIN

Attributes

exceptions[R]
exceptions[R]

Public Class Methods

check_library_version_against(v) click to toggle source

to be called by entry points in bin/, to ensure that their versions match up against the library versions.

this is a perennial source of bug reports from people who both use git and have a gem version installed.

# File lib/sup.rb, line 196
  def check_library_version_against v
    unless Redwood::VERSION == v
      $stderr.puts <<EOS
Error: version mismatch!
The sup executable is at version #{v.inspect}.
The sup libraries are at version #{Redwood::VERSION.inspect}.

Your development environment may be picking up code from a
rubygems installation of sup.

If you're running from git with a commandline like

  ruby -Ilib #{$0}

try this instead:

  RUBY_INVOCATION="ruby -Ilib" ruby -Ilib #{$0}

You can also try `gem uninstall sup` and removing all Sup rubygems.

EOS
      abort
    end
  end
finish() click to toggle source
# File lib/sup.rb, line 136
def finish
  Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
  Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
  Redwood::BufferManager.deinstantiate! if Redwood::BufferManager.instantiated?
end
load_yaml_obj(fn, compress=false) click to toggle source
# File lib/sup.rb, line 107
def load_yaml_obj fn, compress=false
  o = if File.exists? fn
    if compress
      Zlib::GzipReader.open(fn) { |f| YAML::load f }
    else
      YAML::load_file fn
    end
  end
  if o.is_a?(Array)
    o.each { |x| x.after_unmarshal! if x.respond_to?(:after_unmarshal!) }
  else
    o.after_unmarshal! if o.respond_to?(:after_unmarshal!)
  end
  o
end
record_exception(e, name) click to toggle source
# File lib/sup.rb, line 64
def record_exception e, name
  @exception_mutex.synchronize do
    @exceptions ||= []
    @exceptions << [e, name]
  end
end
report_broken_sources(opts={}) click to toggle source

not really a good place for this, so I'll just dump it here.

a source error is either a FatalSourceError or an OutOfSyncSourceError. the superclass SourceError is just a generic.

# File lib/sup.rb, line 146
  def report_broken_sources opts={}
    return unless BufferManager.instantiated?

    broken_sources = SourceManager.sources.select { |s| s.error.is_a? FatalSourceError }
    unless broken_sources.empty?
      BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
        TextMode.new("Source error notification
-------------------------

Hi there. It looks like one or more message sources is reporting
errors. Until this is corrected, messages from these sources cannot
be viewed, and new messages will not be detected.

#{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n        ")}.join("\n\n")}
")
#' stupid ruby-mode
      end
    end

    desynced_sources = SourceManager.sources.select { |s| s.error.is_a? OutOfSyncSourceError }
    unless desynced_sources.empty?
      BufferManager.spawn_unless_exists("Out-of-sync source notification for #{broken_sources.join(',')}", opts) do
        TextMode.new("Out-of-sync source notification
-------------------------------

Hi there. It looks like one or more sources has fallen out of sync
with my index. This can happen when you modify these sources with
other email clients. (Sorry, I don't play well with others.)

Until this is corrected, messages from these sources cannot be viewed,
and new messages will not be detected. Luckily, this is easy to correct!

#{desynced_sources.map do |s|
  "Source: " + s.to_s + 
   "\n Error: " + s.error.message.wrap(70).join("\n        ") + 
   "\n   Fix: sup-sync --changed #{s.to_s}"
  end}
")
#' stupid ruby-mode
      end
    end
  end
reporting_thread(name) { || ... } click to toggle source
# File lib/sup.rb, line 71
def reporting_thread name
  if $opts[:no_threads]
    yield
  else
    ::Thread.new do
      begin
        yield
      rescue Exception => e
        record_exception e, name
      end
    end
  end
end
save_yaml_obj(o, fn, safe=false) click to toggle source

one-stop shop for yamliciousness

# File lib/sup.rb, line 88
def save_yaml_obj o, fn, safe=false
  o = if o.is_a?(Array)
    o.map { |x| (x.respond_to?(:before_marshal) && x.before_marshal) || x }
  elsif o.respond_to? :before_marshal
    o.before_marshal
  else
    o
  end

  if safe
    safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
    mode = File.stat(fn).mode if File.exists? fn
    File.open(safe_fn, "w", mode) { |f| f.puts o.to_yaml }
    FileUtils.mv safe_fn, fn
  else
    File.open(fn, "w") { |f| f.puts o.to_yaml }
  end
end
start() click to toggle source
# File lib/sup.rb, line 123
def start
  Redwood::SentManager.init $config[:sent_source] || 'sup://sent'
  Redwood::ContactManager.init Redwood::CONTACT_FN
  Redwood::LabelManager.init Redwood::LABEL_FN
  Redwood::AccountManager.init $config[:accounts]
  Redwood::DraftManager.init Redwood::DRAFT_DIR
  Redwood::UpdateManager.init
  Redwood::PollManager.init
  Redwood::CryptoManager.init
  Redwood::UndoManager.init
  Redwood::SourceManager.init
end

Private Instance Methods

check_library_version_against(v) click to toggle source

to be called by entry points in bin/, to ensure that their versions match up against the library versions.

this is a perennial source of bug reports from people who both use git and have a gem version installed.

# File lib/sup.rb, line 196
  def check_library_version_against v
    unless Redwood::VERSION == v
      $stderr.puts <<EOS
Error: version mismatch!
The sup executable is at version #{v.inspect}.
The sup libraries are at version #{Redwood::VERSION.inspect}.

Your development environment may be picking up code from a
rubygems installation of sup.

If you're running from git with a commandline like

  ruby -Ilib #{$0}

try this instead:

  RUBY_INVOCATION="ruby -Ilib" ruby -Ilib #{$0}

You can also try `gem uninstall sup` and removing all Sup rubygems.

EOS
      abort
    end
  end
finish() click to toggle source
# File lib/sup.rb, line 136
def finish
  Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
  Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
  Redwood::BufferManager.deinstantiate! if Redwood::BufferManager.instantiated?
end
load_yaml_obj(fn, compress=false) click to toggle source
# File lib/sup.rb, line 107
def load_yaml_obj fn, compress=false
  o = if File.exists? fn
    if compress
      Zlib::GzipReader.open(fn) { |f| YAML::load f }
    else
      YAML::load_file fn
    end
  end
  if o.is_a?(Array)
    o.each { |x| x.after_unmarshal! if x.respond_to?(:after_unmarshal!) }
  else
    o.after_unmarshal! if o.respond_to?(:after_unmarshal!)
  end
  o
end
record_exception(e, name) click to toggle source
# File lib/sup.rb, line 64
def record_exception e, name
  @exception_mutex.synchronize do
    @exceptions ||= []
    @exceptions << [e, name]
  end
end
report_broken_sources(opts={}) click to toggle source

not really a good place for this, so I'll just dump it here.

a source error is either a FatalSourceError or an OutOfSyncSourceError. the superclass SourceError is just a generic.

# File lib/sup.rb, line 146
  def report_broken_sources opts={}
    return unless BufferManager.instantiated?

    broken_sources = SourceManager.sources.select { |s| s.error.is_a? FatalSourceError }
    unless broken_sources.empty?
      BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
        TextMode.new("Source error notification
-------------------------

Hi there. It looks like one or more message sources is reporting
errors. Until this is corrected, messages from these sources cannot
be viewed, and new messages will not be detected.

#{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n        ")}.join("\n\n")}
")
#' stupid ruby-mode
      end
    end

    desynced_sources = SourceManager.sources.select { |s| s.error.is_a? OutOfSyncSourceError }
    unless desynced_sources.empty?
      BufferManager.spawn_unless_exists("Out-of-sync source notification for #{broken_sources.join(',')}", opts) do
        TextMode.new("Out-of-sync source notification
-------------------------------

Hi there. It looks like one or more sources has fallen out of sync
with my index. This can happen when you modify these sources with
other email clients. (Sorry, I don't play well with others.)

Until this is corrected, messages from these sources cannot be viewed,
and new messages will not be detected. Luckily, this is easy to correct!

#{desynced_sources.map do |s|
  "Source: " + s.to_s + 
   "\n Error: " + s.error.message.wrap(70).join("\n        ") + 
   "\n   Fix: sup-sync --changed #{s.to_s}"
  end}
")
#' stupid ruby-mode
      end
    end
  end
reporting_thread(name) { || ... } click to toggle source
# File lib/sup.rb, line 71
def reporting_thread name
  if $opts[:no_threads]
    yield
  else
    ::Thread.new do
      begin
        yield
      rescue Exception => e
        record_exception e, name
      end
    end
  end
end
save_yaml_obj(o, fn, safe=false) click to toggle source

one-stop shop for yamliciousness

# File lib/sup.rb, line 88
def save_yaml_obj o, fn, safe=false
  o = if o.is_a?(Array)
    o.map { |x| (x.respond_to?(:before_marshal) && x.before_marshal) || x }
  elsif o.respond_to? :before_marshal
    o.before_marshal
  else
    o
  end

  if safe
    safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
    mode = File.stat(fn).mode if File.exists? fn
    File.open(safe_fn, "w", mode) { |f| f.puts o.to_yaml }
    FileUtils.mv safe_fn, fn
  else
    File.open(fn, "w") { |f| f.puts o.to_yaml }
  end
end
start() click to toggle source
# File lib/sup.rb, line 123
def start
  Redwood::SentManager.init $config[:sent_source] || 'sup://sent'
  Redwood::ContactManager.init Redwood::CONTACT_FN
  Redwood::LabelManager.init Redwood::LABEL_FN
  Redwood::AccountManager.init $config[:accounts]
  Redwood::DraftManager.init Redwood::DRAFT_DIR
  Redwood::UpdateManager.init
  Redwood::PollManager.init
  Redwood::CryptoManager.init
  Redwood::UndoManager.init
  Redwood::SourceManager.init
end