sup 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +12 -0
  4. data/CONTRIBUTORS +84 -0
  5. data/Gemfile +3 -0
  6. data/HACKING +42 -0
  7. data/History.txt +361 -0
  8. data/LICENSE +280 -0
  9. data/README.md +70 -0
  10. data/Rakefile +12 -0
  11. data/ReleaseNotes +231 -0
  12. data/bin/sup +434 -0
  13. data/bin/sup-add +118 -0
  14. data/bin/sup-config +243 -0
  15. data/bin/sup-dump +43 -0
  16. data/bin/sup-import-dump +101 -0
  17. data/bin/sup-psych-ify-config-files +21 -0
  18. data/bin/sup-recover-sources +87 -0
  19. data/bin/sup-sync +210 -0
  20. data/bin/sup-sync-back-maildir +127 -0
  21. data/bin/sup-tweak-labels +140 -0
  22. data/contrib/colorpicker.rb +100 -0
  23. data/contrib/completion/_sup.zsh +114 -0
  24. data/devel/console.sh +3 -0
  25. data/devel/count-loc.sh +3 -0
  26. data/devel/load-index.rb +9 -0
  27. data/devel/profile.rb +12 -0
  28. data/devel/start-console.rb +5 -0
  29. data/doc/FAQ.txt +119 -0
  30. data/doc/Hooks.txt +79 -0
  31. data/doc/Philosophy.txt +69 -0
  32. data/lib/sup.rb +467 -0
  33. data/lib/sup/account.rb +90 -0
  34. data/lib/sup/buffer.rb +768 -0
  35. data/lib/sup/colormap.rb +239 -0
  36. data/lib/sup/contact.rb +67 -0
  37. data/lib/sup/crypto.rb +461 -0
  38. data/lib/sup/draft.rb +119 -0
  39. data/lib/sup/hook.rb +159 -0
  40. data/lib/sup/horizontal_selector.rb +59 -0
  41. data/lib/sup/idle.rb +42 -0
  42. data/lib/sup/index.rb +882 -0
  43. data/lib/sup/interactive_lock.rb +89 -0
  44. data/lib/sup/keymap.rb +140 -0
  45. data/lib/sup/label.rb +87 -0
  46. data/lib/sup/logger.rb +77 -0
  47. data/lib/sup/logger/singleton.rb +10 -0
  48. data/lib/sup/maildir.rb +257 -0
  49. data/lib/sup/mbox.rb +187 -0
  50. data/lib/sup/message.rb +803 -0
  51. data/lib/sup/message_chunks.rb +328 -0
  52. data/lib/sup/mode.rb +140 -0
  53. data/lib/sup/modes/buffer_list_mode.rb +50 -0
  54. data/lib/sup/modes/completion_mode.rb +55 -0
  55. data/lib/sup/modes/compose_mode.rb +38 -0
  56. data/lib/sup/modes/console_mode.rb +125 -0
  57. data/lib/sup/modes/contact_list_mode.rb +148 -0
  58. data/lib/sup/modes/edit_message_async_mode.rb +110 -0
  59. data/lib/sup/modes/edit_message_mode.rb +728 -0
  60. data/lib/sup/modes/file_browser_mode.rb +109 -0
  61. data/lib/sup/modes/forward_mode.rb +82 -0
  62. data/lib/sup/modes/help_mode.rb +19 -0
  63. data/lib/sup/modes/inbox_mode.rb +85 -0
  64. data/lib/sup/modes/label_list_mode.rb +138 -0
  65. data/lib/sup/modes/label_search_results_mode.rb +38 -0
  66. data/lib/sup/modes/line_cursor_mode.rb +203 -0
  67. data/lib/sup/modes/log_mode.rb +57 -0
  68. data/lib/sup/modes/person_search_results_mode.rb +12 -0
  69. data/lib/sup/modes/poll_mode.rb +19 -0
  70. data/lib/sup/modes/reply_mode.rb +228 -0
  71. data/lib/sup/modes/resume_mode.rb +52 -0
  72. data/lib/sup/modes/scroll_mode.rb +252 -0
  73. data/lib/sup/modes/search_list_mode.rb +204 -0
  74. data/lib/sup/modes/search_results_mode.rb +59 -0
  75. data/lib/sup/modes/text_mode.rb +76 -0
  76. data/lib/sup/modes/thread_index_mode.rb +1033 -0
  77. data/lib/sup/modes/thread_view_mode.rb +941 -0
  78. data/lib/sup/person.rb +134 -0
  79. data/lib/sup/poll.rb +272 -0
  80. data/lib/sup/rfc2047.rb +56 -0
  81. data/lib/sup/search.rb +110 -0
  82. data/lib/sup/sent.rb +58 -0
  83. data/lib/sup/service/label_service.rb +45 -0
  84. data/lib/sup/source.rb +244 -0
  85. data/lib/sup/tagger.rb +50 -0
  86. data/lib/sup/textfield.rb +253 -0
  87. data/lib/sup/thread.rb +452 -0
  88. data/lib/sup/time.rb +93 -0
  89. data/lib/sup/undo.rb +38 -0
  90. data/lib/sup/update.rb +30 -0
  91. data/lib/sup/util.rb +747 -0
  92. data/lib/sup/util/ncurses.rb +274 -0
  93. data/lib/sup/util/path.rb +9 -0
  94. data/lib/sup/util/query.rb +17 -0
  95. data/lib/sup/util/uri.rb +15 -0
  96. data/lib/sup/version.rb +3 -0
  97. data/sup.gemspec +53 -0
  98. data/test/dummy_source.rb +61 -0
  99. data/test/gnupg_test_home/gpg.conf +1 -0
  100. data/test/gnupg_test_home/pubring.gpg +0 -0
  101. data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
  102. data/test/gnupg_test_home/receiver_secring.gpg +0 -0
  103. data/test/gnupg_test_home/receiver_trustdb.gpg +0 -0
  104. data/test/gnupg_test_home/secring.gpg +0 -0
  105. data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -0
  106. data/test/gnupg_test_home/trustdb.gpg +0 -0
  107. data/test/integration/test_label_service.rb +18 -0
  108. data/test/messages/bad-content-transfer-encoding-1.eml +8 -0
  109. data/test/messages/binary-content-transfer-encoding-2.eml +21 -0
  110. data/test/messages/missing-line.eml +9 -0
  111. data/test/test_crypto.rb +109 -0
  112. data/test/test_header_parsing.rb +168 -0
  113. data/test/test_helper.rb +7 -0
  114. data/test/test_message.rb +532 -0
  115. data/test/test_messages_dir.rb +147 -0
  116. data/test/test_yaml_migration.rb +85 -0
  117. data/test/test_yaml_regressions.rb +17 -0
  118. data/test/unit/service/test_label_service.rb +19 -0
  119. data/test/unit/test_horizontal_selector.rb +40 -0
  120. data/test/unit/util/test_query.rb +46 -0
  121. data/test/unit/util/test_string.rb +57 -0
  122. data/test/unit/util/test_uri.rb +19 -0
  123. metadata +423 -0
@@ -0,0 +1,100 @@
1
+ require 'rubygems'
2
+
3
+ require 'ncursesw'
4
+
5
+ Ncurses.initscr
6
+ Ncurses.noecho
7
+ Ncurses.cbreak
8
+ Ncurses.start_color
9
+
10
+ Ncurses.curs_set 0
11
+ Ncurses.move 0, 0
12
+ Ncurses.clear
13
+ Ncurses.refresh
14
+ cc = Ncurses.COLORS
15
+
16
+ Ncurses::keypad(Ncurses::stdscr, 1)
17
+ Ncurses::mousemask(Ncurses::ALL_MOUSE_EVENTS | Ncurses::REPORT_MOUSE_POSITION, [])
18
+
19
+ fail "color count is #{cc}, expected 256" unless cc == 256
20
+
21
+ 1.upto(255) do |c|
22
+ Ncurses.init_pair(c, 0, c)
23
+ end
24
+
25
+ def cell y, x, c
26
+ @map[[y,x]] = c
27
+ Ncurses.attron(Ncurses.COLOR_PAIR(c))
28
+ Ncurses.mvaddstr(y, x, " ")
29
+ Ncurses.attroff(Ncurses.COLOR_PAIR(c))
30
+ end
31
+
32
+ def handle_click y, x
33
+ c = @map[[y,x]] or return
34
+ name = case c
35
+ when 0...16
36
+ c.to_s
37
+ when 16...232
38
+ 'c' + (c-16).to_s(6).rjust(3,'0')
39
+ when 232...256
40
+ 'g' + (c-232).to_s
41
+ end
42
+
43
+ Ncurses.mvaddstr 11, 0, "#{name} "
44
+
45
+ Ncurses.attron(Ncurses.COLOR_PAIR(c))
46
+ 10.times do |i|
47
+ 20.times do |j|
48
+ y = 13 + i
49
+ x = j
50
+ Ncurses.mvaddstr(y, x, " ")
51
+ end
52
+ end
53
+ Ncurses.attroff(Ncurses.COLOR_PAIR(c))
54
+ end
55
+
56
+ @map = {}
57
+ @fg = @bg = 0
58
+
59
+ begin
60
+ 16.times do |i|
61
+ cell 0, i, i
62
+ end
63
+
64
+ 6.times do |i|
65
+ 6.times do |j|
66
+ 6.times do |k|
67
+ c = 16 + 6*6*i + 6*j + k
68
+ y = 2 + j
69
+ x = 7*i + k
70
+ cell y, x, c
71
+ end
72
+ end
73
+ end
74
+
75
+ 16.times do |i|
76
+ c = 16 + 6*6*6 + i
77
+ cell 9, i, c
78
+ end
79
+
80
+ handle_click 0, 0
81
+ Ncurses.refresh
82
+
83
+ while (c = Ncurses.getch)
84
+ case c
85
+ when 113 #q
86
+ break
87
+ when Ncurses::KEY_MOUSE
88
+ mev = Ncurses::MEVENT.new
89
+ Ncurses.getmouse(mev)
90
+ case(mev.bstate)
91
+ when Ncurses::BUTTON1_CLICKED
92
+ handle_click mev.y, mev.x
93
+ end
94
+ end
95
+ Ncurses.refresh
96
+ end
97
+
98
+ ensure
99
+ Ncurses.endwin
100
+ end
@@ -0,0 +1,114 @@
1
+ #compdef sup sup-add sup-config sup-dump sup-sync sup-tweak-labels sup-recover-sources
2
+ # vim: set et sw=2 sts=2 ts=2 ft=zsh :
3
+
4
+ # TODO: sources completion: maildir://some/dir, mbox://some/file, ...
5
+ # for sup-add, sup-sync, sup-tweak-labels
6
+
7
+ (( ${+functions[_sup_cmd]} )) ||
8
+ _sup_cmd()
9
+ {
10
+ _arguments -s : \
11
+ "(--list-hooks -l)"{--list-hooks,-l}"[list all hooks and descriptions, and quit]" \
12
+ "(--no-threads -n)"{--no-threads,-n}"[turn off threading]" \
13
+ "(--no-initial-poll -o)"{--no-initial-poll,-o}"[Don't poll for new messages when starting]" \
14
+ "(--search -s)"{--search,-s}"[search for this query upon startup]:Query: " \
15
+ "(--compose -c)"{--compose,-c}"[compose message to this recipient upon startup]:Email: " \
16
+ "--version[show version information]" \
17
+ "(--help -h)"{--help,-h}"[show help]"
18
+ }
19
+
20
+ (( ${+functions[_sup_add_cmd]} )) ||
21
+ _sup_add_cmd()
22
+ {
23
+ _arguments -s : \
24
+ "(--archive -a)"{--archive,-a}"[automatically archive all new messages from this source]" \
25
+ "(--unusual -u)"{--unusual,-u}"[do not automatically poll for new messages from this source]" \
26
+ "(--labels -l)"{--labels,-l}"[set of labels to apply to all messages from this source]:Labels: " \
27
+ "(--force-new -f)"{--force-new,-f}"[create a new account for this source, even if one already exists]" \
28
+ "--version[show version information]" \
29
+ "(--help -h)"{--help,-h}"[show help]"
30
+ }
31
+
32
+ (( ${+functions[_sup_config_cmd]} )) ||
33
+ _sup_config_cmd()
34
+ {
35
+ _arguments -s : \
36
+ "--version[show version information]" \
37
+ "(--help -h)"{--help,-h}"[show help]"
38
+ }
39
+
40
+ (( ${+functions[_sup_dump_cmd]} )) ||
41
+ _sup_dump_cmd()
42
+ {
43
+ _arguments -s : \
44
+ "--version[show version information]" \
45
+ "(--help -h)"{--help,-h}"[show help]"
46
+ }
47
+
48
+ (( ${+functions[_sup_recover_sources_cmd]} )) ||
49
+ _sup_recover_sources_cmd()
50
+ {
51
+ _arguments -s : \
52
+ "--archive[automatically archive all new messages from this source]" \
53
+ "--scan-num[number of messages to scan per source]:" \
54
+ "--unusual[do not automatically poll for new messages from this source]" \
55
+ "(--help -h)"{--help,-h}"[show help]"
56
+ }
57
+
58
+ (( ${+functions[_sup_sync_cmd]} )) ||
59
+ _sup_sync_cmd()
60
+ {
61
+ # XXX Add only when --restore is given: (--restored -r)
62
+ # Add only when --changed or--all are given: (--start-at -s)
63
+ _arguments -s : \
64
+ "--new[operate on new messages only]" \
65
+ "(--changed -c)"{--changed,-c}"[scan over the entire source for messages that have been deleted, altered, or moved from another source]" \
66
+ "(--restored -r)"{--restored,-r}"[operate only on those messages included in a dump file as specified by --restore which have changed state]" \
67
+ "(--all -a)"{--all,-a}"[operate on all messages in the source, regardless of newness or changedness]" \
68
+ "(--start-at -s)"{--start-at,-s}"[start at a particular offset]:Offset: " \
69
+ "--asis[if the message is already in the index, preserve its state, otherwise, use default source state]" \
70
+ "--restore[restore message state from a dump file created with sup-dump]:File:_file" \
71
+ "--discard[discard any message state in the index and use the default source state]" \
72
+ "(--archive -x)"{--archive,-x}"[mark messages as archived when using the default source state]" \
73
+ "(--read -e)"{--read,-e}"[mark messages as read when using the default source state]" \
74
+ "--extra-labels[apply these labels when using the default source state]:Labels: " \
75
+ "(--verbose -v)"{--verbose,-v}"[print message ids as they're processed]" \
76
+ "(--optimize -o)"{--optimize,-o}"[as the final operation, optimize the index]" \
77
+ "--all-sources[scan over all sources]" \
78
+ "(--dry-run -n)"{--dry-run,-n}"[don't actually modify the index]" \
79
+ "--version[show version information]" \
80
+ "(--help -h)"{--help,-h}"[show help]"
81
+ }
82
+
83
+ (( ${+functions[_sup_sync_back_cmd]} )) ||
84
+ _sup_sync_back_cmd()
85
+ {
86
+ _arguments -s : \
87
+ "(--drop-deleted -d)"{--drop-deleted,-d}"[drop deleted messages]" \
88
+ "--move-deleted[move deleted messages to a local mbox file]:File:_file" \
89
+ "(--drop-spam -s)"{--drop-spam,-s}"[drop spam messages]" \
90
+ "--move-spam[move spam messages to a local mbox file]:File:_file" \
91
+ "--with-dotlockfile[specific dotlockfile location (mbox files only)]:File:_file" \
92
+ "--dont-use-dotlockfile[don't use dotlockfile to lock mbox files]" \
93
+ "(--verbose -v)"{--verbose,-v}"[print message ids as they're processed]" \
94
+ "(--dry-run -n)"{--dry-run,-n}"[don't actually modify the index]" \
95
+ "--version[show version information]" \
96
+ "(--help -h)"{--help,-h}"[show help]"
97
+ }
98
+
99
+ (( ${+functions[_sup_tweak_labels_cmd]} )) ||
100
+ _sup_tweak_labels_cmd()
101
+ {
102
+ _arguments -s : \
103
+ "(--add -a)"{--add,-a}"[which labels to add to every message from the specified sources]:Labels: " \
104
+ "(--remove -r)"{--remove,-r}"[which labels to remove from every message from the specified sources]:Labels: " \
105
+ "--all-sources[scan over all sources]" \
106
+ "(--verbose -v)"{--verbose,-v}"[print message ids as they're processed]" \
107
+ "(--dry-run -n)"{--dry-run,-n}"[don't actually modify the index]" \
108
+ "--version[show version information]" \
109
+ "(--help -h)"{--help,-h}"[show help]"
110
+ }
111
+
112
+ _call_function ret _${words[1]//-/_}_cmd
113
+ return ret
114
+
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ irb -I lib -r ./devel/start-console.rb
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ egrep ".rb$" Manifest.txt | xargs cat | grep -v "^ *$"|grep -v "^ *#"|grep -v "^ *end *$"|wc -l
@@ -0,0 +1,9 @@
1
+ require 'sup'
2
+
3
+ puts "loading index..."
4
+ @index = Redwood::Index.new
5
+ @index.load
6
+ @i = @index.index
7
+ puts "loaded index of #{@i.size} messages"
8
+
9
+
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'ruby-prof'
3
+ require "redwood"
4
+
5
+ result = RubyProf.profile do
6
+ Redwood::ThreadSet.new(ARGV.map { |fn| Redwood::MBox::Scanner.new fn }).load_n_threads 100
7
+ end
8
+
9
+ printer = RubyProf::GraphHtmlPrinter.new(result)
10
+ File.open("profile.html", "w") { |f| printer.print(f, 1) }
11
+ puts "report in profile.html"
12
+
@@ -0,0 +1,5 @@
1
+ require 'sup'
2
+ include Redwood
3
+ start
4
+ Index.init
5
+ Index.load
@@ -0,0 +1,119 @@
1
+ Sup FAQ
2
+ -------
3
+
4
+ Q: What is Sup?
5
+ A: A console-based email client for people with a lot of email.
6
+
7
+ Q: What does Sup stand for?
8
+ A: "What's up?"
9
+
10
+ Q: Sup looks like a text-based Gmail.
11
+ A: First I stole their ideas. Then I improved them.
12
+
13
+ Q: Why not just use Gmail?
14
+ A: I hate ads, I hate using a mouse, and I hate non-programmability and
15
+ non-extensibility.
16
+
17
+ Also, Gmail doesn't let you use a monospace font, which is just
18
+ lame.
19
+
20
+ Also, Gmail encourages top-posting. THIS CANNOT BE TOLERATED!
21
+
22
+ Q: Why the console?
23
+ A: Because a keystroke is worth a hundred mouse clicks, as any Unix
24
+ user knows. Because you don't need a web browser. Because you get
25
+ an instantaneous response and a simple interface.
26
+
27
+ Q: How does Sup deal with spam?
28
+ A: You can manually mark messages as spam, which prevents them from
29
+ showing up in future searches. Later, you can run a batch process to
30
+ remove such messages from your sources. That's as far as Sup goes.
31
+ Spam filtering should be done by a dedicated tool like SpamAssassin.
32
+
33
+ Q: How do I delete a message?
34
+ A: Why delete? Unless it's spam, you might as well just archive it.
35
+
36
+ Q: C'mon, really now!
37
+ A: Ok, press the 'd' key.
38
+
39
+ Q: But I want to delete it for real, not just add a 'deleted' flag in
40
+ the index. I want it gone from disk!
41
+ A: Currently, for mbox sources, there is a batch deletion tool that
42
+ will strip out all messages marked as spam or deleted.
43
+
44
+ Q: How well does Sup play with other mail clients?
45
+ A: Not well at all. If messages have been moved, deleted, or altered
46
+ due to some other client, Sup will have to rebuild its index for
47
+ that message source. For example, for mbox files, reading a single
48
+ unread message changes the offsets of every file on disk. Rather
49
+ than rescanning every time, Sup assumes sources don't change except
50
+ by having new messages added. If that assumption is violated,
51
+ you'll have to sync the index.
52
+
53
+ Q: How do I back up my index?
54
+ A: Since the contents of the messages are recoverable from their
55
+ sources using sup-sync, all you need to back up is the message
56
+ state. To do this, simply run:
57
+ sup-dump > <dumpfile>
58
+ This will save all message state in a big text file, which you
59
+ should probably compress.
60
+
61
+ Q: How do I restore the message state I saved in my state dump?
62
+ A: Run:
63
+ sup-sync [<source>+] --restored --restore <dumpfile>
64
+ where <dumpfile> was created as above.
65
+
66
+ Q: Xapian crashed and I can't read my index. Luckily I made a state
67
+ dump. What should I do?
68
+ Q: How do I rebuild the index completely?
69
+ A: Run:
70
+ rm -rf ~/.sup/xapian # omg wtf
71
+ sup-sync --all-sources --all --restore <dumpfile>
72
+ Voila! A brand new index.
73
+
74
+ Q: I want to move messages from one source to another. (E.g., my
75
+ primary inbox is an mbox file, and I want to move some of those
76
+ messages to a Maildir.) How do I do that while preserving message
77
+ state?
78
+ A: Move the messages from the source to the target using whatever tool
79
+ you'd like. Mutt's a good one. :) Then run:
80
+ sup-sync --changed <source1> <source2>
81
+
82
+ Note that if you sup-sync only one source at a time, depending on
83
+ the order in which you do it, the messages may be treated as
84
+ missing and then deleted from the index, which means that their
85
+ states will be lost when you sync the other source. So do them both
86
+ in one go.
87
+
88
+ Q: What are all these "Redwood" references I see in the code?
89
+ A: That was Sup's original name. (Think pine, elm. Although I was a
90
+ Mutt user, I couldn't think of a good progression there.) But it was
91
+ taken by another project on RubyForge, and wasn't that original, and
92
+ was too long to type anyways.
93
+
94
+ Common Problems
95
+ ---------------
96
+
97
+ P: I get some error message from Rubymail about frozen strings when
98
+ importing messages with attachments.
99
+ S: The current solution is to directly modify RubyMail. Change line 159 of
100
+ multipart.rb to:
101
+ chunk = chunk[0..start]
102
+ This is because RubyMail hasn't been updated since like Ruby 1.8.2.
103
+ Please bug Matt Armstrong.
104
+
105
+ P: I see this error:
106
+ /usr/local/lib/ruby/1.8/yaml.rb:133:in `transfer': allocator undefined for Bignum (TypeError)
107
+ S: You need to upgrade to Ruby 1.8.5. YAML in earlier versions can't
108
+ parse BigNums, but Sup relies on that for Maildir.
109
+
110
+ P: When I run Sup remotely and view an HTML attachment, an existing
111
+ Firefox on the *local* machine is redirected to the attachment
112
+ file, which it can't find (since it's on the remote machine). How do
113
+ I view HTML attachments in this environment?
114
+ S: Put this in your ~/.mailcap on the machine you run Sup on:
115
+ text/html; /usr/bin/firefox -a sup %s; description=HTML Text; test=test -n "$DISPLAY"; nametemplate=%s.html
116
+
117
+ Please read
118
+ https://github.com/sup-heliotrope/sup/wiki/Viewing-Attachments for
119
+ some security concerns on opening attachments.
@@ -0,0 +1,79 @@
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. The values of variables persists across calls to the
16
+ same hook, but is NOT available to other hooks. To make the value of a
17
+ variable available to other hooks, use the get and set methods. Each
18
+ hook description lists the variables and return value expected, if
19
+ any.
20
+
21
+ The following special functions are available to hooks:
22
+ * say msg
23
+ Displays the string msg to the user at the bottom of the screen.
24
+ * log msg
25
+ Adds the string msg to the log, which the user can access via the
26
+ buffer list.
27
+ * ask_yes_or_no question
28
+ Prompts the user with the string question for a yes or no
29
+ response. Returns true if the user answered yes, false otherwise.
30
+ * get key
31
+ Gets the cross-hook value associated with key (which is typically a
32
+ string). If there is no value for a given key, nil is returned.
33
+ * set key value
34
+ Sets the cross-hook value associated with key to value. key is
35
+ typically a string, while value can be whatever type it needs to be,
36
+ including nil.
37
+
38
+ Some example hooks:
39
+
40
+ before-poll:
41
+ ## runs fetchmail before polling
42
+ if (@last_fetchmail_time || Time.now) < Time.now - 60
43
+ say "Running fetchmail..."
44
+ system "fetchmail >& /dev/null"
45
+ say "Done running fetchmail."
46
+ end
47
+ @last_fetchmail_time = Time.now
48
+
49
+
50
+ mime-decode:
51
+ ## Please read:
52
+ https://github.com/sup-heliotrope/sup/wiki/Viewing-Attachments for
53
+ some security concerns on opening attachments.
54
+
55
+ ## turn text/html attachments into plain text, unless they are part
56
+ ## of a multipart/alternative pair
57
+ require 'shellwords'
58
+ unless sibling_types.member? "text/plain"
59
+ case content_type
60
+ when "text/html"
61
+ `/usr/bin/w3m -dump -T #{content_type} #{Shellwords.escape filename}`
62
+ end
63
+ end
64
+
65
+ startup:
66
+ ## runs a background task
67
+ @bgtask_pid = fork
68
+ if @bgtask_pid
69
+ set 'bgtask_pid' @bgtask_pid
70
+ Process.detach(@bgtask_pid) # so we don't have to wait on it when we go to kill it
71
+ else
72
+ exec "background-task args 2&>1 >> /tmp/logfile"
73
+ end
74
+
75
+ after-poll:
76
+ ## kills the background task after the first poll
77
+ @bgtask_pid = get 'bgtask_pid'
78
+ Process.kill("TERM", @bgtask_pid) unless @bgtask_pid == nil
79
+ set 'bgtask_pid' nil