14 ## this is for debugging purposes because i keep calling #id on the
15 ## wrong object and i want it to throw an exception
17 raise "wrong id called on #{self.inspect}"
22 def yaml_properties *props
23 props = props.map { |p| p.to_s }
24 vars = props.map { |p| "@#{p}" }
26 path = klass.name.gsub(/::/, "/")
28 klass.instance_eval do
29 define_method(:to_yaml_properties) { vars }
30 define_method(:to_yaml_type) { "!#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}/#{path}" }
33 YAML.add_domain_type("#{Redwood::YAML_DOMAIN},#{Redwood::YAML_DATE}", path) do |type, val|
34 klass.new(*props.map { |p| val[p] })
42 BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
43 CONFIG_FN = File.join(BASE_DIR, "config.yaml")
44 COLOR_FN = File.join(BASE_DIR, "colors.yaml")
45 SOURCE_FN = File.join(BASE_DIR, "sources.yaml")
46 LABEL_FN = File.join(BASE_DIR, "labels.txt")
47 CONTACT_FN = File.join(BASE_DIR, "contacts.txt")
48 DRAFT_DIR = File.join(BASE_DIR, "drafts")
49 SENT_FN = File.join(BASE_DIR, "sent.mbox")
50 LOCK_FN = File.join(BASE_DIR, "lock")
51 SUICIDE_FN = File.join(BASE_DIR, "please-kill-yourself")
52 HOOK_DIR = File.join(BASE_DIR, "hooks")
54 YAML_DOMAIN = "masanjin.net"
55 YAML_DATE = "2006-10-01"
57 DEFAULT_INDEX = 'ferret'
59 ## record exceptions thrown in threads nicely
61 @exception_mutex = Mutex.new
63 attr_reader :exceptions
64 def record_exception e, name
65 @exception_mutex.synchronize do
67 @exceptions << [e, name]
71 def reporting_thread name
79 record_exception e, name
85 module_function :reporting_thread, :record_exception, :exceptions
87 ## one-stop shop for yamliciousness
88 def save_yaml_obj object, fn, safe=false
90 safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
91 mode = File.stat(fn).mode if File.exists? fn
92 File.open(safe_fn, "w", mode) { |f| f.puts object.to_yaml }
93 FileUtils.mv safe_fn, fn
95 File.open(fn, "w") { |f| f.puts object.to_yaml }
99 def load_yaml_obj fn, compress=false
102 Zlib::GzipReader.open(fn) { |f| YAML::load f }
110 Redwood::SentManager.init $config[:sent_source] || 'sup://sent'
111 Redwood::ContactManager.init Redwood::CONTACT_FN
112 Redwood::LabelManager.init Redwood::LABEL_FN
113 Redwood::AccountManager.init $config[:accounts]
114 Redwood::DraftManager.init Redwood::DRAFT_DIR
115 Redwood::UpdateManager.init
116 Redwood::PollManager.init
117 Redwood::CryptoManager.init
118 Redwood::UndoManager.init
119 Redwood::SourceManager.init
123 Redwood::LabelManager.save if Redwood::LabelManager.instantiated?
124 Redwood::ContactManager.save if Redwood::ContactManager.instantiated?
125 Redwood::BufferManager.deinstantiate! if Redwood::BufferManager.instantiated?
128 ## not really a good place for this, so I'll just dump it here.
130 ## a source error is either a FatalSourceError or an OutOfSyncSourceError.
131 ## the superclass SourceError is just a generic.
132 def report_broken_sources opts={}
133 return unless BufferManager.instantiated?
135 broken_sources = SourceManager.sources.select { |s| s.error.is_a? FatalSourceError }
136 unless broken_sources.empty?
137 BufferManager.spawn_unless_exists("Broken source notification for #{broken_sources.join(',')}", opts) do
139 Source error notification
140 -------------------------
142 Hi there. It looks like one or more message sources is reporting
143 errors. Until this is corrected, messages from these sources cannot
144 be viewed, and new messages will not be detected.
146 #{broken_sources.map { |s| "Source: " + s.to_s + "\n Error: " + s.error.message.wrap(70).join("\n ")}.join("\n\n")}
152 desynced_sources = SourceManager.sources.select { |s| s.error.is_a? OutOfSyncSourceError }
153 unless desynced_sources.empty?
154 BufferManager.spawn_unless_exists("Out-of-sync source notification for #{broken_sources.join(',')}", opts) do
156 Out-of-sync source notification
157 -------------------------------
159 Hi there. It looks like one or more sources has fallen out of sync
160 with my index. This can happen when you modify these sources with
161 other email clients. (Sorry, I don't play well with others.)
163 Until this is corrected, messages from these sources cannot be viewed,
164 and new messages will not be detected. Luckily, this is easy to correct!
166 #{desynced_sources.map do |s|
167 "Source: " + s.to_s +
168 "\n Error: " + s.error.message.wrap(70).join("\n ") +
169 "\n Fix: sup-sync --changed #{s.to_s}"
177 module_function :save_yaml_obj, :load_yaml_obj, :start, :finish,
178 :report_broken_sources
181 ## set up default configuration file
182 if File.exists? Redwood::CONFIG_FN
183 $config = Redwood::load_yaml_obj Redwood::CONFIG_FN
187 name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first rescue nil
189 email = ENV["USER"] + "@" +
191 Socket.gethostbyname(Socket.gethostname).first
202 :sendmail => "/usr/sbin/sendmail -oem -ti",
203 :signature => File.join(ENV["HOME"], ".signature")
206 :editor => ENV["EDITOR"] || "/usr/bin/vim -f -c 'setlocal spell spelllang=en_us' -c 'set filetype=mail'",
207 :thread_by_subject => false,
208 :edit_signature => false,
210 :ask_for_bcc => false,
211 :ask_for_subject => true,
212 :confirm_no_attachments => true,
213 :confirm_top_posting => true,
214 :discard_snippets_from_encrypted_messages => false,
215 :default_attachment_save_dir => "",
216 :sent_source => "sup://sent"
219 FileUtils.mkdir_p Redwood::BASE_DIR
220 Redwood::save_yaml_obj $config, Redwood::CONFIG_FN
221 rescue StandardError => e
222 $stderr.puts "warning: #{e.message}"
229 ## we have to initialize this guy first, because other classes must
230 ## reference it in order to register hooks, and they do that at parse
232 Redwood::HookManager.init Redwood::HOOK_DIR
234 ## everything we need to get logging working
236 Redwood::Logger.init.add_sink $stderr
237 include Redwood::LogsStuff
239 ## determine encoding and character set
240 $encoding = Locale.current.charset
242 debug "using character set encoding #{$encoding.inspect}"
244 warn "can't find character set by using locale, defaulting to utf-8"
251 require "sup/modes/scroll-mode"
252 require "sup/modes/text-mode"
253 require "sup/modes/log-mode"
255 require "sup/message-chunks"
256 require "sup/message"
259 require "sup/maildir"
262 require "sup/account"
264 require "sup/interactive-lock"
266 require "sup/textfield"
267 require "sup/colormap"
269 require "sup/contact"
275 require "sup/horizontal-selector"
276 require "sup/modes/line-cursor-mode"
277 require "sup/modes/help-mode"
278 require "sup/modes/edit-message-mode"
279 require "sup/modes/compose-mode"
280 require "sup/modes/resume-mode"
281 require "sup/modes/forward-mode"
282 require "sup/modes/reply-mode"
283 require "sup/modes/label-list-mode"
284 require "sup/modes/contact-list-mode"
285 require "sup/modes/thread-view-mode"
286 require "sup/modes/thread-index-mode"
287 require "sup/modes/label-search-results-mode"
288 require "sup/modes/search-results-mode"
289 require "sup/modes/person-search-results-mode"
290 require "sup/modes/inbox-mode"
291 require "sup/modes/buffer-list-mode"
292 require "sup/modes/poll-mode"
293 require "sup/modes/file-browser-mode"
294 require "sup/modes/completion-mode"
295 require "sup/modes/console-mode"
299 d = File.join base, "sup/share/modes/"
300 Redwood::Mode.load_all_modes d if File.directory? d