sup 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sup might be problematic. Click here for more details.

Files changed (56) hide show
  1. data.tar.gz.sig +1 -0
  2. data/CONTRIBUTORS +25 -12
  3. data/History.txt +6 -1
  4. data/README.md +70 -0
  5. data/ReleaseNotes +5 -0
  6. data/bin/sup +22 -15
  7. data/bin/sup-add +3 -3
  8. data/bin/sup-config +3 -4
  9. data/bin/sup-import-dump +1 -1
  10. data/bin/sup-sync +1 -1
  11. data/bin/sup-sync-back +2 -2
  12. data/bin/sup-tweak-labels +1 -1
  13. data/lib/sup.rb +39 -23
  14. data/lib/sup/account.rb +4 -0
  15. data/lib/sup/buffer.rb +4 -7
  16. data/lib/sup/colormap.rb +10 -2
  17. data/lib/sup/contact.rb +11 -5
  18. data/lib/sup/crypto.rb +278 -101
  19. data/lib/sup/draft.rb +3 -2
  20. data/lib/sup/horizontal-selector.rb +5 -2
  21. data/lib/sup/index.rb +47 -42
  22. data/lib/sup/label.rb +1 -1
  23. data/lib/sup/message-chunks.rb +4 -2
  24. data/lib/sup/message.rb +14 -3
  25. data/lib/sup/modes/buffer-list-mode.rb +1 -1
  26. data/lib/sup/modes/compose-mode.rb +1 -1
  27. data/lib/sup/modes/contact-list-mode.rb +2 -2
  28. data/lib/sup/modes/edit-message-async-mode.rb +109 -0
  29. data/lib/sup/modes/edit-message-mode.rb +148 -16
  30. data/lib/sup/modes/file-browser-mode.rb +2 -2
  31. data/lib/sup/modes/forward-mode.rb +4 -4
  32. data/lib/sup/modes/line-cursor-mode.rb +2 -2
  33. data/lib/sup/modes/reply-mode.rb +34 -30
  34. data/lib/sup/modes/resume-mode.rb +4 -1
  35. data/lib/sup/modes/scroll-mode.rb +8 -6
  36. data/lib/sup/modes/text-mode.rb +1 -1
  37. data/lib/sup/modes/thread-index-mode.rb +44 -25
  38. data/lib/sup/modes/thread-view-mode.rb +26 -24
  39. data/lib/sup/person.rb +18 -7
  40. data/lib/sup/poll.rb +1 -1
  41. data/lib/sup/rfc2047.rb +1 -1
  42. data/lib/sup/sent.rb +2 -2
  43. data/lib/sup/source.rb +1 -1
  44. data/lib/sup/textfield.rb +38 -1
  45. data/lib/sup/thread.rb +1 -1
  46. data/lib/sup/time.rb +83 -0
  47. data/lib/sup/util.rb +38 -74
  48. data/lib/sup/version.rb +3 -0
  49. metadata +333 -168
  50. metadata.gz.sig +0 -0
  51. data/README.txt +0 -128
  52. data/bin/sup-cmd +0 -138
  53. data/bin/sup-server +0 -44
  54. data/lib/sup/client.rb +0 -92
  55. data/lib/sup/protocol.rb +0 -161
  56. data/lib/sup/server.rb +0 -116
metadata.gz.sig ADDED
Binary file
data/README.txt DELETED
@@ -1,128 +0,0 @@
1
- sup
2
- by William Morgan <wmorgan-sup@masanjin.net>
3
- http://sup.rubyforge.org
4
-
5
- == DESCRIPTION:
6
-
7
- Sup is a console-based email client for people with a lot of email.
8
- It supports tagging, very fast full-text search, automatic contact-
9
- list management, and more. If you're the type of person who treats
10
- email as an extension of your long-term memory, Sup is for you.
11
-
12
- Sup makes it easy to:
13
- - Handle massive amounts of email.
14
-
15
- - Mix email from different sources: mbox files and Maildirs.
16
-
17
- - Instantaneously search over your entire email collection. Search over
18
- body text, or use a query language to combine search predicates in any
19
- way.
20
-
21
- - Handle multiple accounts. Replying to email sent to a particular
22
- account will use the correct SMTP server, signature, and from address.
23
-
24
- - Add custom code to customize Sup to whatever particular and bizarre
25
- needs you may have.
26
-
27
- - Organize email with user-defined labels, automatically track recent
28
- contacts, and much more!
29
-
30
- The goal of Sup is to become the email client of choice for nerds
31
- everywhere.
32
-
33
- == FEATURES/PROBLEMS:
34
-
35
- Features:
36
-
37
- - Scalability to massive amounts of email. Immediate startup and
38
- operability, regardless of how much amount of email you have.
39
-
40
- - Immediate full-text search of your entire email archive, using the
41
- Xapian query language. Search over message bodies, labels, from: and
42
- to: fields, or any combination thereof.
43
-
44
- - Thread-centrism. Operations are performed at the thread, not the
45
- message level. Entire threads are manipulated and viewed (with
46
- redundancies removed) at a time.
47
-
48
- - Labels instead of folders. Drop that tired old metaphor and you'll see
49
- how much easier it is to organize email.
50
-
51
- - GMail-style thread management. Archive a thread, and it will disappear
52
- from your inbox until someone replies. Kill a thread, and it will
53
- never come back to your inbox (but will still show up in searches.)
54
- Mark a thread as spam and you'll never again see it unless explicitly
55
- searching for spam.
56
-
57
- - Console based interface. No mouse clicking required!
58
-
59
- - Programmability. It's in Ruby. The code is good. It has an extensive
60
- hook system that makes it easy to extend and customize.
61
-
62
- - Multiple buffer support. Why be limited to viewing one thing at a
63
- time?
64
-
65
- - Tons of other little features, like automatic context-sensitive help,
66
- multi-message operations, MIME attachment viewing, recent contact list
67
- generation, etc.
68
-
69
- Current limitations which will be fixed:
70
-
71
- - Sup doesn't play nicely with other mail clients. If you alter a mail
72
- source (read, move, delete, etc) with another client Sup will punish
73
- you with a lengthy reindexing process.
74
-
75
- - Unix-centrism in MIME attachment handling and in sendmail invocation.
76
-
77
- == SYNOPSYS:
78
-
79
- 0. sup-config
80
- 1. sup
81
-
82
- Note that Sup never changes the contents of any mailboxes; it only
83
- indexes in to them. So it shouldn't ever corrupt your mail. The flip
84
- side is that if you change a mailbox (e.g. delete messages, or, in the
85
- case of mbox files, read an unread message) then Sup will be unable to
86
- load messages from that source and will ask you to run sup-sync
87
- --changed.
88
-
89
- == REQUIREMENTS:
90
-
91
- - xapian-full >= 1.1.3.2
92
- - ncurses >= 0.9.1
93
- - rmail >= 0.17
94
- - highline
95
- - net-ssh
96
- - trollop >= 1.12
97
- - lockfile
98
- - mime-types
99
- - gettext
100
- - fastthread
101
-
102
- == INSTALL:
103
-
104
- * gem install sup
105
-
106
- == PROBLEMS:
107
-
108
- See FAQ.txt for some common problems and their solutions.
109
-
110
- == LICENSE:
111
-
112
- Copyright (c) 2006--2009 William Morgan.
113
-
114
- This program is free software; you can redistribute it and/or
115
- modify it under the terms of the GNU General Public License
116
- as published by the Free Software Foundation; either version 2
117
- of the License, or (at your option) any later version.
118
-
119
- This program is distributed in the hope that it will be useful,
120
- but WITHOUT ANY WARRANTY; without even the implied warranty of
121
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122
- GNU General Public License for more details.
123
-
124
- You should have received a copy of the GNU General Public License
125
- along with this program; if not, write to the Free Software
126
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
127
- 02110-1301, USA.
128
-
data/bin/sup-cmd DELETED
@@ -1,138 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'trollop'
4
- require 'sup'
5
- require 'sup/client'
6
- require 'pp'
7
- require 'yaml'
8
- include Redwood
9
-
10
- SUB_COMMANDS = %w(query count label add)
11
- global_opts = Trollop::options do
12
- #version = "sup-cmd (sup #{Redwood::VERSION})"
13
- banner <<EOS
14
- Connect to a running sup-server.
15
-
16
- Usage:
17
- sup-cmd [global options] command [options]
18
-
19
- Valid commands: #{SUB_COMMANDS * ', '}
20
-
21
- Global options:
22
- EOS
23
-
24
- opt :host, "server address", :type => :string, :default => 'localhost', :short => 'o'
25
- opt :port, "server port", :type => :int, :default => 4300
26
- opt :socket, "unix domain socket path", :type => :string, :default => nil
27
- opt :verbose
28
-
29
- conflicts :host, :socket
30
- conflicts :port, :socket
31
-
32
- stop_on SUB_COMMANDS
33
- end
34
-
35
- cmd = ARGV.shift
36
- cmd_opts = case cmd
37
- when "query"
38
- Trollop.options do
39
- opt :offset, "Offset", :default => 0, :type => :int
40
- opt :limit, "Limit", :type => :int
41
- opt :raw, "Retrieve raw message text", :default => false
42
- end
43
- when "count"
44
- Trollop.options do
45
- end
46
- when "label"
47
- Trollop.options do
48
- opt :add_labels, "Labels to add", :default => ""
49
- opt :remove_labels, "Labels to remove", :default => ""
50
- end
51
- when "add"
52
- Trollop.options do
53
- opt :labels, "Labels separated by commas", :default => ""
54
- opt :mbox, "Treat input files as mboxes", :default => false
55
- end
56
- else
57
- Trollop::die "unrecognized command #{cmd.inspect}"
58
- end
59
-
60
- class SupCmd < Redwood::Client
61
- def initialize cmd, args, opts
62
- @cmd = cmd
63
- @opts = opts
64
- @args = args
65
- super()
66
- end
67
-
68
- def get_query
69
- @args.first or fail "query argument required"
70
- end
71
-
72
- def connection_established
73
- case @cmd
74
- when "query"
75
- query get_query, @opts[:offset], @opts[:limit], @opts[:raw] do |result|
76
- if result
77
- puts YAML.dump(result['summary'])
78
- puts YAML.dump(result['raw']) if @opts[:raw]
79
- else
80
- close_connection
81
- end
82
- end
83
- when "count"
84
- count(get_query) do |x|
85
- puts x
86
- close_connection
87
- end
88
- when "label"
89
- label get_query, @opts[:remove_labels].split(','), @opts[:add_labels].split(',') do
90
- close_connection
91
- end
92
- when "add"
93
- ARGF.binmode
94
- labels = @opts[:labels].split(',')
95
- get_message = lambda do
96
- return ARGF.gets(nil) unless @opts[:mbox]
97
- str = ""
98
- l = ARGF.gets
99
- str << l until ARGF.closed? || ARGF.eof? || MBox::is_break_line?(l = ARGF.gets)
100
- str.empty? ? nil : str
101
- end
102
- i_s = i = 0
103
- t = Time.now
104
- while raw = get_message[]
105
- i += 1
106
- t_d = Time.now - t
107
- if t_d >= 5
108
- i_d = i - i_s
109
- puts "indexed #{i} messages (#{i_d/t_d} m/s)" if global_opts[:verbose]
110
- t = Time.now
111
- i_s = i
112
- end
113
- add raw, labels do
114
- close_connection
115
- end
116
- end
117
- else
118
- fail "#{@cmd} command unimplemented"
119
- close_connection
120
- end
121
- end
122
-
123
- def unbind
124
- EM.stop
125
- end
126
- end
127
-
128
-
129
- EM.run do
130
- if global_opts[:socket]
131
- EM.connect global_opts[:socket], SupCmd, cmd, ARGV, cmd_opts.merge(global_opts)
132
- else
133
- EM.connect global_opts[:host], global_opts[:port], SupCmd, cmd, ARGV, cmd_opts.merge(global_opts)
134
- end
135
- end
136
-
137
- exit 0
138
-
data/bin/sup-server DELETED
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'trollop'
4
- require 'sup'
5
- require 'sup/server'
6
- require 'pp'
7
- require 'yaml'
8
- include Redwood
9
-
10
- global_opts = Trollop::options do
11
- #version = "sup-cmd (sup #{Redwood::VERSION})"
12
- banner <<EOS
13
- Interact with a Sup index.
14
-
15
- Usage:
16
- sup-server [options]
17
- EOS
18
-
19
- opt :host, "address to listen on", :type => :string, :default => 'localhost', :short => 'o'
20
- opt :port, "port to listen on", :type => :int, :default => 4300
21
- opt :verbose
22
- end
23
-
24
- Redwood.start
25
- Index.init
26
- Index.lock_interactively or exit
27
- begin
28
- if(s = Redwood::SourceManager.source_for SentManager.source_uri)
29
- SentManager.source = s
30
- else
31
- Redwood::SourceManager.add_source SentManager.default_source
32
- end
33
-
34
- Index.load
35
-
36
- EM.run do
37
- EM.start_server global_opts[:host], global_opts[:port],
38
- Redwood::Server, Index.instance
39
- EM.next_tick { puts "ready" }
40
- end
41
-
42
- ensure
43
- Index.unlock
44
- end
data/lib/sup/client.rb DELETED
@@ -1,92 +0,0 @@
1
- require 'sup/protocol'
2
-
3
- module Redwood
4
-
5
- class Client < EM::P::RedwoodClient
6
- def initialize *a
7
- @next_tag = 1
8
- @cbs = {}
9
- super *a
10
- end
11
-
12
- def mktag &b
13
- @next_tag.tap do |x|
14
- @cbs[x] = b
15
- @next_tag += 1
16
- end
17
- end
18
-
19
- def rmtag tag
20
- @cbs.delete tag
21
- end
22
-
23
- def query qstr, offset, limit, raw, &b
24
- tag = mktag do |type,tag,args|
25
- if type == 'message'
26
- b.call args
27
- else
28
- fail unless type == 'done'
29
- b.call nil
30
- rmtag tag
31
- end
32
- end
33
- send_message 'query', tag,
34
- 'query' => qstr,
35
- 'offset' => offset,
36
- 'limit' => limit,
37
- 'raw' => raw
38
- end
39
-
40
- def count qstr, &b
41
- tag = mktag do |type,tag,args|
42
- b.call args['count']
43
- rmtag tag
44
- end
45
- send_message 'count', tag,
46
- 'query' => qstr
47
- end
48
-
49
- def label qstr, add, remove, &b
50
- tag = mktag do |type,tag,args|
51
- b.call
52
- rmtag tag
53
- end
54
- send_message 'label', tag,
55
- 'query' => qstr,
56
- 'add' => add,
57
- 'remove' => remove
58
- end
59
-
60
- def add raw, labels, &b
61
- tag = mktag do |type,tag,args|
62
- b.call
63
- rmtag tag
64
- end
65
- send_message 'add', tag,
66
- 'raw' => raw,
67
- 'labels' => labels
68
- end
69
-
70
- def thread msg_id, raw, &b
71
- tag = mktag do |type,tag,args|
72
- if type == 'message'
73
- b.call args
74
- else
75
- fail unless type == 'done'
76
- b.call nil
77
- rmtag tag
78
- end
79
- end
80
-
81
- send_message 'thread', tag,
82
- 'message_id' => msg_id,
83
- 'raw' => raw
84
- end
85
-
86
- def receive_message type, tag, args
87
- cb = @cbs[tag] or fail "invalid tag #{tag.inspect}"
88
- cb[type, tag, args]
89
- end
90
- end
91
-
92
- end
data/lib/sup/protocol.rb DELETED
@@ -1,161 +0,0 @@
1
- require 'eventmachine'
2
- require 'socket'
3
- require 'stringio'
4
- require 'yajl'
5
-
6
- class EM::P::Redwood < EM::Connection
7
- VERSION = 1
8
- ENCODINGS = %w(marshal json)
9
-
10
- attr_reader :debug
11
-
12
- def initialize *args
13
- @state = :negotiating
14
- @version_buf = ""
15
- @debug = false
16
- super
17
- end
18
-
19
- def receive_data data
20
- if @state == :negotiating
21
- @version_buf << data
22
- if i = @version_buf.index("\n")
23
- l = @version_buf.slice!(0..i)
24
- receive_version *parse_version(l.strip)
25
- x = @version_buf
26
- @version_buf = nil
27
- @state = :established
28
- connection_established
29
- receive_data x
30
- end
31
- else
32
- @filter.decode(data).each do |msg|
33
- puts "#{self.class.name} received: #{msg.inspect}" if @debug
34
- validate_message *msg
35
- receive_message *msg
36
- end
37
- end
38
- end
39
-
40
- def connection_established
41
- end
42
-
43
- def send_version encodings, extensions
44
- fail if encodings.empty?
45
- send_data "Redwood #{VERSION} #{encodings * ','} #{extensions.empty? ? :none : (extensions * ',')}\n"
46
- end
47
-
48
- def send_message type, tag, params={}
49
- fail "attempted to send message during negotiation" unless @state == :established
50
- puts "#{self.class.name} sent: #{[type, tag, params].inspect}" if @debug
51
- validate_message type, tag, params
52
- send_data @filter.encode([type,tag,params])
53
- end
54
-
55
- def receive_version l
56
- fail "unimplemented"
57
- end
58
-
59
- def receive_message type, tag, params
60
- fail "unimplemented"
61
- end
62
-
63
- private
64
-
65
- def validate_message type, tag, params
66
- fail unless type.is_a? String or type.is_a? Symbol
67
- fail unless tag.is_a? String or tag.is_a? Integer
68
- fail unless params.is_a? Hash
69
- end
70
-
71
- def parse_version l
72
- l =~ /^Redwood\s+(\d+)\s+([\w,]+)\s+([\w,]+)$/ or fail "unexpected banner #{l.inspect}"
73
- version, encodings, extensions = $1.to_i, $2, $3
74
- encodings = encodings.split ','
75
- extensions = extensions.split ','
76
- extensions = [] if extensions == ['none']
77
- fail unless version == VERSION
78
- fail if encodings.empty?
79
- [encodings, extensions]
80
- end
81
-
82
- def create_filter encoding
83
- case encoding
84
- when 'json' then JSONFilter.new
85
- when 'marshal' then MarshalFilter.new
86
- else fail "unknown encoding #{encoding.inspect}"
87
- end
88
- end
89
-
90
- class JSONFilter
91
- def initialize
92
- @parser = Yajl::Parser.new :check_utf8 => false
93
- end
94
-
95
- def decode chunk
96
- parsed = []
97
- @parser.on_parse_complete = lambda { |o| parsed << o }
98
- @parser << chunk
99
- parsed
100
- end
101
-
102
- def encode *os
103
- os.inject('') { |s, o| s << Yajl::Encoder.encode(o) }
104
- end
105
- end
106
-
107
- class MarshalFilter
108
- def initialize
109
- @buf = ''
110
- @state = :prefix
111
- @size = 0
112
- end
113
-
114
- def decode chunk
115
- received = []
116
- @buf << chunk
117
-
118
- begin
119
- if @state == :prefix
120
- break unless @buf.size >= 4
121
- prefix = @buf.slice!(0...4)
122
- @size = prefix.unpack('N')[0]
123
- @state = :data
124
- end
125
-
126
- fail unless @state == :data
127
- break if @buf.size < @size
128
- received << Marshal.load(@buf.slice!(0...@size))
129
- @state = :prefix
130
- end until @buf.empty?
131
-
132
- received
133
- end
134
-
135
- def encode o
136
- data = Marshal.dump o
137
- [data.size].pack('N') + data
138
- end
139
- end
140
- end
141
-
142
- class EM::P::RedwoodServer < EM::P::Redwood
143
- def post_init
144
- send_version ENCODINGS, []
145
- end
146
-
147
- def receive_version encodings, extensions
148
- fail unless encodings.size == 1
149
- fail unless ENCODINGS.member? encodings.first
150
- @filter = create_filter encodings.first
151
- end
152
- end
153
-
154
- class EM::P::RedwoodClient < EM::P::Redwood
155
- def receive_version encodings, extensions
156
- encoding = (ENCODINGS & encodings).first
157
- fail unless encoding
158
- @filter = create_filter encoding
159
- send_version [encoding], []
160
- end
161
- end