sup 0.0.2 → 0.0.3

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.

@@ -1,3 +1,14 @@
1
+ == 0.0.3 / 2007-01-02
2
+
3
+ * Major speed increase for index views (inbox, search results), which
4
+ are now loaded completely from the IR index. The only time the
5
+ original sources need to be touched is when viewing a thread. This
6
+ is important for slow sources like IMAP and mbox+ssh.
7
+ * Remote mbox support with mbox+ssh URIs.
8
+ * IMAP now actually works.
9
+ * sup-import uses HighLine and is generally much improved.
10
+ * Multitudinous minor bug fixes and improvements.
11
+
1
12
  == 0.0.2 / 2006-12-10
2
13
 
3
14
  * IMAP support
@@ -1,54 +1,57 @@
1
1
  History.txt
2
+ LICENSE
2
3
  Manifest.txt
3
4
  README.txt
4
- LICENSE
5
5
  Rakefile
6
+ bin/sup
7
+ bin/sup-import
8
+ bin/sup-recover-sources
6
9
  doc/FAQ.txt
7
10
  doc/Philosophy.txt
8
11
  doc/TODO
9
- bin/sup
10
- bin/sup-import
12
+ lib/sup.rb
13
+ lib/sup/account.rb
14
+ lib/sup/buffer.rb
15
+ lib/sup/colormap.rb
16
+ lib/sup/contact.rb
17
+ lib/sup/draft.rb
18
+ lib/sup/imap.rb
19
+ lib/sup/index.rb
20
+ lib/sup/keymap.rb
21
+ lib/sup/label.rb
22
+ lib/sup/logger.rb
23
+ lib/sup/mbox.rb
11
24
  lib/sup/mbox/loader.rb
12
- lib/sup/modes/line-cursor-mode.rb
13
- lib/sup/modes/reply-mode.rb
14
- lib/sup/modes/scroll-mode.rb
15
- lib/sup/modes/resume-mode.rb
25
+ lib/sup/mbox/ssh-file.rb
26
+ lib/sup/mbox/ssh-loader.rb
27
+ lib/sup/message.rb
28
+ lib/sup/mode.rb
29
+ lib/sup/modes/buffer-list-mode.rb
30
+ lib/sup/modes/compose-mode.rb
16
31
  lib/sup/modes/contact-list-mode.rb
32
+ lib/sup/modes/edit-message-mode.rb
17
33
  lib/sup/modes/forward-mode.rb
34
+ lib/sup/modes/help-mode.rb
35
+ lib/sup/modes/inbox-mode.rb
36
+ lib/sup/modes/label-list-mode.rb
18
37
  lib/sup/modes/label-search-results-mode.rb
19
- lib/sup/modes/search-results-mode.rb
20
- lib/sup/modes/compose-mode.rb
38
+ lib/sup/modes/line-cursor-mode.rb
39
+ lib/sup/modes/log-mode.rb
40
+ lib/sup/modes/person-search-results-mode.rb
21
41
  lib/sup/modes/poll-mode.rb
22
- lib/sup/modes/edit-message-mode.rb
42
+ lib/sup/modes/reply-mode.rb
43
+ lib/sup/modes/resume-mode.rb
44
+ lib/sup/modes/scroll-mode.rb
45
+ lib/sup/modes/search-results-mode.rb
46
+ lib/sup/modes/text-mode.rb
23
47
  lib/sup/modes/thread-index-mode.rb
24
- lib/sup/modes/person-search-results-mode.rb
25
- lib/sup/modes/inbox-mode.rb
26
48
  lib/sup/modes/thread-view-mode.rb
27
- lib/sup/modes/log-mode.rb
28
- lib/sup/modes/buffer-list-mode.rb
29
- lib/sup/modes/text-mode.rb
30
- lib/sup/modes/label-list-mode.rb
31
- lib/sup/modes/help-mode.rb
32
- lib/sup/logger.rb
33
- lib/sup/util.rb
34
- lib/sup/update.rb
35
- lib/sup/label.rb
36
- lib/sup/message.rb
37
- lib/sup/mode.rb
38
- lib/sup/keymap.rb
39
- lib/sup/textfield.rb
40
- lib/sup/contact.rb
41
- lib/sup/account.rb
42
- lib/sup/draft.rb
43
- lib/sup/mbox.rb
44
- lib/sup/poll.rb
45
49
  lib/sup/person.rb
46
- lib/sup/index.rb
47
- lib/sup/thread.rb
48
- lib/sup/buffer.rb
50
+ lib/sup/poll.rb
49
51
  lib/sup/sent.rb
50
- lib/sup/tagger.rb
51
- lib/sup/colormap.rb
52
52
  lib/sup/source.rb
53
- lib/sup/imap.rb
54
- lib/sup.rb
53
+ lib/sup/tagger.rb
54
+ lib/sup/textfield.rb
55
+ lib/sup/thread.rb
56
+ lib/sup/update.rb
57
+ lib/sup/util.rb
data/README.txt CHANGED
@@ -4,13 +4,30 @@ sup
4
4
 
5
5
  == DESCRIPTION:
6
6
 
7
- Sup is an attempt to take the UI innovations of web-based email
8
- readers (ok, really just GMail) and to combine them with the
9
- traditional wholesome goodness of a console-based email client.
7
+ Sup is a console-based email client that combines the best
8
+ features of GMail, mutt, and emacs. Sup matches the power of GMail
9
+ with the speed and simplicity of a console interface.
10
10
 
11
- Sup is designed to work with massive amounts of email, potentially
12
- spread out across different mbox files, IMAP folders, and GMail
13
- accounts, and to pull them all together into a single interface.
11
+ Sup makes it easy to:
12
+ - Handle massive amounts of email.
13
+
14
+ - Mix email from different sources: mbox files (even across
15
+ different machines), IMAP folders, POP accounts, and GMail
16
+ accounts.
17
+
18
+ - Instantaneously search over your entire email collection. Search
19
+ over body text, or use a query language to combine search
20
+ predicates in any way.
21
+
22
+ - Handle multiple accounts. Replying to email sent to a particular
23
+ account will use the correct SMTP server, signature, and from
24
+ address.
25
+
26
+ - Add custom code to handle certain types of messages or to handle
27
+ certain types of text within messages.
28
+
29
+ - Organize email with user-defined labels, automatically track
30
+ recent contacts, and much more!
14
31
 
15
32
  The goal of Sup is to become the email client of choice for nerds
16
33
  everywhere.
@@ -21,11 +38,10 @@ Features:
21
38
 
22
39
  - Scalability to massive amounts of email. Immediate startup and
23
40
  operability, regardless of how much amount of email you have.
24
- (At least, once everything's been indexed.)
25
41
 
26
- - Immediate full-text search of your entire email archive, using
27
- the full Ferret query langauge. Search over message bodies, labels,
28
- from: and to: fields, or any combination thereof.
42
+ - Immediate full-text search of your entire email archive, using the
43
+ Ferret query langauge. Search over message bodies, labels, from: and
44
+ to: fields, or any combination thereof.
29
45
 
30
46
  - Thread-centrism. Operations are performed at the thread, not the
31
47
  message level. Entire threads are manipulated and viewed (with
@@ -34,10 +50,11 @@ Features:
34
50
  - Labels instead of folders. Drop that tired old metaphor and you'll
35
51
  see how much easier it is to organize email.
36
52
 
37
- - GMail-style thread management. Archive a thread, and it will
38
- disappear from your inbox until someone replies. Kill a thread, and
39
- it will never come back to your inbox. (But it will still show up in
40
- searches, of course.)
53
+ - GMail-style thread management (but better!). Archive a thread, and
54
+ it will disappear from your inbox until someone replies. Kill a
55
+ thread, and it will never come back to your inbox (but will still
56
+ show up in searches.) Mark a thread as spam and you'll never again
57
+ see it unless explicitly searching for spam.
41
58
 
42
59
  - Console based interface. No mouse clicking required!
43
60
 
@@ -53,8 +70,8 @@ Features:
53
70
 
54
71
  Current limitations which will be fixed:
55
72
 
56
- - Support for mbox and IMAP only at this point. No support for POP, mh,
57
- or GMail mailstores.
73
+ - Support for mbox, remote mbox, and IMAP only at this point. No
74
+ support for POP, mh, or GMail mailstores.
58
75
 
59
76
  - No internationalization support. No wide characters, no subject
60
77
  demangling.
@@ -71,40 +88,47 @@ Current limitations which will be fixed:
71
88
  2. sup
72
89
  3. edit ~/.sup/config.yaml for the (very few) settings sup has
73
90
 
74
- Where <source> is a filename (for mbox files), or an imap or imaps
75
- url. In the case of imap, don't put the username and password in
76
- the URI (which is a terrible, terrible idea). You will be prompted
77
- for them.
91
+ Where <source> is a filename (for mbox files), an imap or imaps URI,
92
+ or a mbox+ssh URI (for remote mbox files). You will be prompted for
93
+ a username and password if required.
78
94
 
79
95
  sup-import has several options which control whether you want
80
- messages from particular mailboxes not to be added to the inbox,
81
- or not to be marked as new, so run it with -h for help.
96
+ messages from particular mailboxes not to be added to the inbox, or
97
+ not to be marked as new, so run it with -h for help.
82
98
 
83
99
  Note that Sup never changes the contents of any mailboxes; it only
84
100
  indexes in to them. So it shouldn't ever corrupt your mail. The flip
85
101
  side is that if you change a mailbox (e.g. delete messages, or, in
86
- the case of mbox files, read an unread message) then Sup may crash,
87
- and will tell you to run sup-import --rebuild to recalculate the
88
- offsets within the mailbox.
102
+ the case of mbox files, read an unread message) then Sup will be
103
+ unable to load messages from that source and will ask you to run
104
+ sup-import --rebuild.
89
105
 
90
106
  == REQUIREMENTS:
91
107
 
92
108
  * ferret >= 0.10.13
93
- * ncurses >= 0.9.1
94
- * rmail >= 0.17
109
+ * ncurses
110
+ * rmail
111
+ * highline
95
112
 
96
113
  == INSTALL:
97
114
 
98
115
  * gem install sup -y
99
- * Then, in rmail, change line 159 of multipart.rb to:
116
+
117
+ == KNOWN BUGS IN OTHER PACKAGES:
118
+ * If you get an error about frozen strings in RubyMail when importing
119
+ certain messages with attachments, in rmail, change line 159 of
120
+ multipart.rb to:
100
121
  chunk = chunk[0..start]
101
- (Sorry; it's an unsupported package.) You might be able to get away
102
- without doing this but if you get frozen string exceptions when
103
- reading in multipart messages, this is what you need to change.
122
+ * Occasionally Ferret produces something the Ruby GC doesn't like
123
+ (particularly when importing messages from very large sources).
124
+ No worries, just re-run sup-import. (This is unresolved atm.)
125
+ * There are a couple other Ferret issues with outstanding patches but
126
+ they are pretty rare.
127
+
104
128
 
105
129
  == LICENSE:
106
130
 
107
- Copyright (c) 2006 William Morgan.
131
+ Copyright (c) 2006, 2007 William Morgan.
108
132
 
109
133
  This program is free software; you can redistribute it and/or
110
134
  modify it under the terms of the GNU General Public License
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ Hoe.new('sup', Redwood::VERSION) do |p|
12
12
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[2].gsub(/^\s+/, "")
13
13
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
14
  p.email = "wmorgan-sup@masanjin.net"
15
- p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17']]
15
+ p.extra_deps = [['ferret', '>= 0.10.13'], ['ncurses', '>= 0.9.1'], ['rmail', '>= 0.17'], 'highline']
16
16
  end
17
17
 
18
18
  rule 'ss?.png' => 'ss?-small.png' do |t|
data/bin/sup CHANGED
@@ -4,20 +4,9 @@ require 'rubygems'
4
4
  require 'ncurses'
5
5
  require "sup"
6
6
 
7
- module Redwood
7
+ Thread.abort_on_exception = true # make debugging possible
8
8
 
9
- $exception = nil
10
- def reporting_thread
11
- ::Thread.new do
12
- begin
13
- yield
14
- rescue Exception => e
15
- $exception ||= e
16
- raise
17
- end
18
- end
19
- end
20
- module_function :reporting_thread
9
+ module Redwood
21
10
 
22
11
  global_keymap = Keymap.new do |k|
23
12
  k.add :quit, "Quit Redwood", 'q'
@@ -32,6 +21,7 @@ global_keymap = Keymap.new do |k|
32
21
  k.add :list_labels, "List labels", 'L'
33
22
  k.add :poll, "Poll for new messages", 'P'
34
23
  k.add :compose, "Compose new message", 'm'
24
+ k.add :recall_draft, "Edit most recent draft message", 'R'
35
25
  end
36
26
 
37
27
  def start_cursing
@@ -50,16 +40,8 @@ def stop_cursing
50
40
  end
51
41
  module_function :start_cursing, :stop_cursing
52
42
 
53
- Redwood::SentManager.new Redwood::SENT_FN
54
- Redwood::ContactManager.new Redwood::CONTACT_FN
55
- Redwood::LabelManager.new Redwood::LABEL_FN
56
- Redwood::AccountManager.new $config[:accounts]
57
- Redwood::DraftManager.new Redwood::DRAFT_DIR
58
- Redwood::UpdateManager.new
59
- Redwood::PollManager.new
60
-
43
+ Redwood::start
61
44
  Index.new.load
62
- log "loaded #{Index.size} messages from index"
63
45
 
64
46
  if(s = Index.source_for DraftManager.source_name)
65
47
  DraftManager.source = s
@@ -72,7 +54,7 @@ if(s = Index.source_for SentManager.source_name)
72
54
  else
73
55
  Index.add_source SentManager.new_source
74
56
  end
75
-
57
+
76
58
  begin
77
59
  log "starting curses"
78
60
  start_cursing
@@ -120,7 +102,8 @@ begin
120
102
  bm.draw_screen
121
103
  imode.load_more_threads ibuf.content_height
122
104
 
123
- reporting_thread { sleep 3; PollManager.poll }
105
+ reporting_thread { sleep 5; PollManager.poll }
106
+ PollManager.start_thread
124
107
 
125
108
  until $exception
126
109
  bm.draw_screen
@@ -147,77 +130,73 @@ begin
147
130
  when :list_buffers
148
131
  bm.spawn_unless_exists("Buffer List") { BufferListMode.new }
149
132
  when :list_contacts
150
- mode = ContactListMode.new
151
- bm.spawn "compose to contacts", mode
133
+ bm.spawn_unless_exists("Contact List") { ContactListMode.new }
152
134
  when :search
153
135
  text = bm.ask :search, "query: "
154
136
  next unless text && text !~ /^\s*$/
155
- mode = SearchResultsMode.new text
156
- short_text =
157
- if text.length < 20
158
- text
159
- else
160
- text[0 ... 20] + "..."
161
- end
162
- bm.spawn "search: \"#{short_text}\"", mode
163
- bm.draw_screen
164
- mode.load_more_threads mode.buffer.content_height
165
- when :list_labels
166
- b = BufferManager.spawn_unless_exists("all labels") do
167
- LabelListMode.new
137
+
138
+ begin
139
+ qobj = Index.parse_user_query_string text
140
+ short_text = text.length < 20 ? text : text[0 ... 20] + "..."
141
+ log "built query from #{text.inspect}: #{qobj}"
142
+ mode = SearchResultsMode.new qobj
143
+ bm.spawn "search: \"#{short_text}\"", mode
144
+ mode.load_more_threads mode.buffer.content_height
145
+ rescue Ferret::QueryParser::QueryParseException => e
146
+ bm.flash "Couldn't parse query."
168
147
  end
148
+
149
+ when :list_labels
150
+ b = bm.spawn_unless_exists("Label List") { LabelListMode.new }
169
151
  b.mode.load_in_background
170
152
  when :compose
171
153
  mode = ComposeMode.new
172
- bm.spawn "new message", mode
154
+ bm.spawn "New Message", mode
173
155
  mode.edit
174
156
  when :poll
175
- BufferManager.raise_to_front PollManager.buffer
176
- PollManager.poll
157
+ bm.raise_to_front PollManager.buffer
158
+ reporting_thread { PollManager.poll }
159
+ when :recall_draft
160
+ case Index.num_results_for :label => :draft
161
+ when 0
162
+ bm.flash "No draft messages."
163
+ when 1
164
+ m = nil
165
+ Index.each_id_by_date(:label => :draft) { |mid, builder| m = builder.call }
166
+ BufferManager.spawn "Edit message", ResumeMode.new(m)
167
+ else
168
+ b = BufferManager.spawn_unless_exists(:draft) do
169
+ mode = LabelSearchResultsMode.new [:draft]
170
+ end
171
+ b.mode.load_more_threads b.content_height
172
+ end
177
173
  when :nothing
178
174
  when :redraw
179
175
  bm.completely_redraw_screen
180
176
  else
181
- BufferManager.flash "Unknown key press '#{c.to_character}' for #{bm.focus_buf.mode.name}."
177
+ bm.flash "Unknown key press '#{c.to_character}' for #{bm.focus_buf.mode.name}."
182
178
  end
183
179
  end
184
180
  end
185
181
  end
186
182
  bm.kill_all_buffers
187
- Redwood::LabelManager.save
188
- Redwood::ContactManager.save
189
183
  rescue Exception => e
190
184
  $exception ||= e
191
185
  ensure
186
+ Redwood::finish
192
187
  stop_cursing
193
188
  end
194
189
 
195
190
  Index.save unless $exception # TODO: think about this
196
191
 
197
192
  if $exception
198
- case $exception
199
- when IndexError
200
- $stderr.puts <<EOS
201
- An error occurred while parsing a message from source:
202
- #{$exception.source}.
203
- Typically, this means that the source has been modified in some
204
- way which has rendered the messages invalid. For example, if it's
205
- an mbox file, you may have read or deleted messages using another
206
- mail client.
207
-
208
- You must rebuild the index for this source. Please run:
209
- sup-import --rebuild #{$exception.source}
210
- to correct this error.
211
- EOS
212
- #' stupid ruby-mode
213
- else
214
- $stderr.puts <<EOS
193
+ $stderr.puts <<EOS
215
194
  ----------------------------------------------------------------
216
195
  I'm very sorry, but it seems that an error occurred in Sup.
217
196
  Please accept my sincere apologies. If you don't mind, please
218
197
  send the backtrace below and a brief report of the circumstances
219
- to user wmorgan-sup at site masanjin dot net so that I might
220
- address this problem. Thank you!
198
+ to wmorgan-sup at masanjin dot nets so that I might address this
199
+ problem. Thank you!
221
200
 
222
201
  Sincerely,
223
202
  William
@@ -226,7 +205,6 @@ William
226
205
  The problem was: #{$exception.message} (error type #{$exception.class.name})
227
206
  A backtrace follows:
228
207
  EOS
229
- end
230
208
  raise $exception
231
209
  end
232
210
 
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'uri'
4
+ require 'rubygems'
5
+ require 'highline/import'
3
6
  require "sup"
4
7
 
8
+
9
+ Thread.abort_on_exception = true # make debugging possible
10
+
5
11
  class Float
6
12
  def to_s; sprintf '%.2f', self; end
7
13
  end
@@ -57,6 +63,39 @@ EOS
57
63
  exit
58
64
  end
59
65
 
66
+ ## for sources that require login information, prompt the user for
67
+ ## that. also provide a list of previously-defined login info to
68
+ ## choose from, if any.
69
+ def get_login_info uri, sources
70
+ uri = URI(uri)
71
+ accounts = sources.map do |s|
72
+ next unless s.respond_to?(:username)
73
+ suri = URI(s.uri)
74
+ [suri.host, s.username, s.password]
75
+ end.compact.uniq.sort_by { |h, u, p| h == uri.host ? 0 : 1 }
76
+
77
+ username, password = nil, nil
78
+ unless accounts.empty?
79
+ say "Would you like to use the same account as for a previous source?"
80
+ choose do |menu|
81
+ accounts.each do |host, olduser, oldpw|
82
+ menu.choice("Use the account info for #{olduser}@#{host}") { username, password = olduser, oldpw }
83
+ end
84
+ menu.choice("Use a new account") { }
85
+ menu.prompt = "Account selection? "
86
+ end
87
+ end
88
+
89
+ unless username && password
90
+ username = ask("Username for #{uri.host}: ");
91
+ password = ask("Password for #{uri.host}: ") { |q| q.echo = false }
92
+ puts # why?
93
+ end
94
+
95
+ [username, password]
96
+ end
97
+
98
+
60
99
  educate_user if ARGV.member? '--help'
61
100
 
62
101
  archive = ARGV.delete "--archive"
@@ -79,25 +118,27 @@ if(o = ARGV.find { |x| x =~ /^--/ })
79
118
  educate_user
80
119
  end
81
120
 
82
- puts "loading index..."
121
+ $terminal.wrap_at = :auto
122
+ Redwood::start
83
123
  index = Redwood::Index.new
84
124
  index.load
85
- puts "loaded index of #{index.size} messages"
86
125
 
87
- sources = ARGV.map do |fn|
88
- fn = "mbox://#{fn}" unless fn =~ %r!://!
89
- source = index.source_for fn
126
+ sources = ARGV.map do |uri|
127
+ uri = "mbox://#{uri}" unless uri =~ %r!://!
128
+ source = index.source_for uri
90
129
  unless source
91
130
  source =
92
- case fn
131
+ case uri
132
+ when %r!^mbox\+ssh://!
133
+ say "For SSH connections, if you will use public key authentication, you may leave the username and password blank."
134
+ say "\n"
135
+ username, password = get_login_info uri, index.sources
136
+ Redwood::MBox::SSHLoader.new(uri, username, password, nil, !unusual, !!archive)
93
137
  when %r!^imaps?://!
94
- print "Username for #{fn}: "
95
- username = $stdin.gets.chomp
96
- print "Password for #{fn} (warning: cleartext): "
97
- password = $stdin.gets.chomp
98
- Redwood::IMAP.new(fn, username, password, nil, !unusual, !!archive)
138
+ username, password = get_login_info uri, index.sources
139
+ Redwood::IMAP.new(uri, username, password, nil, !unusual, !!archive)
99
140
  else
100
- Redwood::MBox::Loader.new(fn, nil, !unusual, !!archive)
141
+ Redwood::MBox::Loader.new(uri, nil, !unusual, !!archive)
101
142
  end
102
143
  index.add_source source
103
144
  end
@@ -118,7 +159,7 @@ start = Time.now
118
159
  begin
119
160
  sources.each do |source|
120
161
  if source.broken?
121
- puts "error loading messages from #{source}: #{source.broken_msg}"
162
+ $stderr.puts "error loading messages from #{source}: #{source.broken_msg}"
122
163
  next
123
164
  end
124
165
  next if source.done?
@@ -137,8 +178,9 @@ begin
137
178
  else
138
179
  found[m.id] = true
139
180
  end
181
+
140
182
  m.remove_label :unread if m.status == "RO" unless force_read
141
- puts "# message at #{offset}, labels: #{labels * ', '}" unless rebuild || force_rebuild
183
+ puts "# message at #{offset}, labels: #{labels * ', '}"
142
184
  if (rebuild || force_rebuild) &&
143
185
  (docid, entry = index.load_entry_for_id(m.id)) && entry
144
186
  if force_rebuild || entry[:source_info].to_i != offset
@@ -162,7 +204,9 @@ begin
162
204
  puts "loaded #{num} messages" unless num == 0
163
205
  end
164
206
  ensure
207
+ $stderr.puts "saving index and sources..."
165
208
  index.save
209
+ Redwood::finish
166
210
  end
167
211
 
168
212
  if rebuild || force_rebuild