sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 8440740a7b84ee3cf1d2bdbaf9e1eb10c6be6b5c
parent aaa384af6ec7054f5e16086b1e54b8f3740723a6
Author: Rich Lane <rlane@club.cc.cmu.edu>
Date:   Sun, 28 Feb 2010 19:04:35 -0800

implement sup-cmd using existing Index apis

Diffstat:
A bin/sup-cmd | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M lib/sup.rb | 1 +
A lib/sup/connection.rb | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/bin/sup-cmd b/bin/sup-cmd
@@ -0,0 +1,110 @@
+#!/usr/bin/env ruby
+require 'rubygems'
+require 'trollop'
+require 'sup'
+require 'pp'
+require 'yaml'
+include Redwood
+
+SUB_COMMANDS = %w(query count label add)
+global_opts = Trollop::options do
+  #version = "sup-cmd (sup #{Redwood::VERSION})"
+  banner <<EOS
+Interact with a Sup index.
+
+Usage:
+  sup-cmd [global options] command [options]
+
+  Valid commands: #{SUB_COMMANDS * ', '}
+
+  Global options:
+EOS
+
+  opt :verbose
+
+  stop_on SUB_COMMANDS
+end
+
+cmd = ARGV.shift
+cmd_opts = case cmd
+when "query"
+  Trollop.options do
+    opt :offset, "Offset", :default => 0, :type => :int
+    opt :limit, "Limit", :type => :int
+    opt :raw, "Retrieve raw message text", :default => false
+  end
+when "count"
+  Trollop.options do
+  end
+when "label"
+  Trollop.options do
+    opt :add_labels, "Labels to add", :default => ""
+    opt :remove_labels, "Labels to remove", :default => ""
+  end
+when "add"
+  Trollop.options do
+    opt :labels, "Labels separated by commas", :default => ""
+    opt :mbox, "Treat input files as mboxes", :default => false
+  end
+else
+  Trollop::die "unrecognized command #{cmd.inspect}"
+end
+
+def get_query
+  text = ARGV.first or fail "query argument required"
+  Redwood::Index.parse_query text
+end
+
+Redwood.start
+Index.init
+Index.lock_interactively or exit
+begin
+  if(s = Redwood::SourceManager.source_for SentManager.source_uri)
+    SentManager.source = s
+  else
+    Redwood::SourceManager.add_source SentManager.default_source
+  end
+
+  Index.load
+  c = Redwood::Connection.new
+
+case cmd
+when "query"
+  c.query get_query, cmd_opts[:offset], cmd_opts[:limit], cmd_opts[:raw] do |result|
+    puts YAML.dump(result['summary'])
+    puts YAML.dump(result['raw']) if cmd_opts[:raw]
+  end
+when "count"
+  puts c.count(get_query)
+when "label"
+  c.label get_query, cmd_opts[:remove_labels].split(','), cmd_opts[:add_labels].split(',')
+when "add"
+  ARGF.binmode
+  labels = cmd_opts[:labels].split(',')
+  get_message = lambda do
+    return ARGF.gets(nil) unless cmd_opts[:mbox]
+    str = ""
+    l = ARGF.gets
+    str << l until ARGF.closed? || ARGF.eof? || MBox::is_break_line?(l = ARGF.gets)
+    str.empty? ? nil : str
+  end
+  i_s = i = 0
+  t = Time.now
+  while raw = get_message[]
+    i += 1
+    t_d = Time.now - t
+    if t_d >= 5
+      i_d = i - i_s
+      puts "indexed #{i} messages (#{i_d/t_d} m/s)" if global_opts[:verbose]
+      t = Time.now
+      i_s = i
+    end
+    c.add raw, labels
+  end
+else
+  fail "#{cmd} command unimplemented"
+end
+
+ensure
+  Index.unlock
+end
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -348,6 +348,7 @@ require "sup/modes/console-mode"
 require "sup/sent"
 require "sup/search"
 require "sup/modes/search-list-mode"
+require "sup/connection"
 
 $:.each do |base|
   d = File.join base, "sup/share/modes/"
diff --git a/lib/sup/connection.rb b/lib/sup/connection.rb
@@ -0,0 +1,63 @@
+module Redwood
+
+## Hacky implementation of the sup-server API using existing Sup code
+class Connection
+  def result_from_message m, raw
+    mkperson = lambda { |p| { :email => p.email, :name => p.name } }
+    {
+      'summary' => {
+        'message_id' => m.id,
+        'date' => m.date,
+        'from' => mkperson[m.from],
+        'to' => m.to.map(&mkperson),
+        'cc' => m.cc.map(&mkperson),
+        'bcc' => m.bcc.map(&mkperson),
+        'subject' => m.subj,
+        'refs' => m.refs,
+        'replytos' => m.replytos,
+        'labels' => m.labels.map(&:to_s),
+      },
+      'raw' => raw ? m.raw_message : nil,
+    }
+  end
+
+  def query query, offset, limit, raw
+    c = 0
+    Index.each_message query do |m|
+      next if c < offset
+      break if c >= offset + limit if limit
+      yield result_from_message(m, raw)
+      c += 1
+    end
+    nil
+  end
+
+  def count query
+    Index.num_results_for query
+  end
+
+  def label query, remove_labels, add_labels
+    Index.each_message query do |m|
+      remove_labels.each { |l| m.remove_label l }
+      add_labels.each { |l| m.add_label l }
+      Index.update_message_state m
+    end
+    nil
+  end
+
+  def add raw, labels
+    SentManager.source.store_message Time.now, "test@example.com" do |io|
+      io.write raw
+    end
+    m2 = nil
+    PollManager.each_message_from(SentManager.source) do |m|
+      PollManager.add_new_message m
+      m2 = m
+    end
+    m2.labels = Set.new(labels.map(&:to_sym))
+    Index.update_message_state m2
+    nil
+  end
+end
+
+end