]> git.cworth.org Git - sup/blobdiff - lib/sup.rb
bump version to 0.5
[sup] / lib / sup.rb
index cf46b8de7b1153f79d36acc7ce14c998e1bebbe5..910b333841c28654d2abee3d95d0b83194ad7fbc 100644 (file)
@@ -3,6 +3,8 @@ require 'yaml'
 require 'zlib'
 require 'thread'
 require 'fileutils'
 require 'zlib'
 require 'thread'
 require 'fileutils'
+require 'gettext'
+require 'curses'
 
 class Object
   ## this is for debugging purposes because i keep calling #id on the
 
 class Object
   ## this is for debugging purposes because i keep calling #id on the
@@ -12,8 +14,26 @@ class Object
   end
 end
 
   end
 end
 
+class Module
+  def yaml_properties *props
+    props = props.map { |p| p.to_s }
+    vars = props.map { |p| "@#{p}" }
+    klass = self
+    path = klass.name.gsub(/::/, "/")
+    
+    klass.instance_eval do
+      define_method(:to_yaml_properties) { vars }
+      define_method(:to_yaml_type) { "!#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}" }
+    end
+
+    YAML.add_domain_type("#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}", path) do |type, val|
+      klass.new(*props.map { |p| val[p] })
+    end
+  end
+end
+
 module Redwood
 module Redwood
-  VERSION = "0.0.7"
+  VERSION = "0.5"
 
   BASE_DIR   = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
   CONFIG_FN  = File.join(BASE_DIR, "config.yaml")
 
   BASE_DIR   = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
   CONFIG_FN  = File.join(BASE_DIR, "config.yaml")
@@ -23,46 +43,38 @@ module Redwood
   CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
   DRAFT_DIR  = File.join(BASE_DIR, "drafts")
   SENT_FN    = File.join(BASE_DIR, "sent.mbox")
   CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
   DRAFT_DIR  = File.join(BASE_DIR, "drafts")
   SENT_FN    = File.join(BASE_DIR, "sent.mbox")
+  LOCK_FN    = File.join(BASE_DIR, "lock")
+  SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
+  HOOK_DIR   = File.join(BASE_DIR, "hooks")
 
   YAML_DOMAIN = "masanjin.net"
   YAML_DATE = "2006-10-01"
 
 ## record exceptions thrown in threads nicely
 
   YAML_DOMAIN = "masanjin.net"
   YAML_DATE = "2006-10-01"
 
 ## record exceptions thrown in threads nicely
-  $exception = nil
-  def reporting_thread
-    ::Thread.new do
-      begin
-        yield
-      rescue Exception => e
-        File.open("sup-exception-log.txt", "w") do |f|
-          f.puts "--- #{e.class.name} at #{Time.now}"
-          f.puts e.message, e.backtrace
+  def reporting_thread name
+    if $opts[:no_threads]
+      yield
+    else
+      ::Thread.new do
+        begin
+          yield
+        rescue Exception => e
+          $exceptions ||= []
+          $exceptions << [e, name]
+          raise
         end
         end
-        $exception ||= e
-        raise
       end
     end
   end
   module_function :reporting_thread
 
 ## one-stop shop for yamliciousness
       end
     end
   end
   module_function :reporting_thread
 
 ## one-stop shop for yamliciousness
-  def register_yaml klass, props
-    vars = props.map { |p| "@#{p}" }
-    path = klass.name.gsub(/::/, "/")
-    
-    klass.instance_eval do
-      define_method(:to_yaml_properties) { vars }
-      define_method(:to_yaml_type) { "!#{YAML_DOMAIN},#{YAML_DATE}/#{path}" }
-    end
-
-    YAML.add_domain_type("#{YAML_DOMAIN},#{YAML_DATE}", path) do |type, val|
-      klass.new(*props.map { |p| val[p] })
-    end
-  end
-
-  def save_yaml_obj object, fn, compress=false
-    if compress
-      Zlib::GzipWriter.open(fn) { |f| f.puts object.to_yaml }
+  def save_yaml_obj object, fn, safe=false
+    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 object.to_yaml }
+      FileUtils.mv safe_fn, fn
     else
       File.open(fn, "w") { |f| f.puts object.to_yaml }
     end
     else
       File.open(fn, "w") { |f| f.puts object.to_yaml }
     end
@@ -87,53 +99,104 @@ module Redwood
     Redwood::DraftManager.new Redwood::DRAFT_DIR
     Redwood::UpdateManager.new
     Redwood::PollManager.new
     Redwood::DraftManager.new Redwood::DRAFT_DIR
     Redwood::UpdateManager.new
     Redwood::PollManager.new
+    Redwood::SuicideManager.new Redwood::SUICIDE_FN
+    Redwood::CryptoManager.new
   end
 
   def finish
   end
 
   def finish
-    Redwood::LabelManager.save
-    Redwood::ContactManager.save
-    Redwood::PersonManager.save
+    Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
+    Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
+    Redwood::PersonManager.save if Redwood::PersonManager.instantiated?
+    Redwood::BufferManager.deinstantiate! if Redwood::BufferManager.instantiated?
   end
 
   ## not really a good place for this, so I'll just dump it here.
   end
 
   ## not really a good place for this, so I'll just dump it here.
-  def report_broken_sources
+  ##
+  ## a source error is either a FatalSourceError or an OutOfSyncSourceError.
+  ## the superclass SourceError is just a generic.
+  def report_broken_sources opts={}
     return unless BufferManager.instantiated?
     return unless BufferManager.instantiated?
-    broken_sources = Index.usual_sources.select { |s| s.broken? }
+
+    broken_sources = Index.sources.select { |s| s.error.is_a? FatalSourceError }
     unless broken_sources.empty?
     unless broken_sources.empty?
-      BufferManager.spawn "Out-of-sync soure notification", TextMode.new(<<EOM)
+      BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
+        TextMode.new(<<EOM)
+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")}
+EOM
+#' stupid ruby-mode
+      end
+    end
+
+    desynced_sources = Index.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(<<EOM)
 Out-of-sync source notification
 -------------------------------
 
 Out-of-sync source notification
 -------------------------------
 
-Hi there. It looks like one or more sources have fallen out of sync
+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!
 
 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!
 
-#{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.broken_msg.wrap(70).join("\n        ") }.join('\n\n')}
+#{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}
 EOM
 EOM
+#' stupid ruby-mode
+      end
     end
   end
 
     end
   end
 
-  module_function :register_yaml, :save_yaml_obj, :load_yaml_obj, :start, :finish, :report_broken_sources
+  module_function :save_yaml_obj, :load_yaml_obj, :start, :finish,
+                  :report_broken_sources
 end
 
 ## set up default configuration file
 if File.exists? Redwood::CONFIG_FN
   $config = Redwood::load_yaml_obj Redwood::CONFIG_FN
 else
 end
 
 ## set up default configuration file
 if File.exists? Redwood::CONFIG_FN
   $config = Redwood::load_yaml_obj Redwood::CONFIG_FN
 else
+  require 'etc'
+  require 'socket'
+  name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first rescue nil
+  name ||= ENV["USER"]
+  email = ENV["USER"] + "@" + 
+    begin
+      Socket.gethostbyname(Socket.gethostname).first
+    rescue SocketError
+      Socket.gethostname
+    end
+
   $config = {
     :accounts => {
       :default => {
   $config = {
     :accounts => {
       :default => {
-        :name => "Your Name Here",
-        :email => "your.email.here@domain.tld",
+        :name => name,
+        :email => email,
         :alternates => [],
         :sendmail => "/usr/sbin/sendmail -oem -ti",
         :signature => File.join(ENV["HOME"], ".signature")
       }
     },
         :alternates => [],
         :sendmail => "/usr/sbin/sendmail -oem -ti",
         :signature => File.join(ENV["HOME"], ".signature")
       }
     },
-    :editor => ENV["EDITOR"] || "/usr/bin/vi",
+    :editor => ENV["EDITOR"] || "/usr/bin/vim -f -c 'setlocal spell spelllang=en_us' -c 'set filetype=mail'",
+    :thread_by_subject => false,
+    :edit_signature => false,
+    :ask_for_cc => true,
+    :ask_for_bcc => false,
+    :ask_for_subject => true,
+    :confirm_no_attachments => true,
+    :confirm_top_posting => true,
+    :discard_snippets_from_encrypted_messages => false,
   }
   begin
     FileUtils.mkdir_p Redwood::BASE_DIR
   }
   begin
     FileUtils.mkdir_p Redwood::BASE_DIR
@@ -144,7 +207,39 @@ else
 end
 
 require "sup/util"
 end
 
 require "sup/util"
+require "sup/hook"
+
+## we have to initialize this guy first, because other classes must
+## reference it in order to register hooks, and they do that at parse
+## time.
+Redwood::HookManager.new Redwood::HOOK_DIR
+
+## everything we need to get logging working
+require "sup/buffer"
+require "sup/keymap"
+require "sup/mode"
+require "sup/modes/scroll-mode"
+require "sup/modes/text-mode"
+require "sup/modes/log-mode"
+require "sup/logger"
+module Redwood
+  def log s; Logger.log s; end
+  module_function :log
+end
+
+## determine encoding and character set
+  $encoding = Locale.current.charset
+  if $encoding
+    Redwood::log "using character set encoding #{$encoding.inspect}"
+  else
+    Redwood::log "warning: can't find character set by using locale, defaulting to utf-8"
+    $encoding = "utf-8"
+  end
+
+## now everything else (which can feel free to call Redwood::log at load time)
 require "sup/update"
 require "sup/update"
+require "sup/suicide"
+require "sup/message-chunks"
 require "sup/message"
 require "sup/source"
 require "sup/mbox"
 require "sup/message"
 require "sup/source"
 require "sup/mbox"
@@ -155,17 +250,14 @@ require "sup/account"
 require "sup/thread"
 require "sup/index"
 require "sup/textfield"
 require "sup/thread"
 require "sup/index"
 require "sup/textfield"
-require "sup/buffer"
-require "sup/keymap"
-require "sup/mode"
 require "sup/colormap"
 require "sup/label"
 require "sup/contact"
 require "sup/tagger"
 require "sup/draft"
 require "sup/poll"
 require "sup/colormap"
 require "sup/label"
 require "sup/contact"
 require "sup/tagger"
 require "sup/draft"
 require "sup/poll"
-require "sup/modes/scroll-mode"
-require "sup/modes/text-mode"
+require "sup/crypto"
+require "sup/horizontal-selector"
 require "sup/modes/line-cursor-mode"
 require "sup/modes/help-mode"
 require "sup/modes/edit-message-mode"
 require "sup/modes/line-cursor-mode"
 require "sup/modes/help-mode"
 require "sup/modes/edit-message-mode"
@@ -182,16 +274,11 @@ require "sup/modes/search-results-mode"
 require "sup/modes/person-search-results-mode"
 require "sup/modes/inbox-mode"
 require "sup/modes/buffer-list-mode"
 require "sup/modes/person-search-results-mode"
 require "sup/modes/inbox-mode"
 require "sup/modes/buffer-list-mode"
-require "sup/modes/log-mode"
 require "sup/modes/poll-mode"
 require "sup/modes/poll-mode"
-require "sup/logger"
+require "sup/modes/file-browser-mode"
+require "sup/modes/completion-mode"
 require "sup/sent"
 
 require "sup/sent"
 
-module Redwood
-  def log s; Logger.log s; end
-  module_function :log
-end
-
 $:.each do |base|
   d = File.join base, "sup/share/modes/"
   Redwood::Mode.load_all_modes d if File.directory? d
 $:.each do |base|
   d = File.join base, "sup/share/modes/"
   Redwood::Mode.load_all_modes d if File.directory? d