]> git.cworth.org Git - sup/commitdiff
labels now fully determined by sources.yaml, and lots of improvements to sup-config
authorwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Sun, 3 Jun 2007 22:00:24 +0000 (22:00 +0000)
committerwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Sun, 3 Jun 2007 22:00:24 +0000 (22:00 +0000)
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@427 5c8cc53c-5e98-4d25-b20a-d8db53a31250

bin/sup-add
bin/sup-config
bin/sup-sync
doc/TODO
lib/sup.rb
lib/sup/imap.rb
lib/sup/maildir.rb
lib/sup/mbox/loader.rb
lib/sup/mbox/ssh-loader.rb
lib/sup/poll.rb
lib/sup/source.rb

index 97a579badcb1c8a395818c2a82d683453019cb12..f0def12a678ca5f12ddc115eddcd2063807ed057 100644 (file)
@@ -37,6 +37,7 @@ Options are:
 EOS
   opt :archive, "Automatically archive all new messages from these sources."
   opt :unusual, "Do not automatically poll these sources for new messages."
+  opt :labels, "A comma-separated set of labels to apply to all messages from this source", :type => String
   opt :force_new, "Create a new account for this source, even if one already exists."
 end
 
@@ -80,6 +81,8 @@ index = Redwood::Index.new
 index.load
 
 ARGV.each do |uri|
+  labels = $opts[:labels] ? $opts[:labels].split(/\s*,\s*/).uniq : []
+
   if !$opts[:force_new] && index.source_for(uri) 
     say "Already know about #{uri}; skipping."
     next
@@ -94,14 +97,14 @@ ARGV.each do |uri|
       say "For SSH connections, if you will use public key authentication, you may leave the username and password blank."
       say ""
       username, password = get_login_info uri, index.sources
-      Redwood::MBox::SSHLoader.new uri, username, password, nil, !$opts[:unusual], $opts[:archive]
+      Redwood::MBox::SSHLoader.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, labels
     when "imap", "imaps"
       username, password = get_login_info uri, index.sources
-      Redwood::IMAP.new uri, username, password, nil, !$opts[:unusual], $opts[:archive]
+      Redwood::IMAP.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, labels
     when "maildir"
-      Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive]
+      Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
     when "mbox"
-      Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive]
+      Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
     else
       Trollop::die "Unknown source type #{parsed_uri.scheme.inspect}"      
     end
index 5b75b2b83b0c04410102dfc8daa5ae559ead6eb8..1cd244ace1cb96a868072ffb896337e0f38f20e4 100644 (file)
@@ -21,11 +21,11 @@ end #' stupid ruby-mode
 
 def axe q, default=nil
   ans = 
-  if default && !default.empty?
-    ask "#{q} (enter for \"#{default}\"): "
-  else
-    ask "#{q}: "
-  end
+    if default && !default.empty?
+      ask "#{q} (enter for \"#{default}\"): "
+    else
+      ask "#{q}: "
+    end
   ans.empty? ? default : ans
 end
 
@@ -42,7 +42,7 @@ def add_source
 
   say "Ok, adding a new source."
   choose do |menu|
-    menu.prompt = "What type of mail source is it?"
+    menu.prompt = "What type of mail source is it? "
     menu.choice("mbox file") { type = :mbox }
     menu.choice("maildir directory") { type = :maildir }
     menu.choice("remote mbox file (accessible via ssh)") { type = :mboxssh }
@@ -52,40 +52,57 @@ def add_source
   end
 
   while true do
-    say "Now for the details."
+    say "Ok, now for the details."
 
-    components = 
+    default_labels, components = 
       case type
       when :mbox
-        fn = axe "What's the full path to the mbox file?", ENV["MAIL"] #"srm
+        $last_fn ||= ENV["MAIL"]
+        fn = axe "What's the full path to the mbox file?", $last_fn #"srm
         return if fn.nil? || fn.empty?
-        { :scheme => "mbox", :path => fn }
+
+        $last_fn = fn
+        [Redwood::MBox::Loader.suggest_labels_for(fn),
+         { :scheme => "mbox", :path => fn }]
       when :maildir
-        fn = axe "What's the full path to the maildir directory?", ENV["MAIL"] #"srm
+        $last_fn ||= ENV["MAIL"]
+        fn = axe "What's the full path to the maildir directory?", $last_fn #"srm
         return if fn.nil? || fn.empty?
-        { :scheme => "maildir", :path => fn }
+
+        $last_fn = fn
+        [Redwood::Maildir.suggest_labels_for(fn),
+         { :scheme => "maildir", :path => fn }]
       when :mboxssh
+        $last_server ||= "localhost"
         srv = axe "What machine is the mbox file located on?", $last_server 
         return if srv.nil? || srv.empty?
         $last_server = srv
-        fn = axe "What's the full path to the mbox file?", ENV["MAIL"] #"srm
+
+        fn = axe "What's the full path to the mbox file?", $last_fn #" stupid ruby-mode
         return if fn.nil? || fn.empty?
+        $last_fn = fn
         fn = "/#{fn}" # lame
-        { :scheme => "mbox+ssh", :host => srv, :path => fn }
+        [Redwood::MBox::SSHLoader.suggest_labels_for(fn),
+         { :scheme => "mbox+ssh", :host => srv, :path => fn }]
       when :imap, :imaps
+        $last_server ||= "localhost"
         srv = axe "What is the IMAP server (host, or host:port notation)?", $last_server
         return if srv.nil? || srv.empty?
         $last_server = srv
-        fn = axe "What's the folder path?", "INBOX" #"srm 
+
+        $last_folder ||= "INBOX"
+        fn = axe "What's the folder path?", $last_folder #"srm 
         return if fn.nil? || fn.empty?
-        fn = "/#{fn}" # lame
+        $last_folder = fn
 
+        fn = "/#{fn}" # lame
         if srv =~ /^(\w+):(\d+)$/
           host, port = $1, $2.to_i
         else
           host, port = srv, nil
         end
-        { :scheme => type.to_s, :host => host, :port => port, :path => fn }
+        [Redwood::IMAP.suggest_labels_for(fn),
+         { :scheme => type.to_s, :host => host, :port => port, :path => fn }]
       end
     
     uri = 
@@ -96,17 +113,27 @@ def add_source
         if axe_yes("Try again?") then next else return end
       end
 
-    say "I'm going to add this source: #{uri}."
+    say "I'm going to add this source: #{uri}"
     unless axe("Does that look right?", "y") =~ /^y|yes$/i
       if axe_yes("Try again?") then next else return end
     end
 
     usual = axe_yes "Does this source ever receive new messages?", "y"
-    archive = usual ? axe_yes("Should those new messages be automatically archived?") : false
+    archive = usual ? axe_yes("Should new messages be automatically archived? (I.e. not appear in your inbox, though still be accessible via search.)") : false
+
+    labels_str = axe("Enter any labels to be automatically added to all messages from this source, separated by spaces (or 'none')", default_labels.join(","))
+
+    labels =
+      if labels_str =~ /^\s*none\s*$/i
+        nil
+      else
+        labels_str.split(/\s+/)
+      end
     
     cmd = build_cmd "sup-add"
     cmd += " --unusual" unless usual
     cmd += " --archive" if archive
+    cmd += " --labels=#{labels.join(',')}" if labels
     cmd += " #{uri}"
 
     puts "Ok, trying to run \"#{cmd}\"..."
@@ -134,8 +161,7 @@ program. Get ready to be the envy of everyone in your internets
 with your amazing keyboarding skills! Jump from email to email with
 nary a click of the mouse!
 
-Just answer these simple questions and you'll be on your way! Press
-enter at any point to accept the default answer.
+Just answer these simple questions and you'll be on your way.
 
 EOS
 #' stupid ruby-mode
@@ -143,7 +169,7 @@ EOS
 account = $config[:accounts][:default]
 
 name = axe "What's your name?", account[:name]
-email = axe "What's your email address?", account[:email] #'srm
+email = axe "What's your (primary) email address?", account[:email] #'srm
 
 say "Ok, your header will look like this:"
 say "  From: #{name} <#{email}>"
@@ -178,7 +204,7 @@ until done
 
   say "\n"
   choose do |menu|
-    menu.prompt = "Your wish?"
+    menu.prompt = "Your wish? "
     menu.choice("Add a new source.") { add_source }
     menu.choice("Done adding sources!") { done = true }
   end
@@ -190,12 +216,6 @@ Ok. The final step is to import all your messages into the Sup index.
 Depending on how many messages are in the sources, this could take
 quite a while.
 
-IMPORTANT NOTE: this import will archive messages if the source is
-marked archival, and won't otherwise. It will preserve read/unread
-status as given by the source, and it will automatically add one label
-per source. All of this behavior can be controlled on per-source
-basis by running sup-sync manually.
-
 EOS
 #'
 if axe_yes "Run sup-sync to import all messages now?"
@@ -222,7 +242,7 @@ like you're ready to jack in to cyberspace there, cowboy.
 
 Just one last command:
 
-  sup
+  #{build_cmd "sup"}
 
 Have fun!
 EOS
index c7e826b601483923c1975a0f1d0eb97f759bb2c7..6c28a761442947dda2150a284beaf5102f03b1b7 100644 (file)
@@ -176,7 +176,7 @@ begin
         elapsed = last_info_time - start_time
         pctdone = source.respond_to?(:pct_done) ? source.pct_done : 100.0 * (source.cur_offset.to_f - source.start_offset).to_f / (source.end_offset - source.start_offset).to_f
         remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
-        $stderr.puts "## #{num_added + num_updated} (#{pctdone}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining (for this source)"
+        $stderr.puts "## #{num_added + num_updated} (#{pctdone}%) read; #{elapsed.to_time_s} elapsed; #{remaining.to_time_s} remaining"
       end
 
       if index_state.nil?
index 72ff6757379c816e5028135eae0a4be16e9fb93c..1ca6d43122ff97796ab5133b7f1e4630c2068143 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,18 +1,20 @@
 for 0.0.9
 ---------
-_ add arbitrary labels to sources
+_ bugfix: when one new message comes into an imap folder, we don't
+   catch it until a reload (sometimes?)
+  message indicating they're loaded to inbox (imap only?)
+_ rss feed reading
 _ detect other sup instances and do something intelligent (because ferret crashes violently with more than one index writer open)
 _ bugfix: need a way to force an address to a particular name, for things like evite addresses
 _ bugfix: read before thread-index has finished loading then hides the thread?!? wtf. (on jamie)
 _ bugfix: ferret flakiness: just added message but can't find it.
-_ bugfix: when one new message comes into an imap folder, we don't
-   catch it until a reload (sometimes?)
 _ bugfix: add new message counts until keypress
-_ bugfix: deadlock
-_ split out threading & message chunk parsing to a separate library
+_ bugfix: deadlock (on rubyforge)
 _ decode RFC 2047 ("encoded word") headers
   - see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
 _ refactor all the *-search-results-mode classes into one.
+x add arbitrary labels to sources
+x improve sup-config
 x autoload more threads when you go down
 x add a sync-back tool that at least works for mboxes
 x thread by subject configurable in config.yaml
@@ -55,6 +57,10 @@ toggle wrapping
 maybe: de-archived messages auto-added to inbox
 prune old entries from contacts.txt so that it doesn't arbitrarily
 
+maybe
+_ split out threading & message chunk parsing to a separate library
+
+
 done
 ----
 x nice little startup config program
index 45f43c0d369f5b8bf018581a2474796b609f3a48..5311a4404126f8b85285a590ef761e3ae1db2eb9 100644 (file)
@@ -153,11 +153,21 @@ end
 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
+  email = ENV["USER"] + "@" + 
+    begin
+      Socket.gethostbyname(Socket.gethostname).first
+    rescue SocketError
+      Socket.gethostname
+    end
+
   $config = {
     :accounts => {
       :default => {
-        :name => "Sup Rocks",
-        :email => "sup-rocks@reading-my-emails",
+        :name => name,
+        :email => email,
         :alternates => [],
         :sendmail => "/usr/sbin/sendmail -oem -ti",
         :signature => File.join(ENV["HOME"], ".signature")
index 7370b91a3003b7fcb98f76ea237aee2479b522c3..c1ba2bf6c4b366d1d60c1d79bcf22d6a5d81163d 100644 (file)
@@ -50,9 +50,9 @@ class IMAP < Source
 
   attr_accessor :username, :password
   yaml_properties :uri, :username, :password, :cur_offset, :usual,
-                  :archived, :id
+                  :archived, :id, :labels
 
-  def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil
+  def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil, labels=[]
     raise ArgumentError, "username and password must be specified" unless username && password
     raise ArgumentError, "not an imap uri" unless uri =~ %r!imaps?://!
 
@@ -65,11 +65,19 @@ class IMAP < Source
     @imap_ids = {}
     @ids = []
     @last_scan = nil
-    @labels = [:unread]
-    @labels << mailbox.intern unless mailbox =~ /inbox/i
+    @labels = (labels || []).freeze
+    @say_id = nil
     @mutex = Mutex.new
   end
 
+  def self.suggest_labels_for path
+    if path =~ /inbox/i
+      [path.intern]
+    else
+      []
+    end
+  end
+
   def host; @parsed_uri.host; end
   def port; @parsed_uri.port || (ssl? ? 993 : 143); end
   def mailbox
@@ -102,6 +110,7 @@ class IMAP < Source
   def raw_header id
     unsynchronized_scan_mailbox
     header, flags = get_imap_fields id, 'RFC822.HEADER', 'FLAGS'
+    ## very bad. this is very very bad. very bad bad bad.
     header = header + "Status: RO\n" if flags.include? :Seen # fake an mbox-style read header # TODO: improve source-marked-as-read reporting system
     header.gsub(/\r\n/, "\n")
   end
@@ -151,7 +160,7 @@ class IMAP < Source
     start.upto(ids.length - 1) do |i|         
       id = ids[i]
       self.cur_offset = id
-      yield id, @labels.clone
+      yield id, @labels
     end
   end
 
index c17e8f3191527253571f125df1e02b6a9f8d480f..c439fca4194a05164a7dfba229836a0d778c2a47 100644 (file)
@@ -11,21 +11,24 @@ module Redwood
 class Maildir < Source
   SCAN_INTERVAL = 30 # seconds
 
-  yaml_properties :uri, :cur_offset, :usual, :archived, :id
-  def initialize uri, last_date=nil, usual=true, archived=false, id=nil
-    super
+  yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels
+  def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[]
+    super uri, last_date, usual, archived, id
     uri = URI(uri)
 
     raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir"
     raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host
 
     @dir = uri.path
+    @labels = (labels || []).freeze
     @ids = []
     @ids_to_fns = {}
     @last_scan = nil
     @mutex = Mutex.new
   end
 
+  def self.suggest_labels_for path; [] end
+
   def check
     scan_mailbox
     start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
@@ -90,7 +93,7 @@ class Maildir < Source
     start.upto(@ids.length - 1) do |i|         
       id = @ids[i]
       self.cur_offset = id
-      yield id, (@ids_to_fns[id] =~ /,.*R.*$/ ? [] : [:unread])
+      yield id, @labels + (@ids_to_fns[id] =~ /,.*R.*$/ ? [] : [:unread])
     end
   end
 
index 05ddbab09f6ae90153274add4c0b9083a2c78781..49e97036ef8e328c8b66ae86cd55c47068e6ed97 100644 (file)
@@ -5,27 +5,34 @@ module Redwood
 module MBox
 
 class Loader < Source
-  yaml_properties :uri, :cur_offset, :usual, :archived, :id
-  def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil
-    super
+  yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels
+  def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil, labels=[]
+    super uri_or_fp, start_offset, usual, archived, id
 
     @mutex = Mutex.new
-    @labels = [:unread]
+    @labels = (labels || []).freeze
 
     case uri_or_fp
     when String
       uri = URI(uri_or_fp)
       raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
       raise ArgumentError, "mbox uri ('#{uri}') cannot have a host: #{uri.host}" if uri.host
-      ## heuristic: use the filename as a label, unless the file
-      ## has a path that probably represents an inbox.
-      @labels << File.basename(uri.path).intern unless File.dirname(uri.path) =~ /\b(var|usr|spool)\b/
       @f = File.open uri.path
     else
       @f = uri_or_fp
     end
   end
 
+  def self.suggest_labels_for path
+    ## heuristic: use the filename as a label, unless the file
+    ## has a path that probably represents an inbox.
+    if File.dirname(path) =~ /\b(var|usr|spool)\b/
+      []
+    else
+      [File.basename(path).intern]
+    end
+  end
+
   def check
     if (cur_offset ||= start_offset) > end_offset
       raise OutOfSyncSourceError, "mbox file is smaller than last recorded message offset. Messages have probably been deleted by another client."
@@ -128,7 +135,7 @@ class Loader < Source
     end
 
     self.cur_offset = next_offset
-    [returned_offset, @labels.clone]
+    [returned_offset, @labels]
   end
 end
 
index 42a2b98cf3c62a24cddf83f12ab7f99769950033..bea585e87e08adf6263fe8e0261311b28076c0e8 100644 (file)
@@ -7,9 +7,9 @@ class SSHLoader < Source
   attr_accessor :username, :password
 
   yaml_properties :uri, :username, :password, :cur_offset, :usual, 
-                  :archived, :id
+                  :archived, :id, :labels
 
-  def initialize uri, username=nil, password=nil, start_offset=nil, usual=true, archived=false, id=nil
+  def initialize uri, username=nil, password=nil, start_offset=nil, usual=true, archived=false, id=nil, labels=[]
     raise ArgumentError, "not an mbox+ssh uri: #{uri.inspect}" unless uri =~ %r!^mbox\+ssh://!
 
     super uri, start_offset, usual, archived, id
@@ -19,6 +19,7 @@ class SSHLoader < Source
     @password = password
     @uri = uri
     @cur_offset = start_offset
+    @labels = (labels || []).freeze
 
     opts = {}
     opts[:username] = @username if @username
@@ -29,10 +30,10 @@ class SSHLoader < Source
     
     ## heuristic: use the filename as a label, unless the file
     ## has a path that probably represents an inbox.
-    @labels = [:unread]
-    @labels << File.basename(filename).intern unless File.dirname(filename) =~ /\b(var|usr|spool)\b/
   end
 
+  def self.suggest_labels_for path; Loader.suggest_labels_for(path) end
+
   def connect; safely { @f.connect }; end
   def host; @parsed_uri.host; end
   def filename; @parsed_uri.path[1..-1] end
index 690caa295c45e98deebd9710853529d12ef29e3a..37e8c2e2a7982a1e95d35b4d8fbf608ded5e5c55 100644 (file)
@@ -106,6 +106,9 @@ class PollManager
           if m.source_marked_read?
             m.remove_label :unread
             labels.delete :unread
+          else
+            m.add_label :unread
+            labels << :unread
           end
 
           docid, entry = Index.load_entry_for_id m.id
index 00f2d072261a2f810b20a50aab0bf2656bf52a7c..bcdc6e4801a41b4979e8e7422d8dae096f7800ab 100644 (file)
@@ -61,6 +61,8 @@ class Source
   attr_accessor :id
 
   def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
+    raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id
+
     @uri = uri
     @cur_offset = initial_offset
     @usual = usual