sup 0.1 → 0.2

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 (48) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +5 -1
  3. data/Rakefile +2 -1
  4. data/bin/sup +27 -10
  5. data/bin/sup-add +2 -1
  6. data/bin/sup-sync-back +51 -23
  7. data/doc/FAQ.txt +29 -37
  8. data/doc/Hooks.txt +38 -0
  9. data/doc/{UserGuide.txt → NewUserGuide.txt} +27 -21
  10. data/doc/TODO +91 -57
  11. data/lib/sup.rb +17 -1
  12. data/lib/sup/buffer.rb +80 -16
  13. data/lib/sup/colormap.rb +0 -2
  14. data/lib/sup/contact.rb +3 -2
  15. data/lib/sup/crypto.rb +110 -0
  16. data/lib/sup/draft.rb +2 -6
  17. data/lib/sup/hook.rb +131 -0
  18. data/lib/sup/imap.rb +27 -16
  19. data/lib/sup/index.rb +38 -14
  20. data/lib/sup/keymap.rb +0 -2
  21. data/lib/sup/label.rb +30 -9
  22. data/lib/sup/logger.rb +12 -1
  23. data/lib/sup/maildir.rb +48 -3
  24. data/lib/sup/mbox.rb +1 -1
  25. data/lib/sup/mbox/loader.rb +22 -12
  26. data/lib/sup/mbox/ssh-loader.rb +1 -1
  27. data/lib/sup/message-chunks.rb +198 -0
  28. data/lib/sup/message.rb +154 -115
  29. data/lib/sup/modes/compose-mode.rb +18 -0
  30. data/lib/sup/modes/contact-list-mode.rb +1 -1
  31. data/lib/sup/modes/edit-message-mode.rb +112 -31
  32. data/lib/sup/modes/file-browser-mode.rb +1 -1
  33. data/lib/sup/modes/inbox-mode.rb +1 -1
  34. data/lib/sup/modes/label-list-mode.rb +8 -6
  35. data/lib/sup/modes/label-search-results-mode.rb +4 -1
  36. data/lib/sup/modes/log-mode.rb +1 -1
  37. data/lib/sup/modes/reply-mode.rb +18 -16
  38. data/lib/sup/modes/search-results-mode.rb +1 -1
  39. data/lib/sup/modes/thread-index-mode.rb +61 -33
  40. data/lib/sup/modes/thread-view-mode.rb +111 -102
  41. data/lib/sup/person.rb +5 -1
  42. data/lib/sup/poll.rb +36 -7
  43. data/lib/sup/sent.rb +1 -0
  44. data/lib/sup/source.rb +7 -3
  45. data/lib/sup/textfield.rb +48 -34
  46. data/lib/sup/thread.rb +9 -5
  47. data/lib/sup/util.rb +16 -22
  48. metadata +7 -3
@@ -1,3 +1,12 @@
1
+ == 0.2 / 2007-10-29
2
+ * Complete hook system for user-inserted code.
3
+ * GPG signature verification and decryption.
4
+ * Automatically scold users who top-post.
5
+ * Automatically warn when sending a message with words like
6
+ "attachment" in the body if there aren't actually any attachments to
7
+ the message.
8
+ * Millions of bugfixes.
9
+
1
10
  == 0.1 / 2007-07-17
2
11
  * MIME attachment creation.
3
12
  * i18n support: character set conversion and rfc2047 header decoding.
@@ -12,15 +12,18 @@ bin/sup-recover-sources
12
12
  bin/sup-sync
13
13
  bin/sup-sync-back
14
14
  doc/FAQ.txt
15
+ doc/Hooks.txt
16
+ doc/NewUserGuide.txt
15
17
  doc/Philosophy.txt
16
18
  doc/TODO
17
- doc/UserGuide.txt
18
19
  lib/sup.rb
19
20
  lib/sup/account.rb
20
21
  lib/sup/buffer.rb
21
22
  lib/sup/colormap.rb
22
23
  lib/sup/contact.rb
24
+ lib/sup/crypto.rb
23
25
  lib/sup/draft.rb
26
+ lib/sup/hook.rb
24
27
  lib/sup/imap.rb
25
28
  lib/sup/index.rb
26
29
  lib/sup/keymap.rb
@@ -31,6 +34,7 @@ lib/sup/mbox.rb
31
34
  lib/sup/mbox/loader.rb
32
35
  lib/sup/mbox/ssh-file.rb
33
36
  lib/sup/mbox/ssh-loader.rb
37
+ lib/sup/message-chunks.rb
34
38
  lib/sup/message.rb
35
39
  lib/sup/mode.rb
36
40
  lib/sup/modes/buffer-list-mode.rb
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'hoe'
5
+ $:.unshift 'lib' # force loading from ./lib/ if it exists
5
6
  require 'sup'
6
7
 
7
8
  class Hoe
@@ -23,7 +24,7 @@ rule 'ss?.png' => 'ss?-small.png' do |t|
23
24
  end
24
25
 
25
26
  ## is there really no way to make a rule for this?
26
- WWW_FILES = %w(www/index.html README.txt doc/Philosophy.txt doc/FAQ.txt doc/UserGuide.txt www/main.css)
27
+ WWW_FILES = %w(www/index.html README.txt doc/Philosophy.txt doc/FAQ.txt doc/NewUserGuide.txt www/main.css)
27
28
 
28
29
  SCREENSHOTS = FileList["www/ss?.png"]
29
30
  SCREENSHOTS_SMALL = []
data/bin/sup CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'ncurses'
5
+ require 'curses'
5
6
  require 'fileutils'
6
7
  require 'trollop'
7
8
  require "sup"
@@ -16,9 +17,15 @@ Usage:
16
17
 
17
18
  Options are:
18
19
  EOS
20
+ opt :list_hooks, "List all hooks and descriptions thereof, and quit."
19
21
  opt :no_threads, "Turn of threading. Helps with debugging. (Necessarily disables background polling for new messages.)"
20
22
  end
21
23
 
24
+ if $opts[:list_hooks]
25
+ Redwood::HookManager.print_hooks
26
+ exit
27
+ end
28
+
22
29
  Thread.abort_on_exception = true # make debugging possible
23
30
 
24
31
  module Redwood
@@ -85,6 +92,7 @@ EOS
85
92
  end
86
93
 
87
94
  begin
95
+ extend CanSpawnComposeMode
88
96
  Redwood::start
89
97
  Index.load
90
98
 
@@ -120,6 +128,10 @@ begin
120
128
  c.add :alternate_patina_color, Ncurses::COLOR_BLACK, Ncurses::COLOR_BLUE
121
129
  c.add :missing_message_color, Ncurses::COLOR_BLACK, Ncurses::COLOR_RED
122
130
  c.add :attachment_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
131
+ c.add :cryptosig_valid_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK, Ncurses::A_BOLD
132
+ c.add :cryptosig_unknown_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
133
+ c.add :cryptosig_invalid_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_RED, Ncurses::A_BOLD
134
+ c.add :generic_notice_patina_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
123
135
  c.add :quote_patina_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
124
136
  c.add :sig_patina_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
125
137
  c.add :quote_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK
@@ -139,6 +151,9 @@ begin
139
151
  Ncurses::A_BOLD
140
152
  c.add :completion_character_color, Ncurses::COLOR_WHITE,
141
153
  Ncurses::COLOR_BLACK, Ncurses::A_BOLD
154
+ c.add :reply_mode_selected_color, Ncurses::COLOR_YELLOW, Ncurses::COLOR_BLACK, Ncurses::A_BOLD
155
+ c.add :reply_mode_unselected_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
156
+ c.add :reply_mode_label_color, Ncurses::COLOR_CYAN, Ncurses::COLOR_BLACK
142
157
  end
143
158
 
144
159
  log "initializing buffer manager"
@@ -217,28 +232,30 @@ begin
217
232
  bm.flash "Couldn't parse query."
218
233
  end
219
234
  when :list_labels
220
- labels = LabelManager.listable_label_strings
235
+ labels = LabelManager.listable_labels.map { |l| LabelManager.string_for l }
221
236
  user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
222
- user_label = bm.spawn_modal("Label list", LabelListMode.new) if user_label && user_label.empty?
237
+ user_label =
238
+ case user_label
239
+ when nil, /^\s*$/
240
+ bm.spawn_modal("Label list", LabelListMode.new) if user_label && user_label.empty?
241
+ else
242
+ LabelManager.label_for user_label
243
+ end
223
244
 
224
- label = LabelManager.label_for user_label if user_label
225
- case label
245
+ case user_label
226
246
  when nil
227
247
  when :inbox
228
248
  BufferManager.raise_to_front InboxMode.instance.buffer
229
249
  else
230
250
  b = BufferManager.spawn_unless_exists("All threads with label '#{user_label}'") do
231
- mode = LabelSearchResultsMode.new([label])
251
+ mode = LabelSearchResultsMode.new([user_label])
232
252
  end
233
253
  b.mode.load_threads :num => b.content_height
234
254
  end
235
255
 
236
256
  when :compose
237
- mode = ComposeMode.new
238
- bm.spawn "New Message", mode
239
- mode.edit
257
+ spawn_compose_mode
240
258
  when :poll
241
- # bm.raise_to_front PollManager.buffer
242
259
  reporting_thread { PollManager.poll }
243
260
  when :recall_draft
244
261
  case Index.num_results_for :label => :draft
@@ -249,7 +266,7 @@ begin
249
266
  Index.each_id_by_date(:label => :draft) { |mid, builder| m = builder.call }
250
267
  r = ResumeMode.new(m)
251
268
  BufferManager.spawn "Edit message", r
252
- r.edit
269
+ r.edit_message
253
270
  else
254
271
  b = BufferManager.spawn_unless_exists("All drafts") do
255
272
  mode = LabelSearchResultsMode.new [:draft]
@@ -93,7 +93,6 @@ begin
93
93
  end
94
94
 
95
95
  parsed_uri = URI(uri)
96
- Trollop::die "no path component to uri: #{parsed_uri}" unless parsed_uri.path
97
96
 
98
97
  source =
99
98
  case parsed_uri.scheme
@@ -109,6 +108,8 @@ begin
109
108
  Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
110
109
  when "mbox"
111
110
  Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
111
+ when nil
112
+ Trollop::die "Sources must be specified with an URI"
112
113
  else
113
114
  Trollop::die "Unknown source type #{parsed_uri.scheme.inspect}"
114
115
  end
@@ -8,14 +8,17 @@ require "sup"
8
8
 
9
9
  ## save a message 'm' to an open file pointer 'fp'
10
10
  def save m, fp
11
- m.source.each_raw_full_message_line(m.source_info) { |l| fp.print l }
11
+ m.source.each_raw_message_line(m.source_info) { |l| fp.print l }
12
+ end
13
+ def die msg
14
+ $stderr.puts "Error: #{msg}"
15
+ exit(-1)
12
16
  end
13
17
 
14
18
  opts = Trollop::options do
15
19
  version "sup-sync-back (sup #{Redwood::VERSION})"
16
20
  banner <<EOS
17
- Partially synchronizes a message source with the Sup index by deleting
18
- or moving any messages from the source that are marked as deleted or
21
+ Drop or move messages from Sup sources that are marked as deleted or
19
22
  spam in the Sup index.
20
23
 
21
24
  Currently only works with mbox sources.
@@ -26,20 +29,30 @@ Usage:
26
29
  where <source>* is zero or more source URIs. If no sources are given,
27
30
  sync back all usual sources.
28
31
 
29
- You probably want to run sup-sync --changed after this command.
32
+ You almost certainly want to run sup-sync --changed after this command.
33
+ Running this does not change the index.
30
34
 
31
35
  Options include:
32
36
  EOS
33
- opt :delete_deleted, "Delete deleted messages.", :default => false, :short => "d"
37
+ opt :drop_deleted, "Drop deleted messages.", :default => false, :short => "d"
34
38
  opt :move_deleted, "Move deleted messages to a local mbox file.", :type => String, :short => :none
35
- opt :delete_spam, "Delete spam messages.", :default => false, :short => "s"
39
+ opt :drop_spam, "Drop spam messages.", :default => false, :short => "s"
36
40
  opt :move_spam, "Move spam messages to a local mbox file.", :type => String, :short => :none
41
+
42
+ opt :with_dotlockfile, "Specific dotlockfile location (mbox files only).", :default => "/usr/bin/dotlockfile", :short => :none
43
+ opt :dont_use_dotlockfile, "Don't use dotlockfile to lock mbox files. Dangerous if other processes modify them concurrently.", :default => false, :short => :none
44
+
37
45
  opt :verbose, "Print message ids as they're processed."
38
46
  opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
39
47
  opt :version, "Show version information", :short => :none
40
48
 
41
- conflicts :delete_deleted, :move_deleted
42
- conflicts :delete_spam, :move_spam
49
+ conflicts :drop_deleted, :move_deleted
50
+ conflicts :drop_spam, :move_spam
51
+ end
52
+
53
+ unless opts[:drop_deleted] || opts[:move_deleted] || opts[:drop_spam] || opts[:move_spam]
54
+ puts "Nothing to do."
55
+ exit
43
56
  end
44
57
 
45
58
  Redwood::start
@@ -52,12 +65,14 @@ unless opts[:dry_run]
52
65
  spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam]
53
66
  end
54
67
 
68
+ dotlockfile = opts[:with_dotlockfile] || "/usr/bin/dotlockfile"
69
+
55
70
  begin
56
71
  index.load
57
72
 
58
73
  sources = ARGV.map do |uri|
59
- s = index.source_for(uri) or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
60
- s.is_a?(Redwood::MBox::Loader) or Trollop::die "#{uri} is not an mbox source."
74
+ s = index.source_for(uri) or die "unknown source: #{uri}. Did you add it with sup-add first?"
75
+ s.is_a?(Redwood::MBox::Loader) or die "#{uri} is not an mbox source."
61
76
  s
62
77
  end
63
78
 
@@ -65,17 +80,25 @@ begin
65
80
  sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader }
66
81
  end
67
82
 
83
+ unless sources.all? { |s| s.file_path.nil? } || File.executable?(dotlockfile) || opts[:dont_use_dotlockfile]
84
+ die <<EOS
85
+ can't execute dotlockfile binary: #{dotlockfile}. Specify --with-dotlockfile
86
+ if it's in a nonstandard location, or, if you want to live dangerously, try
87
+ --dont-use-dotlockfile
88
+ EOS
89
+ end
90
+
68
91
  modified_sources = []
69
92
  sources.each do |source|
70
93
  $stderr.puts "Scanning #{source}..."
71
94
 
72
- unless ((opts[:delete_deleted] || opts[:move_deleted]) && index.has_any_from_source_with_label?(source, :deleted)) || ((opts[:delete_spam] || opts[:move_spam]) && index.has_any_from_source_with_label?(source, :spam))
95
+ unless ((opts[:drop_deleted] || opts[:move_deleted]) && index.has_any_from_source_with_label?(source, :deleted)) || ((opts[:drop_spam] || opts[:move_spam]) && index.has_any_from_source_with_label?(source, :spam))
73
96
  $stderr.puts "Nothing to do from this source; skipping"
74
97
  next
75
98
  end
76
99
 
77
100
  source.reset!
78
- num_deleted = num_moved = num_scanned = 0
101
+ num_dropped = num_moved = num_scanned = 0
79
102
 
80
103
  out_fp = Tempfile.new "sup-sync-back-#{source.id}"
81
104
  Redwood::PollManager.add_messages_from source do |m, offset, entry|
@@ -85,9 +108,9 @@ begin
85
108
  labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
86
109
 
87
110
  if labels.member? :deleted
88
- if opts[:delete_deleted]
111
+ if opts[:drop_deleted]
89
112
  puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
90
- num_deleted += 1
113
+ num_dropped += 1
91
114
  elsif opts[:move_deleted] && labels.member?(:deleted)
92
115
  puts "Moving deleted message #{source}##{offset}" if opts[:verbose]
93
116
  save m, deleted_fp unless opts[:dry_run]
@@ -95,9 +118,9 @@ begin
95
118
  end
96
119
 
97
120
  elsif labels.member? :spam
98
- if opts[:delete_spam]
99
- puts "Deleting spam message #{source}##{offset}" if opts[:verbose]
100
- num_deleted += 1
121
+ if opts[:drop_spam]
122
+ puts "Dropping spam message #{source}##{offset}" if opts[:verbose]
123
+ num_dropped += 1
101
124
  elsif opts[:move_spam] && labels.member?(:spam)
102
125
  puts "Moving spam message #{source}##{offset}" if opts[:verbose]
103
126
  save m, spam_fp unless opts[:dry_run]
@@ -112,15 +135,21 @@ begin
112
135
 
113
136
  nil # don't actually add anything!
114
137
  end
115
- $stderr.puts "Scanned #{num_scanned}, deleted #{num_deleted}, moved #{num_moved} messages from #{source}."
116
- modified_sources << source if num_deleted > 0 || num_moved > 0
138
+ $stderr.puts "Scanned #{num_scanned}, dropped #{num_dropped}, moved #{num_moved} messages from #{source}."
139
+ modified_sources << source if num_dropped > 0 || num_moved > 0
117
140
  out_fp.close unless opts[:dry_run]
118
141
 
119
- unless opts[:dry_run] || (num_deleted == 0 && num_moved == 0)
142
+ unless opts[:dry_run] || (num_dropped == 0 && num_moved == 0)
120
143
  deleted_fp.flush if deleted_fp
121
144
  spam_fp.flush if spam_fp
122
- $stderr.puts "Moving #{out_fp.path} to #{source.file_path}"
123
- FileUtils.mv out_fp.path, source.file_path
145
+ unless opts[:dont_use_dotlockfile]
146
+ puts "Locking #{source.file_path}..."
147
+ system "#{opts[:dotlockfile]} -l #{source.file_path}"
148
+ puts "Writing #{source.file_path}..."
149
+ FileUtils.cp out_fp.path, source.file_path
150
+ puts "Unlocking #{source.file_path}..."
151
+ system "#{opts[:dotlockfile]} -u #{source.file_path}"
152
+ end
124
153
  end
125
154
  end
126
155
 
@@ -137,7 +166,6 @@ rescue Exception => e
137
166
  File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
138
167
  raise
139
168
  ensure
140
- index.save
141
169
  Redwood::finish
142
170
  index.unlock
143
171
  end
@@ -4,21 +4,22 @@ Sup FAQ
4
4
  Q: What is Sup?
5
5
  A: Sup is a console-based email client for people with a lot of email.
6
6
  It supports tagging, very fast full-text search, automatic contact-
7
- list management, and more. If you're the type of person who treats
8
- email as an extension of your long-term memory, Sup is for you.
7
+ list management, custom code insertion via a hook system, and more.
8
+ If you're the type of person who treats email as an extension of your
9
+ long-term memory, Sup is for you.
9
10
 
10
11
  Q: What does Sup stand for?
11
12
  A: "What's up?"
12
13
 
13
- Q: Sup looks a lot like a text-based Gmail.
14
- A: I stole a lot of their ideas. And improved upon them, of course.
14
+ Q: Sup looks like a text-based Gmail.
15
+ A: I stole their ideas. And improved them!
15
16
 
16
- Q: If you love Gmail so much, why not just use it?
17
- A: I hate ads, I hate using a mouse, and I hate non-programmability
18
- and non-extensibility.
17
+ Q: Why not just use Gmail?
18
+ A: I wrote Sup because I hate ads, I hate using a mouse, and I hate
19
+ non-programmability and non-extensibility.
19
20
 
20
21
  Also, Gmail doesn't let you use a monospace font, which is just
21
- plain lame.
22
+ lame.
22
23
 
23
24
  Also, Gmail encourages top-posting. THIS CANNOT BE TOLERATED!
24
25
 
@@ -43,17 +44,16 @@ Q: But I want to delete it for real, not just add a 'deleted' flag in
43
44
  A: Currently, for mbox sources, there is a batch deletion tool that
44
45
  will strip out all messages marked as spam or deleted.
45
46
 
46
- Q: I got some error message about needing to run sup-sync --changed
47
- when I tried to read a message. What's that about?
48
- A: If messages have been moved, deleted, or altered in a source, Sup
49
- may have to rebuild its index for that source. For example, for
50
- mbox files, reading a single unread message changes the offsets of
51
- every file on disk. Rather than rescanning every time, Sup assumes
52
- sources don't change except by having new messages added. If that
53
- assumption is violated, you'll have to sync the index.
47
+ Q: How well does Sup play with other mail clients?
48
+ A: Not well at all. If messages have been moved, deleted, or altered
49
+ due to some other client, Sup will have to rebuild its index for
50
+ that message source. For example, for mbox files, reading a single
51
+ unread message changes the offsets of every file on disk. Rather
52
+ than rescanning every time, Sup assumes sources don't change except
53
+ by having new messages added. If that assumption is violated,
54
+ you'll have to sync the index.
54
55
 
55
56
  Q: How do I back up my index?
56
- Q: How do I make a state dump?
57
57
  A: Since the contents of the messages are recoverable from their
58
58
  sources using sup-sync, all you need to back up is the message
59
59
  state. To do this, simply run:
@@ -66,13 +66,10 @@ A: Run:
66
66
  sup-sync [<source>+] --restored --restore <dumpfile>
67
67
  where <dumpfile> was created as above.
68
68
 
69
- Q: I upgraded Ferret and the index format changed. I need to
70
- completely rebuild my index. How do I do this?
71
69
  Q: Ferret crashed and I can't read my index. Luckily I made a state
72
70
  dump. What should I do?
73
- A: First, you'll need a complete state dump. If you haven't made
74
- one, you'll need to downgrade Ferret and make a state dump as
75
- above. Then run these commands:
71
+ Q: How do I rebuild the index completely?
72
+ A: Run:
76
73
  rm -rf ~/.sup/ferret # omg wtf
77
74
  sup-sync --all-sources --all --restore <dumpfile>
78
75
  Voila! A brand new index.
@@ -82,29 +79,24 @@ Q: I want to move messages from one source to another. (E.g., my
82
79
  some of those messages to local mbox files.) How do I do that while
83
80
  preserving message state?
84
81
  A: Move the messages from the source to the target using whatever tool
85
- you'd like. Then (and this is the important part), run:
82
+ you'd like. Mutt's a good one. :) Then run:
86
83
  sup-sync --changed <source1> <source2>
87
84
 
88
- If you sup-sync only one source at a time, depending on the order,
89
- the messages may be treated as missing and then deleted from the
90
- index, which means that their states will be lost when you sync the
91
- other source.
85
+ Note that if you sup-sync only one source at a time, depending on
86
+ the order in which you do it, the messages may be treated as
87
+ missing and then deleted from the index, which means that their
88
+ states will be lost when you sync the other source. So do them both
89
+ in one go.
92
90
 
93
91
  Q: What are all these "Redwood" references I see in the code?
94
- A: That was Sup's original name. (Think pine, elm. Although I am a
95
- Mutt user, I couldn't think of a good progression there.) But it was
96
- taken by another project on RubyForge, and wasn't that original,
97
- and was too long to type anyways.
92
+ A: That was Sup's original name. (Think pine, elm. Although I was a
93
+ Mutt user, I couldn't think of a good progression there.) But it
94
+ was taken by another project on RubyForge, and wasn't that
95
+ original, and was too long to type anyways.
98
96
 
99
97
  Maybe one day I'll do a huge search-and-replace on the code, but it
100
98
  doesn't seem that important at this point.
101
99
 
102
- Q: How is Sup possible?
103
- A: Sup is only possible through the hard work of Dave Balmain, the
104
- author of ferret, which is the search engine behind Sup. Ferret is
105
- really a first-class piece of software, and it's due to the
106
- tremendous amount of time and effort he's put in to it.
107
-
108
100
  Common Problems
109
101
  ---------------
110
102
 
@@ -0,0 +1,38 @@
1
+ Sup's Hook System
2
+ -----------------
3
+
4
+ Sup can be easily customized via its hook system, which allows custom
5
+ user code to be injected into Sup's execution path by "hooking" the
6
+ code onto pre-defined events. When those events occur, the code is
7
+ executed.
8
+
9
+ To see which hooks are available, simply run sup -l. Each hook sits in
10
+ a file in ~/.sup/hooks/. Hooks are written in Ruby, and require no
11
+ class or method definitions, just the executable code itself.
12
+
13
+ Information passes from Sup to the hook code via Ruby variables
14
+ (actually method calls), and from the hook code back to Sup via a
15
+ return value. Each hook description lists the variables and return
16
+ value expected, if any.
17
+
18
+ Some example hooks:
19
+
20
+ before-poll:
21
+ ## runs fetchmail before polling
22
+ if (@last_fetchmail_time || Time.now) < Time.now - 60
23
+ say "Running fetchmail..."
24
+ system "fetchmail >& /dev/null"
25
+ say "Done running fetchmail."
26
+ end
27
+ @last_fetchmail_time = Time.now
28
+
29
+
30
+ mime-decode:
31
+ ## turn text/html attachments into plain text, unless they are part
32
+ ## of a multipart/alternative pair
33
+ unless sibling_types.member? "text/plain"
34
+ case content_type
35
+ when "text/html"
36
+ `/usr/bin/w3m -dump -T #{content_type} '#{filename}'`
37
+ end
38
+ end