sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit d2c6a624d8ee9778719c5b113171674a9b50ca30
parent b10ba6d33dc0330c2daf56a2f02e73d35abe3357
Author: Gaute Hope <eg@gaute.vetsj.com>
Date:   Fri,  3 May 2013 14:35:44 +0200

Remove obsolete server and client, is now in heliotrope and turnsole

Diffstat:
D bin/sup-cmd | 138 -------------------------------------------------------------------------------
D bin/sup-server | 44 --------------------------------------------
D lib/sup/client.rb | 92 -------------------------------------------------------------------------------
D lib/sup/protocol.rb | 161 -------------------------------------------------------------------------------
D lib/sup/server.rb | 116 -------------------------------------------------------------------------------
D test/test_server.rb | 106 -------------------------------------------------------------------------------
6 files changed, 0 insertions(+), 657 deletions(-)
diff --git a/bin/sup-cmd b/bin/sup-cmd
@@ -1,138 +0,0 @@
-#!/usr/bin/env ruby
-require 'rubygems'
-require 'trollop'
-require 'sup'
-require 'sup/client'
-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
-Connect to a running sup-server.
-
-Usage:
-  sup-cmd [global options] command [options]
-
-  Valid commands: #{SUB_COMMANDS * ', '}
-
-  Global options:
-EOS
-
-  opt :host, "server address", :type => :string, :default => 'localhost', :short => 'o'
-  opt :port, "server port", :type => :int, :default => 4300
-  opt :socket, "unix domain socket path", :type => :string, :default => nil
-  opt :verbose
-
-  conflicts :host, :socket
-  conflicts :port, :socket
-
-  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
-
-class SupCmd < Redwood::Client
-  def initialize cmd, args, opts
-    @cmd = cmd
-    @opts = opts
-    @args = args
-    super()
-  end
-
-  def get_query
-    @args.first or fail "query argument required"
-  end
-
-  def connection_established
-    case @cmd
-    when "query"
-      query get_query, @opts[:offset], @opts[:limit], @opts[:raw] do |result|
-        if result
-          puts YAML.dump(result['summary'])
-          puts YAML.dump(result['raw']) if @opts[:raw]
-        else
-          close_connection
-        end
-      end
-    when "count"
-      count(get_query) do |x|
-        puts x
-        close_connection
-      end
-    when "label"
-      label get_query, @opts[:remove_labels].split(','), @opts[:add_labels].split(',') do
-        close_connection
-      end
-    when "add"
-      ARGF.binmode
-      labels = @opts[:labels].split(',')
-      get_message = lambda do
-        return ARGF.gets(nil) unless @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
-        add raw, labels do
-          close_connection
-        end
-      end
-    else
-      fail "#{@cmd} command unimplemented"
-      close_connection
-    end
-  end
-
-  def unbind
-    EM.stop
-  end
-end
-
-
-EM.run do
-  if global_opts[:socket]
-    EM.connect global_opts[:socket], SupCmd, cmd, ARGV, cmd_opts.merge(global_opts)
-  else
-    EM.connect global_opts[:host], global_opts[:port], SupCmd, cmd, ARGV, cmd_opts.merge(global_opts)
-  end
-end
-
-exit 0
-
diff --git a/bin/sup-server b/bin/sup-server
@@ -1,44 +0,0 @@
-#!/usr/bin/env ruby
-require 'rubygems'
-require 'trollop'
-require 'sup'
-require 'sup/server'
-require 'pp'
-require 'yaml'
-include Redwood
-
-global_opts = Trollop::options do
-  #version = "sup-cmd (sup #{Redwood::VERSION})"
-  banner <<EOS
-Interact with a Sup index.
-
-Usage:
-  sup-server [options]
-EOS
-
-  opt :host, "address to listen on", :type => :string, :default => 'localhost', :short => 'o'
-  opt :port, "port to listen on", :type => :int, :default => 4300
-  opt :verbose
-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
-
-  EM.run do
-    EM.start_server global_opts[:host], global_opts[:port],
-                    Redwood::Server, Index.instance
-    EM.next_tick { puts "ready" }
-  end
-
-ensure
-  Index.unlock
-end
diff --git a/lib/sup/client.rb b/lib/sup/client.rb
@@ -1,92 +0,0 @@
-require 'sup/protocol'
-
-module Redwood
-
-class Client < EM::P::RedwoodClient
-  def initialize *a
-    @next_tag = 1
-    @cbs = {}
-    super *a
-  end
-
-  def mktag &b
-    @next_tag.tap do |x|
-      @cbs[x] = b
-      @next_tag += 1
-    end
-  end
-
-  def rmtag tag
-    @cbs.delete tag
-  end
-
-  def query qstr, offset, limit, raw, &b
-    tag = mktag do |type,tag,args|
-      if type == 'message'
-        b.call args
-      else
-        fail unless type == 'done'
-        b.call nil
-        rmtag tag
-      end
-    end
-    send_message 'query', tag,
-                 'query' => qstr,
-                 'offset' => offset,
-                 'limit' => limit,
-                 'raw' => raw
-  end
-
-  def count qstr, &b
-    tag = mktag do |type,tag,args|
-      b.call args['count']
-      rmtag tag
-    end
-    send_message 'count', tag,
-                 'query' => qstr
-  end
-
-  def label qstr, add, remove, &b
-    tag = mktag do |type,tag,args|
-      b.call
-      rmtag tag
-    end
-    send_message 'label', tag,
-                 'query' => qstr,
-                 'add' => add,
-                 'remove' => remove
-  end
-
-  def add raw, labels, &b
-    tag = mktag do |type,tag,args|
-      b.call
-      rmtag tag
-    end
-    send_message 'add', tag,
-                 'raw' => raw,
-                 'labels' => labels
-  end
-
-  def thread msg_id, raw, &b
-    tag = mktag do |type,tag,args|
-      if type == 'message'
-        b.call args
-      else
-        fail unless type == 'done'
-        b.call nil
-        rmtag tag
-      end
-    end
-
-    send_message 'thread', tag,
-                 'message_id' => msg_id,
-                 'raw' => raw
-  end
-
-  def receive_message type, tag, args
-    cb = @cbs[tag] or fail "invalid tag #{tag.inspect}"
-    cb[type, tag, args]
-  end
-end
-
-end
diff --git a/lib/sup/protocol.rb b/lib/sup/protocol.rb
@@ -1,161 +0,0 @@
-require 'eventmachine'
-require 'socket'
-require 'stringio'
-require 'yajl'
-
-class EM::P::Redwood < EM::Connection
-  VERSION = 1
-  ENCODINGS = %w(marshal json)
-
-  attr_reader :debug
-
-  def initialize *args
-    @state = :negotiating
-    @version_buf = ""
-    @debug = false
-    super
-  end
-
-  def receive_data data
-    if @state == :negotiating
-      @version_buf << data
-      if i = @version_buf.index("\n")
-        l = @version_buf.slice!(0..i)
-        receive_version *parse_version(l.strip)
-        x = @version_buf
-        @version_buf = nil
-        @state = :established
-        connection_established
-        receive_data x
-      end
-    else
-      @filter.decode(data).each do |msg|
-        puts "#{self.class.name} received: #{msg.inspect}" if @debug
-        validate_message *msg
-        receive_message *msg
-      end
-    end
-  end
-
-  def connection_established
-  end
-
-  def send_version encodings, extensions
-    fail if encodings.empty?
-    send_data "Redwood #{VERSION} #{encodings * ','} #{extensions.empty? ? :none : (extensions * ',')}\n"
-  end
-
-  def send_message type, tag, params={}
-    fail "attempted to send message during negotiation" unless @state == :established
-    puts "#{self.class.name} sent: #{[type, tag, params].inspect}" if @debug
-    validate_message type, tag, params
-    send_data @filter.encode([type,tag,params])
-  end
-
-  def receive_version l
-    fail "unimplemented"
-  end
-
-  def receive_message type, tag, params
-    fail "unimplemented"
-  end
-
-private
-
-  def validate_message type, tag, params
-    fail unless type.is_a? String or type.is_a? Symbol
-    fail unless tag.is_a? String or tag.is_a? Integer
-    fail unless params.is_a? Hash
-  end
-
-  def parse_version l
-    l =~ /^Redwood\s+(\d+)\s+([\w,]+)\s+([\w,]+)$/ or fail "unexpected banner #{l.inspect}"
-    version, encodings, extensions = $1.to_i, $2, $3
-    encodings = encodings.split ','
-    extensions = extensions.split ','
-    extensions = [] if extensions == ['none']
-    fail unless version == VERSION
-    fail if encodings.empty?
-    [encodings, extensions]
-  end
-
-  def create_filter encoding
-    case encoding
-    when 'json' then JSONFilter.new
-    when 'marshal' then MarshalFilter.new
-    else fail "unknown encoding #{encoding.inspect}"
-    end
-  end
-
-  class JSONFilter
-    def initialize
-      @parser = Yajl::Parser.new :check_utf8 => false
-    end
-
-    def decode chunk
-      parsed = []
-      @parser.on_parse_complete = lambda { |o| parsed << o }
-      @parser << chunk
-      parsed
-    end
-
-    def encode *os
-      os.inject('') { |s, o| s << Yajl::Encoder.encode(o) }
-    end
-  end
-
-  class MarshalFilter
-    def initialize
-      @buf = ''
-      @state = :prefix
-      @size = 0
-    end
-
-    def decode chunk
-      received = []
-      @buf << chunk
-
-      begin
-        if @state == :prefix
-          break unless @buf.size >= 4
-          prefix = @buf.slice!(0...4)
-          @size = prefix.unpack('N')[0]
-          @state = :data
-        end
-
-        fail unless @state == :data
-        break if @buf.size < @size
-        received << Marshal.load(@buf.slice!(0...@size))
-        @state = :prefix
-      end until @buf.empty?
-
-      received
-    end
-
-    def encode o
-      data = Marshal.dump o
-      [data.size].pack('N') + data
-    end
-  end
-end
-
-class EM::P::RedwoodServer < EM::P::Redwood
-  def post_init
-    send_version ENCODINGS, []
-  end
-
-  def receive_version encodings, extensions
-    fail unless encodings.size == 1
-    fail unless ENCODINGS.member? encodings.first
-    @filter = create_filter encodings.first
-  end
-end
-
-class EM::P::RedwoodClient < EM::P::Redwood
-  def receive_version encodings, extensions
-    encoding = (ENCODINGS & encodings).first
-    fail unless encoding
-    @filter = create_filter encoding
-    send_version [encoding], []
-  end
-end
diff --git a/lib/sup/server.rb b/lib/sup/server.rb
@@ -1,116 +0,0 @@
-require 'sup/protocol'
-
-module Redwood
-
-class Server < EM::P::RedwoodServer
-  def initialize index
-    super
-    @index = index
-  end
-
-  def receive_message type, tag, params
-    if respond_to? :"request_#{type}"
-      send :"request_#{type}", tag, params
-    else
-      send_message 'error', tag, 'description' => "invalid request type #{type.inspect}"
-    end
-  end
-
-  def request_query tag, a
-    q = @index.parse_query a['query']
-    query q, a['offset'], a['limit'], a['raw'] do |r|
-      send_message 'message', tag, r
-    end
-    send_message 'done', tag
-  end
-
-  def request_count tag, a
-    q = @index.parse_query a['query']
-    c = count q
-    send_message 'count', tag, 'count' => c
-  end
-
-  def request_label tag, a
-    q = @index.parse_query a['query']
-    label q, a['add'], a['remove']
-    send_message 'done', tag
-  end
-
-  def request_add tag, a
-    add a['raw'], a['labels']
-    send_message 'done', tag
-  end
-
-  def request_thread tag, a
-    thread a['message_id'], a['raw'] do |r|
-      send_message 'message', tag, r
-    end
-    send_message 'done', tag
-  end
-
-private
-
-  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
-    PollManager.poll_from SentManager.source do |sym,m,old_m,progress|
-      next unless sym == :add
-      m.labels = labels
-    end
-    nil
-  end
-
-  def thread msg_id, raw
-    msg = @index.build_message msg_id
-    @index.each_message_in_thread_for msg do |id, builder|
-      m = builder.call
-      yield result_from_message(m, raw)
-    end
-  end
-end
-
-end
diff --git a/test/test_server.rb b/test/test_server.rb
@@ -1,106 +0,0 @@
-#!/usr/bin/ruby
-# encoding: utf-8
-
-require 'test/unit'
-require 'iconv'
-require 'stringio'
-require 'tmpdir'
-require 'fileutils'
-require 'thread'
-require 'eventmachine'
-require 'sup'
-require 'sup/server'
-
-Thread.abort_on_exception = true
-
-module EM
-  # Run the reactor in a new thread. This is useful for using EventMachine
-  # alongside synchronous code. It is recommended to use EM.error_handler to
-  # detect when an exception terminates the reactor thread.
-  def self.spawn_reactor_thread
-    fail "reactor already started" if EM.reactor_running?
-    q = ::Queue.new
-    Thread.new { EM.run { q << nil } }
-    q.pop
-  end
-
-  # Stop the reactor and wait for it to finish. This is the counterpart to #spawn_reactor_thread.
-  def self.kill_reactor_thread
-    fail "reactor is not running" unless EM.reactor_running?
-    fail "current thread is running the reactor" if EM.reactor_thread?
-    EM.stop
-    EM.reactor_thread.join
-  end
-end
-
-class QueueingClient < EM::P::RedwoodClient
-  def initialize
-    super
-    @q = Queue.new
-    @readyq = Queue.new
-  end
-
-  def receive_message type, tag, params
-    @q << [type, tag, params]
-  end
-
-  def connection_established
-    @readyq << nil
-  end
-
-  def wait_until_ready
-    @readyq.pop
-  end
-
-  def read
-    @q.pop
-  end
-
-  alias write send_message
-end
-
-class TestServer < Test::Unit::TestCase
-  def setup
-    port = rand(1000) + 30000
-    EM.spawn_reactor_thread
-    @path = Dir.mktmpdir
-    socket_path = File.join(@path, 'socket')
-    Redwood::HookManager.init File.join(@path, 'hooks')
-    Redwood::SourceManager.init
-    Redwood::SourceManager.load_sources File.join(@path, 'sources.yaml')
-    Redwood::Index.init @path
-    Redwood::SearchManager.init File.join(@path, 'searches')
-    Redwood::Index.load
-    @server = EM.start_server socket_path,
-              Redwood::Server, Redwood::Index.instance
-    @client = EM.connect socket_path, QueueingClient
-    @client.wait_until_ready
-  end
-
-  def teardown
-    FileUtils.rm_r @path if passed?
-    puts "not cleaning up #{@path}" unless passed?
-    %w(Index SearchManager SourceManager HookManager).each do |x|
-      Redwood.const_get(x.to_sym).deinstantiate!
-    end
-    EM.kill_reactor_thread
-  end
-
-  def test_invalid_request
-    @client.write 'foo', '1'
-    check @client.read, 'error', '1'
-  end
-
-  def test_query
-    @client.write 'query', '1', 'query' => 'type:mail'
-    check @client.read, 'done', '1'
-  end
-
-  def check resp, type, tag, args={}
-    assert_equal type.to_s, resp[0]
-    assert_equal tag.to_s, resp[1]
-    args.each do |k,v|
-      assert_equal v, resp[2][k.to_s]
-    end
-  end
-end