sup 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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