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,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