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,69 @@
1
+ Should an email client have a philosophy? For many people, email is
2
+ one of our primary means of communication, and email archives are an
3
+ integral part of our long-term memory. Something so important ought to
4
+ warrant a little thought.
5
+
6
+ Here's Sup's philosophy.
7
+
8
+ Using "traditional" email clients today is increasingly problematic.
9
+ Anyone who's on a high-traffic mailing list knows this. My ruby-talk
10
+ folder is 430 megs and Mutt sits there for 60 seconds while it opens
11
+ it. Keeping up with the all the new traffic is impossible, even with
12
+ Mutt's excellent threading features, simply because there's so much of
13
+ it. A single thread can span several pages in the folder index view
14
+ alone! And Mutt is probably the fastest, most mailing-list aware email
15
+ client out there. God help me if I try and use Thunderbird.
16
+
17
+ The problem with traditional clients like Mutt is that they deal with
18
+ individual pieces of email. This places a high mental cost on the user
19
+ for each incoming email, by forcing them to ask: Should I keep this
20
+ email, or delete it? If I keep it, where should I file it? I've spent
21
+ the last 10 years of my life laboriously hand-filing every email
22
+ message I received and feeling a mild sense of panic every time an
23
+ email was both "from Mom" and "about school". The massive amounts of
24
+ email that many people receive, and the cheap cost of storage, have
25
+ made these questions both more costly and less useful to answer.
26
+
27
+ Contrast that with using Gmail. As a long-time Mutt user, I was blown
28
+ away when I first saw someone use Gmail. They treated their email
29
+ differently from how I ever had. They never filed email and they never
30
+ deleted it. They relied on an immediate, global, full-text search, and
31
+ thread-level tagging, to do everything I'd ever done with Mutt, but
32
+ with a trivial cost to the user at message receipt time.
33
+
34
+ From Gmail I learned that making certain operations quantitatively
35
+ easier (namely, search) resulted in a qualitative improvement in
36
+ usage. I also learned how thread-centrism was advantageous over
37
+ message-centrism when message volume was high: most of the time, a
38
+ message and its context deserve the same treatment. I think it's to
39
+ the Gmail designers' credit that they started with a somewhat ad-hoc
40
+ idea (hey, we're really good at search engines, so maybe we can build
41
+ an email client on top of one) and managed to build something that was
42
+ actually better than everything else out there. At least, that's how I
43
+ imagine in happened. Maybe they knew what they were doing from the
44
+ start.
45
+
46
+ Unfortunately, there's a lot to Gmail I can't tolerate (top posting,
47
+ HTML mail, one-level threads, and ads come to mind, never mind the
48
+ fact that it's not FOSS). Thus Sup was born.
49
+
50
+ Sup is based on the following principles, which I stole directly from
51
+ Gmail:
52
+
53
+ - An immediately accessible and fast search capability over the entire
54
+ email archive eliminates most of the need for folders, and most of
55
+ the necessity of deleting email.
56
+
57
+ - Labels eliminate what little need for folders search doesn't cover.
58
+
59
+ - A thread-centric approach to the UI is much more in line with how
60
+ people operate than dealing with individual messages is. In the vast
61
+ majority of cases, a message and its context should be subject to
62
+ the same treatment.
63
+
64
+ Sup is also based on many ideas from mutt and Emacs and vi, having to
65
+ do with the fantastic productivity of a console- and keyboard-based
66
+ application, the usefulness of multiple buffers, the necessity of
67
+ handling multiple email accounts, etc. But those are just details!
68
+
69
+ Try it and let me know what you think.
@@ -0,0 +1,467 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'zlib'
6
+ require 'thread'
7
+ require 'fileutils'
8
+ require 'locale'
9
+ require 'ncursesw'
10
+ require 'rmail'
11
+ begin
12
+ require 'fastthread'
13
+ rescue LoadError
14
+ end
15
+
16
+ class Object
17
+ ## this is for debugging purposes because i keep calling #id on the
18
+ ## wrong object and i want it to throw an exception
19
+ def id
20
+ raise "wrong id called on #{self.inspect}"
21
+ end
22
+ end
23
+
24
+ class Module
25
+ def yaml_properties *props
26
+ props = props.map { |p| p.to_s }
27
+
28
+ path = name.gsub(/::/, "/")
29
+ yaml_tag "!#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}"
30
+
31
+ define_method :init_with do |coder|
32
+ initialize(*coder.map.values_at(*props))
33
+ end
34
+
35
+ define_method :encode_with do |coder|
36
+ coder.map = props.inject({}) do |hash, key|
37
+ hash[key] = instance_variable_get("@#{key}")
38
+ hash
39
+ end
40
+ end
41
+
42
+ # Legacy
43
+ Psych.load_tags["!#{Redwood::LEGACY_YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}"] = self
44
+ end
45
+ end
46
+
47
+ module Redwood
48
+ BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
49
+ CONFIG_FN = File.join(BASE_DIR, "config.yaml")
50
+ COLOR_FN = File.join(BASE_DIR, "colors.yaml")
51
+ SOURCE_FN = File.join(BASE_DIR, "sources.yaml")
52
+ LABEL_FN = File.join(BASE_DIR, "labels.txt")
53
+ CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
54
+ DRAFT_DIR = File.join(BASE_DIR, "drafts")
55
+ SENT_FN = File.join(BASE_DIR, "sent.mbox")
56
+ LOCK_FN = File.join(BASE_DIR, "lock")
57
+ SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
58
+ HOOK_DIR = File.join(BASE_DIR, "hooks")
59
+ SEARCH_FN = File.join(BASE_DIR, "searches.txt")
60
+ LOG_FN = File.join(BASE_DIR, "log")
61
+ SYNC_OK_FN = File.join(BASE_DIR, "sync-back-ok")
62
+
63
+ YAML_DOMAIN = "supmua.org"
64
+ LEGACY_YAML_DOMAIN = "masanjin.net"
65
+ YAML_DATE = "2006-10-01"
66
+ MAILDIR_SYNC_CHECK_SKIPPED = 'SKIPPED'
67
+
68
+ ## record exceptions thrown in threads nicely
69
+ @exceptions = []
70
+ @exception_mutex = Mutex.new
71
+
72
+ attr_reader :exceptions
73
+ def record_exception e, name
74
+ @exception_mutex.synchronize do
75
+ @exceptions ||= []
76
+ @exceptions << [e, name]
77
+ end
78
+ end
79
+
80
+ def reporting_thread name
81
+ if $opts[:no_threads]
82
+ yield
83
+ else
84
+ ::Thread.new do
85
+ begin
86
+ yield
87
+ rescue Exception => e
88
+ record_exception e, name
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ module_function :reporting_thread, :record_exception, :exceptions
95
+
96
+ ## one-stop shop for yamliciousness
97
+ def save_yaml_obj o, fn, safe=false, backup=false
98
+ o = if o.is_a?(Array)
99
+ o.map { |x| (x.respond_to?(:before_marshal) && x.before_marshal) || x }
100
+ elsif o.respond_to? :before_marshal
101
+ o.before_marshal
102
+ else
103
+ o
104
+ end
105
+
106
+ mode = if File.exists? fn
107
+ File.stat(fn).mode
108
+ else
109
+ 0600
110
+ end
111
+
112
+ if backup
113
+ backup_fn = fn + '.bak'
114
+ if File.exists?(fn) && File.size(fn) > 0
115
+ File.open(backup_fn, "w", mode) do |f|
116
+ File.open(fn, "r") { |old_f| FileUtils.copy_stream old_f, f }
117
+ f.fsync
118
+ end
119
+ end
120
+ File.open(fn, "w") do |f|
121
+ f.puts o.to_yaml
122
+ f.fsync
123
+ end
124
+ elsif safe
125
+ safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
126
+ File.open(safe_fn, "w", mode) do |f|
127
+ f.puts o.to_yaml
128
+ f.fsync
129
+ end
130
+ FileUtils.mv safe_fn, fn
131
+ else
132
+ File.open(fn, "w", mode) do |f|
133
+ f.puts o.to_yaml
134
+ f.fsync
135
+ end
136
+ end
137
+ end
138
+
139
+ def load_yaml_obj fn, compress=false
140
+ o = if File.exists? fn
141
+ if compress
142
+ Zlib::GzipReader.open(fn) { |f| YAML::load f }
143
+ else
144
+ YAML::load_file fn
145
+ end
146
+ end
147
+ if o.is_a?(Array)
148
+ o.each { |x| x.after_unmarshal! if x.respond_to?(:after_unmarshal!) }
149
+ else
150
+ o.after_unmarshal! if o.respond_to?(:after_unmarshal!)
151
+ end
152
+ o
153
+ end
154
+
155
+ def managers
156
+ %w(HookManager SentManager ContactManager LabelManager AccountManager
157
+ DraftManager UpdateManager PollManager CryptoManager UndoManager
158
+ SourceManager SearchManager IdleManager).map { |x| Redwood.const_get x.to_sym }
159
+ end
160
+
161
+ def start bypass_sync_check = false
162
+ managers.each { |x| fail "#{x} already instantiated" if x.instantiated? }
163
+
164
+ FileUtils.mkdir_p Redwood::BASE_DIR
165
+ $config = load_config Redwood::CONFIG_FN
166
+ @log_io = File.open(Redwood::LOG_FN, 'a')
167
+ Redwood::Logger.add_sink @log_io
168
+ Redwood::HookManager.init Redwood::HOOK_DIR
169
+ Redwood::SentManager.init $config[:sent_source] || 'sup://sent'
170
+ Redwood::ContactManager.init Redwood::CONTACT_FN
171
+ Redwood::LabelManager.init Redwood::LABEL_FN
172
+ Redwood::AccountManager.init $config[:accounts]
173
+ Redwood::DraftManager.init Redwood::DRAFT_DIR
174
+ Redwood::SearchManager.init Redwood::SEARCH_FN
175
+
176
+ managers.each { |x| x.init unless x.instantiated? }
177
+
178
+ return if bypass_sync_check
179
+
180
+ if $config[:sync_back_to_maildir]
181
+ if not File.exists? Redwood::SYNC_OK_FN
182
+ Redwood.warn_syncback <<EOS
183
+ It appears that the "sync_back_to_maildir" option has been changed
184
+ from false to true since the last execution of sup.
185
+ EOS
186
+ $stderr.puts <<EOS
187
+
188
+ Should I complain about this again? (Y/n)
189
+ EOS
190
+ File.open(Redwood::SYNC_OK_FN, 'w') {|f| f.write(Redwood::MAILDIR_SYNC_CHECK_SKIPPED) } if STDIN.gets.chomp.downcase == 'n'
191
+ end
192
+ elsif not $config[:sync_back_to_maildir] and File.exists? Redwood::SYNC_OK_FN
193
+ File.delete(Redwood::SYNC_OK_FN)
194
+ end
195
+ end
196
+
197
+ def check_syncback_settings
198
+ # don't check if syncback was never performed
199
+ return unless File.exists? Redwood::SYNC_OK_FN
200
+ active_sync_sources = File.readlines(Redwood::SYNC_OK_FN).collect { |e| e.strip }.find_all { |e| not e.empty? }
201
+ return if active_sync_sources.length == 1 and active_sync_sources[0] == Redwood::MAILDIR_SYNC_CHECK_SKIPPED
202
+ sources = SourceManager.sources
203
+ newly_synced = sources.select { |s| s.is_a? Maildir and s.sync_back_enabled? and not active_sync_sources.include? s.uri }
204
+ unless newly_synced.empty?
205
+
206
+ details =<<EOS
207
+ It appears that the option "sync_back" of the following source(s)
208
+ has been changed from false to true since the last execution of
209
+ sup:
210
+
211
+ EOS
212
+ newly_synced.each do |s|
213
+ details += "#{s} (usual: #{s.usual})\n"
214
+ end
215
+
216
+ Redwood.warn_syncback details
217
+ end
218
+ end
219
+
220
+ def self.warn_syncback details
221
+ $stderr.puts <<EOS
222
+ WARNING
223
+ -------
224
+
225
+ #{details}
226
+
227
+ It is *strongly* recommended that you run "sup-sync-back-maildir"
228
+ before continuing, otherwise you might lose changes you have made in sup
229
+ to your Xapian index.
230
+
231
+ This script should be run each time you change the
232
+ "sync_back_to_maildir" flag in config.yaml from false to true or
233
+ the "sync_back" flag is changed to true for a source in sources.yaml.
234
+
235
+ Please run "sup-sync-back-maildir -h" for more information and why this
236
+ is needed.
237
+
238
+ Note that if you have any sources that are not marked as 'ususal' in
239
+ sources.yaml you need to manually specify them when running the
240
+ sup-sync-back-maildir script.
241
+
242
+ Are you really sure you want to continue? (y/N)
243
+ EOS
244
+ abort "Aborted" unless STDIN.gets.chomp.downcase == 'y'
245
+ end
246
+
247
+ def finish
248
+ Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
249
+ Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
250
+ Redwood::SearchManager.save if Redwood::SearchManager.instantiated?
251
+ Redwood::Logger.remove_sink @log_io
252
+
253
+ managers.each { |x| x.deinstantiate! if x.instantiated? }
254
+
255
+ @log_io.close if @log_io
256
+ @log_io = nil
257
+ $config = nil
258
+ end
259
+
260
+ ## not really a good place for this, so I'll just dump it here.
261
+ ##
262
+ ## a source error is either a FatalSourceError or an OutOfSyncSourceError.
263
+ ## the superclass SourceError is just a generic.
264
+ def report_broken_sources opts={}
265
+ return unless BufferManager.instantiated?
266
+
267
+ broken_sources = SourceManager.sources.select { |s| s.error.is_a? FatalSourceError }
268
+ unless broken_sources.empty?
269
+ BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
270
+ TextMode.new(<<EOM)
271
+ Source error notification
272
+ -------------------------
273
+
274
+ Hi there. It looks like one or more message sources is reporting
275
+ errors. Until this is corrected, messages from these sources cannot
276
+ be viewed, and new messages will not be detected.
277
+
278
+ #{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n ")}.join("\n\n")}
279
+ EOM
280
+ #' stupid ruby-mode
281
+ end
282
+ end
283
+
284
+ desynced_sources = SourceManager.sources.select { |s| s.error.is_a? OutOfSyncSourceError }
285
+ unless desynced_sources.empty?
286
+ BufferManager.spawn_unless_exists("Out-of-sync source notification for #{broken_sources.join(',')}", opts) do
287
+ TextMode.new(<<EOM)
288
+ Out-of-sync source notification
289
+ -------------------------------
290
+
291
+ Hi there. It looks like one or more sources has fallen out of sync
292
+ with my index. This can happen when you modify these sources with
293
+ other email clients. (Sorry, I don't play well with others.)
294
+
295
+ Until this is corrected, messages from these sources cannot be viewed,
296
+ and new messages will not be detected. Luckily, this is easy to correct!
297
+
298
+ #{desynced_sources.map do |s|
299
+ "Source: " + s.to_s +
300
+ "\n Error: " + s.error.message.wrap(70).join("\n ") +
301
+ "\n Fix: sup-sync --changed #{s.to_s}"
302
+ end}
303
+ EOM
304
+ #' stupid ruby-mode
305
+ end
306
+ end
307
+ end
308
+
309
+
310
+ ## set up default configuration file
311
+ def load_config filename
312
+ default_config = {
313
+ :editor => ENV["EDITOR"] || "/usr/bin/vim -f -c 'setlocal spell spelllang=en_us' -c 'set filetype=mail'",
314
+ :thread_by_subject => false,
315
+ :edit_signature => false,
316
+ :ask_for_from => false,
317
+ :ask_for_to => true,
318
+ :ask_for_cc => true,
319
+ :ask_for_bcc => false,
320
+ :ask_for_subject => true,
321
+ :account_selector => true,
322
+ :confirm_no_attachments => true,
323
+ :confirm_top_posting => true,
324
+ :jump_to_open_message => true,
325
+ :discard_snippets_from_encrypted_messages => false,
326
+ :load_more_threads_when_scrolling => true,
327
+ :default_attachment_save_dir => "",
328
+ :sent_source => "sup://sent",
329
+ :archive_sent => true,
330
+ :poll_interval => 300,
331
+ :wrap_width => 0,
332
+ :slip_rows => 0,
333
+ :col_jump => 2,
334
+ :stem_language => "english",
335
+ :sync_back_to_maildir => false,
336
+ :continuous_scroll => false,
337
+ :always_edit_async => false,
338
+ }
339
+ if File.exists? filename
340
+ config = Redwood::load_yaml_obj filename
341
+ abort "#{filename} is not a valid configuration file (it's a #{config.class}, not a hash)" unless config.is_a?(Hash)
342
+ default_config.merge config
343
+ else
344
+ require 'etc'
345
+ require 'socket'
346
+ name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first.force_encoding($encoding).fix_encoding! rescue nil
347
+ name ||= ENV["USER"]
348
+ email = ENV["USER"] + "@" +
349
+ begin
350
+ Socket.gethostbyname(Socket.gethostname).first
351
+ rescue SocketError
352
+ Socket.gethostname
353
+ end
354
+
355
+ config = {
356
+ :accounts => {
357
+ :default => {
358
+ :name => name.dup.fix_encoding!,
359
+ :email => email.dup.fix_encoding!,
360
+ :alternates => [],
361
+ :sendmail => "/usr/sbin/sendmail -oem -ti",
362
+ :signature => File.join(ENV["HOME"], ".signature"),
363
+ :gpgkey => ""
364
+ }
365
+ },
366
+ }
367
+ config.merge! default_config
368
+ begin
369
+ Redwood::save_yaml_obj config, filename, false, true
370
+ rescue StandardError => e
371
+ $stderr.puts "warning: #{e.message}"
372
+ end
373
+ config
374
+ end
375
+ end
376
+
377
+ module_function :save_yaml_obj, :load_yaml_obj, :start, :finish,
378
+ :report_broken_sources, :load_config, :managers,
379
+ :check_syncback_settings
380
+ end
381
+
382
+ require 'sup/version'
383
+ require "sup/util"
384
+ require "sup/hook"
385
+ require "sup/time"
386
+
387
+ ## everything we need to get logging working
388
+ require "sup/logger/singleton"
389
+
390
+ ## determine encoding and character set
391
+ $encoding = Locale.current.charset
392
+ $encoding = "UTF-8" if $encoding == "utf8"
393
+ $encoding = "UTF-8" if $encoding == "UTF8"
394
+ if $encoding
395
+ debug "using character set encoding #{$encoding.inspect}"
396
+ else
397
+ warn "can't find character set by using locale, defaulting to utf-8"
398
+ $encoding = "UTF-8"
399
+ end
400
+
401
+ # test encoding
402
+ teststr = "test"
403
+ teststr.encode('UTF-8')
404
+ begin
405
+ teststr.encode($encoding)
406
+ rescue Encoding::ConverterNotFoundError
407
+ warn "locale encoding is invalid, defaulting to utf-8"
408
+ $encoding = "UTF-8"
409
+ end
410
+
411
+ require "sup/buffer"
412
+ require "sup/keymap"
413
+ require "sup/mode"
414
+ require "sup/modes/scroll_mode"
415
+ require "sup/modes/text_mode"
416
+ require "sup/modes/log_mode"
417
+ require "sup/update"
418
+ require "sup/message_chunks"
419
+ require "sup/message"
420
+ require "sup/source"
421
+ require "sup/mbox"
422
+ require "sup/maildir"
423
+ require "sup/person"
424
+ require "sup/account"
425
+ require "sup/thread"
426
+ require "sup/interactive_lock"
427
+ require "sup/index"
428
+ require "sup/textfield"
429
+ require "sup/colormap"
430
+ require "sup/label"
431
+ require "sup/contact"
432
+ require "sup/tagger"
433
+ require "sup/draft"
434
+ require "sup/poll"
435
+ require "sup/crypto"
436
+ require "sup/undo"
437
+ require "sup/horizontal_selector"
438
+ require "sup/modes/line_cursor_mode"
439
+ require "sup/modes/help_mode"
440
+ require "sup/modes/edit_message_mode"
441
+ require "sup/modes/edit_message_async_mode"
442
+ require "sup/modes/compose_mode"
443
+ require "sup/modes/resume_mode"
444
+ require "sup/modes/forward_mode"
445
+ require "sup/modes/reply_mode"
446
+ require "sup/modes/label_list_mode"
447
+ require "sup/modes/contact_list_mode"
448
+ require "sup/modes/thread_view_mode"
449
+ require "sup/modes/thread_index_mode"
450
+ require "sup/modes/label_search_results_mode"
451
+ require "sup/modes/search_results_mode"
452
+ require "sup/modes/person_search_results_mode"
453
+ require "sup/modes/inbox_mode"
454
+ require "sup/modes/buffer_list_mode"
455
+ require "sup/modes/poll_mode"
456
+ require "sup/modes/file_browser_mode"
457
+ require "sup/modes/completion_mode"
458
+ require "sup/modes/console_mode"
459
+ require "sup/sent"
460
+ require "sup/search"
461
+ require "sup/modes/search_list_mode"
462
+ require "sup/idle"
463
+
464
+ $:.each do |base|
465
+ d = File.join base, "sup/share/modes/"
466
+ Redwood::Mode.load_all_modes d if File.directory? d
467
+ end