Level is now set by SUP_LOG_LEVEL environment variable.
Big patch here, but most of it is replacing the old Redwood::log calls.
Some work on poll-mode, which subclasses log-mode. Remind me never to use
inheritance again.
27 files changed:
- Redwood::log "dynamically loading setlocale() from #{setlocale_lib}"
+ debug "dynamically loading setlocale() from #{setlocale_lib}"
begin
dlload setlocale_lib
extern "void setlocale(int, const char *)"
begin
dlload setlocale_lib
extern "void setlocale(int, const char *)"
- Redwood::log "setting locale..."
+ debug "setting locale..."
LibC.setlocale(6, "") # LC_ALL == 6
rescue RuntimeError => e
LibC.setlocale(6, "") # LC_ALL == 6
rescue RuntimeError => e
- Redwood::log "cannot dlload setlocale(); ncurses wide character support probably broken."
- Redwood::log "dlload error was #{e.class}: #{e.message}"
+ warn "cannot dlload setlocale(); ncurses wide character support probably broken."
+ warn "dlload error was #{e.class}: #{e.message}"
if Config::CONFIG['arch'] =~ /bsd/
if Config::CONFIG['arch'] =~ /bsd/
- Redwood::log "BSD variant detected. You may have to install a compat6x package to acquire libc."
+ warn "BSD variant detected. You may have to install a compat6x package to acquire libc."
if(s = Redwood::SourceManager.source_for DraftManager.source_name)
DraftManager.source = s
else
if(s = Redwood::SourceManager.source_for DraftManager.source_name)
DraftManager.source = s
else
- Redwood::log "no draft source, auto-adding..."
+ debug "no draft source, auto-adding..."
Redwood::SourceManager.add_source DraftManager.new_source
end
Redwood::SourceManager.add_source DraftManager.new_source
end
HookManager.run "startup"
HookManager.run "startup"
+ debug "starting curses"
+ Redwood::Logger.remove_sink $stderr
start_cursing
bm = BufferManager.init
Colormap.new.populate_colormap
start_cursing
bm = BufferManager.init
Colormap.new.populate_colormap
- log "initializing mail index buffer"
+ debug "initializing log buffer"
+ lmode = Redwood::LogMode.new "system log"
+ lmode.on_kill { Logger.clear! }
+ Logger.add_sink lmode
+ Logger.force_message "Welcome to Sup! Log level is set to #{Logger.level}."
+
+ debug "initializing inbox buffer"
imode = InboxMode.new
ibuf = bm.spawn "Inbox", imode
imode = InboxMode.new
ibuf = bm.spawn "Inbox", imode
- log "ready for interaction!"
- Logger.make_buf
+ debug "ready for interaction!"
begin
s.connect
rescue SourceError => e
begin
s.connect
rescue SourceError => e
- Redwood::log "fatal error loading from #{s}: #{e.message}"
+ error "fatal error loading from #{s}: #{e.message}"
end
end
end unless $opts[:no_initial_poll]
end
end
end unless $opts[:no_initial_poll]
Redwood::finish
stop_cursing
Redwood::finish
stop_cursing
- Redwood::log "stopped cursing"
+ Redwood::Logger.remove_all_sinks!
+ Redwood::Logger.add_sink $stderr, false
+ debug "stopped cursing"
if SuicideManager.instantiated? && SuicideManager.die?
if SuicideManager.instantiated? && SuicideManager.die?
- Redwood::log "I've been ordered to commit seppuku. I obey!"
+ info "I've been ordered to commit seppuku. I obey!"
end
if Redwood::exceptions.empty?
end
if Redwood::exceptions.empty?
- Redwood::log "no fatal errors. good job, william."
+ debug "no fatal errors. good job, william."
- Redwood::log "oh crap, an exception"
+ error "oh crap, an exception"
Redwood::HookManager.init Redwood::HOOK_DIR
## everything we need to get logging working
Redwood::HookManager.init Redwood::HOOK_DIR
## everything we need to get logging working
-require "sup/buffer"
-require "sup/keymap"
-require "sup/mode"
-require "sup/modes/scroll-mode"
-require "sup/modes/text-mode"
-require "sup/modes/log-mode"
-module Redwood
- def log s; Logger.log s; end
- module_function :log
-end
+Redwood::Logger.init.add_sink $stderr
+include Redwood::LogsStuff
## determine encoding and character set
$encoding = Locale.current.charset
if $encoding
## determine encoding and character set
$encoding = Locale.current.charset
if $encoding
- Redwood::log "using character set encoding #{$encoding.inspect}"
+ debug "using character set encoding #{$encoding.inspect}"
- Redwood::log "warning: can't find character set by using locale, defaulting to utf-8"
+ warn "can't find character set by using locale, defaulting to utf-8"
-## now everything else (which can feel free to call Redwood::log at load time)
+require "sup/buffer"
+require "sup/keymap"
+require "sup/mode"
+require "sup/modes/scroll-mode"
+require "sup/modes/text-mode"
+require "sup/modes/log-mode"
require "sup/update"
require "sup/suicide"
require "sup/message-chunks"
require "sup/update"
require "sup/suicide"
require "sup/message-chunks"
@next_id = (@next_id + 1) % NUM_COLORS
@next_id += 1 if @next_id == 0 # 0 is always white on black
id = @next_id
@next_id = (@next_id + 1) % NUM_COLORS
@next_id += 1 if @next_id == 0 # 0 is always white on black
id = @next_id
- Redwood::log "colormap: for color #{sym}, using id #{id} -> #{fg}, #{bg}"
+ debug "colormap: for color #{sym}, using id #{id} -> #{fg}, #{bg}"
Curses.init_pair id, fg, bg or raise ArgumentError,
"couldn't initialize curses color pair #{fg}, #{bg} (key #{id})"
Curses.init_pair id, fg, bg or raise ArgumentError,
"couldn't initialize curses color pair #{fg}, #{bg} (key #{id})"
## delete the old mapping, if it exists
if @users[cp]
@users[cp].each do |usym|
## delete the old mapping, if it exists
if @users[cp]
@users[cp].each do |usym|
- Redwood::log "dropping color #{usym} (#{id})"
+ warn "dropping color #{usym} (#{id})"
@entries[usym][3] = nil
end
@users[cp] = []
@entries[usym][3] = nil
end
@users[cp] = []
## to the default ones.
def populate_colormap
user_colors = if File.exists? Redwood::COLOR_FN
## to the default ones.
def populate_colormap
user_colors = if File.exists? Redwood::COLOR_FN
- Redwood::log "loading user colors from #{Redwood::COLOR_FN}"
+ debug "loading user colors from #{Redwood::COLOR_FN}"
Redwood::load_yaml_obj Redwood::COLOR_FN
end
Redwood::load_yaml_obj Redwood::COLOR_FN
end
fg = Curses.const_get "COLOR_#{ufg.upcase}"
rescue NameError
error ||= "Warning: there is no color named \"#{ufg}\", using fallback."
fg = Curses.const_get "COLOR_#{ufg.upcase}"
rescue NameError
error ||= "Warning: there is no color named \"#{ufg}\", using fallback."
- Redwood::log "Warning: there is no color named \"#{ufg}\""
+ warn "there is no color named \"#{ufg}\""
bg = Curses.const_get "COLOR_#{ubg.upcase}"
rescue NameError
error ||= "Warning: there is no color named \"#{ubg}\", using fallback."
bg = Curses.const_get "COLOR_#{ubg.upcase}"
rescue NameError
error ||= "Warning: there is no color named \"#{ubg}\", using fallback."
- Redwood::log "Warning: there is no color named \"#{ubg}\""
+ warn "there is no color named \"#{ubg}\""
Curses.const_get "A_#{a.upcase}"
rescue NameError
error ||= "Warning: there is no attribute named \"#{a}\", using fallback."
Curses.const_get "A_#{a.upcase}"
rescue NameError
error ||= "Warning: there is no attribute named \"#{a}\", using fallback."
- Redwood::log "Warning: there is no attribute named \"#{a}\", using fallback."
+ warn "there is no attribute named \"#{a}\", using fallback."
@cmd =
case bin
when /\S/
@cmd =
case bin
when /\S/
- Redwood::log "crypto: detected gpg binary in #{bin}"
+ debug "crypto: detected gpg binary in #{bin}"
"#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
else
"#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
else
- Redwood::log "crypto: no gpg binary detected"
+ debug "crypto: no gpg binary detected"
def run_gpg args
cmd = "#{@cmd} #{args} 2> /dev/null"
def run_gpg args
cmd = "#{@cmd} #{args} 2> /dev/null"
- #Redwood::log "crypto: running: #{cmd}"
- #Redwood::log "crypto: output: #{output.inspect}" unless $?.success?
def load_index dir=File.join(@dir, "ferret")
if File.exists? dir
def load_index dir=File.join(@dir, "ferret")
if File.exists? dir
- Redwood::log "loading index..."
+ debug "loading index..."
@index_mutex.synchronize do
@index = Ferret::Index::Index.new(:path => dir, :analyzer => @analyzer, :id_field => 'message_id')
@index_mutex.synchronize do
@index = Ferret::Index::Index.new(:path => dir, :analyzer => @analyzer, :id_field => 'message_id')
- Redwood::log "loaded index of #{@index.size} messages"
+ debug "loaded index of #{@index.size} messages"
- Redwood::log "creating index..."
+ debug "creating index..."
@index_mutex.synchronize do
field_infos = Ferret::Index::FieldInfos.new :store => :yes
field_infos.add_field :message_id, :index => :untokenized
@index_mutex.synchronize do
field_infos = Ferret::Index::FieldInfos.new :store => :yes
field_infos.add_field :message_id, :index => :untokenized
if entry[:source_id] && entry[:source_info] && entry[:label] &&
((entry[:source_id].to_i > source_id) || (entry[:source_info].to_i < m.source_info))
labels += entry[:label].to_set_of_symbols
if entry[:source_id] && entry[:source_info] && entry[:label] &&
((entry[:source_id].to_i > source_id) || (entry[:source_info].to_i < m.source_info))
labels += entry[:label].to_set_of_symbols
- #Redwood::log "found updated version of message #{m.id}: #{m.subj}"
- #Redwood::log "previous version was at #{entry[:source_id].inspect}:#{entry[:source_info].inspect}, this version at #{source_id.inspect}:#{m.source_info.inspect}"
- #Redwood::log "merged labels are #{labels.inspect} (index #{entry[:label].inspect}, message #{m.labels.inspect})"
+ #debug "found updated version of message #{m.id}: #{m.subj}"
+ #debug "previous version was at #{entry[:source_id].inspect}:#{entry[:source_info].inspect}, this version at #{source_id.inspect}:#{m.source_info.inspect}"
+ #debug "merged labels are #{labels.inspect} (index #{entry[:label].inspect}, message #{m.labels.inspect})"
while true
limit = (query[:limit])? [EACH_BY_DATE_NUM, query[:limit] - offset].min : EACH_BY_DATE_NUM
results = @index_mutex.synchronize { @index.search ferret_query, :sort => "date DESC", :limit => limit, :offset => offset }
while true
limit = (query[:limit])? [EACH_BY_DATE_NUM, query[:limit] - offset].min : EACH_BY_DATE_NUM
results = @index_mutex.synchronize { @index.search ferret_query, :sort => "date DESC", :limit => limit, :offset => offset }
- Redwood::log "got #{results.total_hits} results for query (offset #{offset}) #{ferret_query.inspect}"
+ debug "got #{results.total_hits} results for query (offset #{offset}) #{ferret_query.inspect}"
results.hits.each do |hit|
yield @index_mutex.synchronize { @index[hit.doc][:message_id] }, lambda { build_message hit.doc }
end
results.hits.each do |hit|
yield @index_mutex.synchronize { @index[hit.doc][:message_id] }, lambda { build_message hit.doc }
end
SAME_SUBJECT_DATE_LIMIT = 7
MAX_CLAUSES = 1000
def each_message_in_thread_for m, opts={}
SAME_SUBJECT_DATE_LIMIT = 7
MAX_CLAUSES = 1000
def each_message_in_thread_for m, opts={}
- #Redwood::log "Building thread for #{m.id}: #{m.subj}"
+ #debug "Building thread for #{m.id}: #{m.subj}"
messages = {}
searched = {}
num_queries = 0
messages = {}
searched = {}
num_queries = 0
q = build_ferret_query :qobj => q
p1 = @index_mutex.synchronize { @index.search(q).hits.map { |hit| @index[hit.doc][:message_id] } }
q = build_ferret_query :qobj => q
p1 = @index_mutex.synchronize { @index.search(q).hits.map { |hit| @index[hit.doc][:message_id] } }
- Redwood::log "found #{p1.size} results for subject query #{q}"
+ debug "found #{p1.size} results for subject query #{q}"
p2 = @index_mutex.synchronize { @index.search(q.to_s, :limit => :all).hits.map { |hit| @index[hit.doc][:message_id] } }
p2 = @index_mutex.synchronize { @index.search(q.to_s, :limit => :all).hits.map { |hit| @index[hit.doc][:message_id] } }
- Redwood::log "found #{p2.size} results in string form"
+ debug "found #{p2.size} results in string form"
pending = (pending + p1 + p2).uniq
end
pending = (pending + p1 + p2).uniq
end
end
mid = @index[docid][:message_id]
unless messages.member?(mid)
end
mid = @index[docid][:message_id]
unless messages.member?(mid)
- #Redwood::log "got #{mid} as a child of #{id}"
+ #debug "got #{mid} as a child of #{id}"
messages[mid] ||= lambda { build_message docid }
refs = @index[docid][:refs].split
pending += refs.select { |id| !searched[id] }
messages[mid] ||= lambda { build_message docid }
refs = @index[docid][:refs].split
pending += refs.select { |id| !searched[id] }
- #Redwood::log "thread for #{m.id} is killed, ignoring"
+ #debug "thread for #{m.id} is killed, ignoring"
- #Redwood::log "ran #{num_queries} queries to build thread of #{messages.size} messages for #{m.id}: #{m.subj}" if num_queries > 0
+ #debug "ran #{num_queries} queries to build thread of #{messages.size} messages for #{m.id}: #{m.subj}" if num_queries > 0
messages.each { |mid, builder| yield mid, builder }
true
end
messages.each { |mid, builder| yield mid, builder }
true
end
end
q.add_query Ferret::Search::TermQuery.new(:label, "spam"), :must_not
end
q.add_query Ferret::Search::TermQuery.new(:label, "spam"), :must_not
- Redwood::log "contact search: #{q}"
+ debug "contact search: #{q}"
contacts = {}
num = h[:num] || 20
@index_mutex.synchronize do
@index.search_each q, :sort => "date DESC", :limit => :all do |docid, score|
break if contacts.size >= num
contacts = {}
num = h[:num] || 20
@index_mutex.synchronize do
@index.search_each q, :sort => "date DESC", :limit => :all do |docid, score|
break if contacts.size >= num
- #Redwood::log "got message #{docid} to: #{@index[docid][:to].inspect} and from: #{@index[docid][:from].inspect}"
+ #debug "got message #{docid} to: #{@index[docid][:to].inspect} and from: #{@index[docid][:from].inspect}"
f = @index[docid][:from]
t = @index[docid][:to]
f = @index[docid][:from]
t = @index[docid][:to]
field, name = $1, ($3 || $4)
case field
when "filename"
field, name = $1, ($3 || $4)
case field
when "filename"
- Redwood::log "filename - translated #{field}:#{name} to attachments:(#{name.downcase})"
+ debug "filename: translated #{field}:#{name} to attachments:(#{name.downcase})"
"attachments:(#{name.downcase})"
when "filetype"
"attachments:(#{name.downcase})"
when "filetype"
- Redwood::log "filetype - translated #{field}:#{name} to attachments:(*.#{name.downcase})"
+ debug "filetype: translated #{field}:#{name} to attachments:(*.#{name.downcase})"
"attachments:(*.#{name.downcase})"
end
end
"attachments:(*.#{name.downcase})"
end
end
if realdate
case field
when "after"
if realdate
case field
when "after"
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.end}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate.end}"
"date:(>= #{sprintf "%012d", realdate.end.to_i})"
when "before"
"date:(>= #{sprintf "%012d", realdate.end.to_i})"
when "before"
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
"date:(<= #{sprintf "%012d", realdate.begin.to_i})"
else
"date:(<= #{sprintf "%012d", realdate.begin.to_i})"
else
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate}"
"date:(<= #{sprintf "%012d", realdate.end.to_i}) date:(>= #{sprintf "%012d", realdate.begin.to_i})"
end
else
"date:(<= #{sprintf "%012d", realdate.end.to_i}) date:(>= #{sprintf "%012d", realdate.begin.to_i})"
end
else
- Redwood::log "hook[#@__name]: #{s}"
+ info "hook[#@__name]: #{s}"
- Redwood::log("hook: " + m)
return if last_id == @ids.length
range = (@ids.length + 1) .. last_id
return if last_id == @ids.length
range = (@ids.length + 1) .. last_id
- Redwood::log "fetching IMAP headers #{range}"
+ debug "fetching IMAP headers #{range}"
fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v|
id = make_id v
@ids << id
@imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] }
end
fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v|
id = make_id v
@ids << id
@imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] }
end
- Redwood::log "done fetching IMAP headers"
+ debug "done fetching IMAP headers"
end
synchronized :scan_mailbox
end
synchronized :scan_mailbox
if good_results.empty?
raise FatalSourceError, "no IMAP response for #{ids} containing all fields #{fields.join(', ')} (got #{results.size} results)"
elsif good_results.size < results.size
if good_results.empty?
raise FatalSourceError, "no IMAP response for #{ids} containing all fields #{fields.join(', ')} (got #{results.size} results)"
elsif good_results.size < results.size
- Redwood::log "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
+ warn "Your IMAP server sucks. It sent #{results.size} results for a request for #{good_results.size} messages. What are you using, Binc?"
raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=CRAM-MD5"
@imap.authenticate 'CRAM-MD5', @username, @password
rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=CRAM-MD5"
@imap.authenticate 'CRAM-MD5', @username, @password
rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
- Redwood::log "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
+ debug "CRAM-MD5 authentication failed: #{e.class}. Trying LOGIN auth..."
begin
raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=LOGIN"
@imap.authenticate 'LOGIN', @username, @password
rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
begin
raise Net::IMAP::NoResponseError unless @imap.capability().member? "AUTH=LOGIN"
@imap.authenticate 'LOGIN', @username, @password
rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
- Redwood::log "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
+ debug "LOGIN authentication failed: #{e.class}. Trying plain-text LOGIN..."
@imap.login @username, @password
end
end
@imap.login @username, @password
end
end
def say s
@say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
def say s
@say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
rescue *RECOVERABLE_ERRORS => e
if (retries += 1) <= 3
@imap = nil
rescue *RECOVERABLE_ERRORS => e
if (retries += 1) <= 3
@imap = nil
- Redwood::log "got #{e.class.name}: #{e.message.inspect}"
+ warn "got #{e.class.name}: #{e.message.inspect}"
require 'chronic'
$have_chronic = true
rescue LoadError => e
require 'chronic'
$have_chronic = true
rescue LoadError => e
- Redwood::log "optional 'chronic' library not found (run 'gem install chronic' to install)"
+ debug "optional 'chronic' library not found; date-time query restrictions disabled"
$have_chronic = false
end
$have_chronic = false
end
def lockfile; File.join @dir, "lock" end
def lock
def lockfile; File.join @dir, "lock" end
def lock
- Redwood::log "locking #{lockfile}..."
+ debug "locking #{lockfile}..."
begin
@lock.lock
rescue Lockfile::MaxTriesLockError
begin
@lock.lock
rescue Lockfile::MaxTriesLockError
def unlock
if @lock && @lock.locked?
def unlock
if @lock && @lock.locked?
- Redwood::log "unlocking #{lockfile}..."
+ debug "unlocking #{lockfile}..."
- Redwood::log "saving index and sources..."
+ debug "saving index and sources..."
FileUtils.mkdir_p @dir unless File.exists? @dir
SourceManager.save_sources
save_index
FileUtils.mkdir_p @dir unless File.exists? @dir
SourceManager.save_sources
save_index
else fail "unknown index type #{index_name.inspect}"
end
Index = Redwood.const_get "#{index_name.capitalize}Index"
else fail "unknown index type #{index_name.inspect}"
end
Index = Redwood.const_get "#{index_name.capitalize}Index"
-Redwood::log "using index #{Index.name}"
+debug "using index #{Index.name}"
+require "sup"
+require 'stringio'
+require 'thread'
+
+## simple centralized logger. outputs to multiple sinks by calling << on them.
+## also keeps a record of all messages, so that adding a new sink will send all
+## previous messages to it by default.
+ LEVELS = %w(debug info warn error) # in order!
- def initialize
- raise "only one Log can be defined" if @@instance
- @@instance = self
- @mode = LogMode.new
- @respawn = true
- @spawning = false # to prevent infinite loops!
+ def initialize level=nil
+ level ||= ENV["SUP_LOG_LEVEL"] || "info"
+ @level = LEVELS.index(level) or raise ArgumentError, "invalid log level #{level.inspect}: should be one of #{LEVELS * ', '}"
+ @mutex = Mutex.new
+ @buf = StringIO.new
+ @sinks = []
- ## must be called if you want to see anything!
- ## once called, will respawn if killed...
- def make_buf
- return if @mode.buffer || !BufferManager.instantiated? || !@respawn || @spawning
- @spawning = true
- @mode.buffer = BufferManager.instance.spawn "log", @mode, :hidden => true, :system => true
- @spawning = false
+ def level; LEVELS[@level] end
+
+ def add_sink s, copy_current=true
+ @mutex.synchronize do
+ @sinks << s
+ s << @buf.string if copy_current
+ end
- def log s
-# $stderr.puts s
- make_buf
- prefix = "#{Time.now}: "
- padding = " " * prefix.length
- first = true
- s.split(/[\r\n]/).each do |l|
- l = l.chomp
- if first
- first = false
- @mode << "#{prefix}#{l}\n"
- else
- @mode << "#{padding}#{l}\n"
+ def remove_sink s; @mutex.synchronize { @sinks.delete s } end
+ def remove_all_sinks!; @mutex.synchronize { @sinks.clear } end
+ def clear!; @mutex.synchronize { @buf = StringIO.new } end
+
+ LEVELS.each_with_index do |l, method_level|
+ define_method(l) do |s|
+ if method_level >= @level
+ send_message format_message(l, Time.now, s)
- $stderr.puts "[#{Time.now}] #{s.chomp}" unless BufferManager.instantiated? && @mode.buffer
-
- def self.method_missing m, *a
- @@instance = Logger.new unless @@instance
- @@instance.send m, *a
+
+ ## send a message regardless of the current logging level
+ def force_message m; send_message format_message(nil, Time.now, m) end
+
+private
+
+ ## level can be nil!
+ def format_message level, time, msg
+ prefix = case level
+ when "warn"; "WARNING: "
+ when "error"; "ERROR: "
+ else ""
+ end
+ "[#{time.to_s}] #{prefix}#{msg}\n"
- def self.buffer
- @@instance.buf
+ ## actually distribute the message
+ def send_message m
+ @mutex.synchronize do
+ @sinks.each { |sink| sink << m }
+ @buf << m
+ end
+## include me to have top-level #debug, #info, etc. methods.
+module LogsStuff
+ Logger::LEVELS.each { |l| define_method(l) { |s| Logger.instance.send(l, s) } }
initial_poll = @ids.empty?
initial_poll = @ids.empty?
- Redwood::log "scanning maildir #@dir..."
+ debug "scanning maildir #@dir..."
begin
@mtimes.each_key do |d|
subdir = File.join(@dir, d)
begin
@mtimes.each_key do |d|
subdir = File.join(@dir, d)
@ids_to_fns[id] = fn
end
else
@ids_to_fns[id] = fn
end
else
- Redwood::log "no poll on #{d}. mtime on indicates no new messages."
+ debug "no poll on #{d}. mtime on indicates no new messages."
end
end
@ids = @dir_ids.values.flatten.uniq.sort!
end
end
@ids = @dir_ids.values.flatten.uniq.sort!
raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
end
raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
end
- Redwood::log "done scanning maildir"
+ debug "done scanning maildir"
@last_scan = Time.now
end
synchronized :scan_mailbox
@last_scan = Time.now
end
synchronized :scan_mailbox
Time.parse time, 0
true
rescue NoMethodError
Time.parse time, 0
true
rescue NoMethodError
- Redwood::log "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
+ warn "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
## all of the methods here can throw SSHFileErrors, SocketErrors,
## Net::SSH::Exceptions and Errno::ENOENTs.
## all of the methods here can throw SSHFileErrors, SocketErrors,
## Net::SSH::Exceptions and Errno::ENOENTs.
-## debugging TODO: remove me
-def debug s
- Redwood::log s
-end
-module_function :debug
-
## a simple buffer of contiguous data
class Buffer
def initialize
## a simple buffer of contiguous data
class Buffer
def initialize
## TODO: share this code with imap
def say s
@say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
## TODO: share this code with imap
def say s
@say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
def viewable?; @lines.nil? end
def view_default! path
cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}' 2>/dev/null"
def viewable?; @lines.nil? end
def view_default! path
cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}' 2>/dev/null"
- Redwood::log "running: #{cmd.inspect}"
+ debug "running: #{cmd.inspect}"
else
id = "sup-faked-" + Digest::MD5.hexdigest(raw_header)
from = header["from"]
else
id = "sup-faked-" + Digest::MD5.hexdigest(raw_header)
from = header["from"]
- #Redwood::log "faking non-existent message-id for message from #{from}: #{id}"
+ #debug "faking non-existent message-id for message from #{from}: #{id}"
header["from"]
else
name = "Sup Auto-generated Fake Sender <sup@fake.sender.example.com>"
header["from"]
else
name = "Sup Auto-generated Fake Sender <sup@fake.sender.example.com>"
- #Redwood::log "faking non-existent sender for message #@id: #{name}"
+ #debug "faking non-existent sender for message #@id: #{name}"
begin
Time.parse date
rescue ArgumentError => e
begin
Time.parse date
rescue ArgumentError => e
- #Redwood::log "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
+ #debug "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
- #Redwood::log "faking non-existent date header for #{@id}"
+ #debug "faking non-existent date header for #{@id}"
parse_header @source.load_header(@source_info)
message_to_chunks @source.load_message(@source_info)
rescue SourceError, SocketError => e
parse_header @source.load_header(@source_info)
message_to_chunks @source.load_message(@source_info)
rescue SourceError, SocketError => e
- Redwood::log "problem getting messages from #{@source}: #{e.message}"
+ warn "problem getting messages from #{@source}: #{e.message}"
## we need force_to_top here otherwise this window will cover
## up the error message one
@source.error ||= e
## we need force_to_top here otherwise this window will cover
## up the error message one
@source.error ||= e
begin
yield
rescue SourceError => e
begin
yield
rescue SourceError => e
- Redwood::log "problem getting messages from #{@source}: #{e.message}"
+ warn "problem getting messages from #{@source}: #{e.message}"
@source.error ||= e
Redwood::report_broken_sources :force_to_top => true
error_message e.message
@source.error ||= e
Redwood::report_broken_sources :force_to_top => true
error_message e.message
def multipart_signed_to_chunks m
if m.body.size != 2
def multipart_signed_to_chunks m
if m.body.size != 2
- Redwood::log "warning: multipart/signed with #{m.body.size} parts (expecting 2)"
+ warn "multipart/signed with #{m.body.size} parts (expecting 2)"
return
end
payload, signature = m.body
if signature.multipart?
return
end
payload, signature = m.body
if signature.multipart?
- Redwood::log "warning: multipart/signed with payload multipart #{payload.multipart?} and signature multipart #{signature.multipart?}"
+ warn "multipart/signed with payload multipart #{payload.multipart?} and signature multipart #{signature.multipart?}"
return
end
## this probably will never happen
if payload.header.content_type == "application/pgp-signature"
return
end
## this probably will never happen
if payload.header.content_type == "application/pgp-signature"
- Redwood::log "warning: multipart/signed with payload content type #{payload.header.content_type}"
+ warn "multipart/signed with payload content type #{payload.header.content_type}"
return
end
if signature.header.content_type != "application/pgp-signature"
## unknown signature type; just ignore.
return
end
if signature.header.content_type != "application/pgp-signature"
## unknown signature type; just ignore.
- #Redwood::log "warning: multipart/signed with signature content type #{signature.header.content_type}"
+ #warn "multipart/signed with signature content type #{signature.header.content_type}"
def multipart_encrypted_to_chunks m
if m.body.size != 2
def multipart_encrypted_to_chunks m
if m.body.size != 2
- Redwood::log "warning: multipart/encrypted with #{m.body.size} parts (expecting 2)"
+ warn "multipart/encrypted with #{m.body.size} parts (expecting 2)"
return
end
control, payload = m.body
if control.multipart?
return
end
control, payload = m.body
if control.multipart?
- Redwood::log "warning: multipart/encrypted with control multipart #{control.multipart?} and payload multipart #{payload.multipart?}"
+ warn "multipart/encrypted with control multipart #{control.multipart?} and payload multipart #{payload.multipart?}"
return
end
if payload.header.content_type != "application/octet-stream"
return
end
if payload.header.content_type != "application/octet-stream"
- Redwood::log "warning: multipart/encrypted with payload content type #{payload.header.content_type}"
+ warn "multipart/encrypted with payload content type #{payload.header.content_type}"
return
end
if control.header.content_type != "application/pgp-encrypted"
return
end
if control.header.content_type != "application/pgp-encrypted"
- Redwood::log "warning: multipart/encrypted with control content type #{signature.header.content_type}"
+ warn "multipart/encrypted with control content type #{signature.header.content_type}"
unless err.empty?
message = err.first.read
if message =~ /^\s*$/
unless err.empty?
message = err.first.read
if message =~ /^\s*$/
- Redwood::log "error running #{command} (but no error message)"
+ warn "error running #{command} (but no error message)"
BufferManager.flash "Error running #{command}!"
else
BufferManager.flash "Error running #{command}!"
else
- Redwood::log "error running #{command}: #{message}"
+ warn "error running #{command}: #{message}"
BufferManager.flash "Error: #{message}"
end
return
BufferManager.flash "Error: #{message}"
end
return
BufferManager.flash "Message sent!"
true
rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error => e
BufferManager.flash "Message sent!"
true
rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error => e
- Redwood::log "Problem sending mail: #{e.message}"
+ warn "Problem sending mail: #{e.message}"
BufferManager.flash "Problem sending mail: #{e.message}"
false
end
BufferManager.flash "Problem sending mail: #{e.message}"
false
end
## TODO make the labelmanager responsible for label counts
## and then it can listen to labeled and unlabeled events, etc.
if total == 0 && !LabelManager::RESERVED_LABELS.include?(label) && !LabelManager.new_label?(label)
## TODO make the labelmanager responsible for label counts
## and then it can listen to labeled and unlabeled events, etc.
if total == 0 && !LabelManager::RESERVED_LABELS.include?(label) && !LabelManager.new_label?(label)
- Redwood::log "no hits for label #{label}, deleting"
+ debug "no hits for label #{label}, deleting"
LabelManager.delete label
next
end
LabelManager.delete label
next
end
+## a variant of text mode that allows the user to automatically follow text,
+## and respawns when << is called if necessary.
+
class LogMode < TextMode
register_keymap do |k|
k.add :toggle_follow, "Toggle follow mode", 'f'
end
class LogMode < TextMode
register_keymap do |k|
k.add :toggle_follow, "Toggle follow mode", 'f'
end
+ def initialize buffer_name
+ @buffer_name = buffer_name
+ @on_kill = []
+ super()
+ ## register callbacks for when the buffer is killed
+ def on_kill &b; @on_kill << b end
+
def toggle_follow
@follow = !@follow
def toggle_follow
@follow = !@follow
- if buffer
- if @follow
- jump_to_line lines - buffer.content_height + 1 # leave an empty line at bottom
- end
- buffer.mark_dirty
+ if @follow
+ jump_to_line(lines - buffer.content_height + 1) # leave an empty line at bottom
- def text= t
- super
- if buffer && @follow
- follow_top = lines - buffer.content_height + 1
- jump_to_line follow_top if topline < follow_top
+ def << s
+ unless buffer
+ BufferManager.spawn @buffer_name, self, :hidden => true, :system => true
- def << line
- super
- if buffer && @follow
+ s.split("\n").each { |l| super(l + "\n") } # insane. different << semantics.
+
+ if @follow
follow_top = lines - buffer.content_height + 1
jump_to_line follow_top if topline < follow_top
end
follow_top = lines - buffer.content_height + 1
jump_to_line follow_top if topline < follow_top
end
def status
super + " (follow: #@follow)"
end
def status
super + " (follow: #@follow)"
end
+
+ def cleanup
+ @on_kill.each { |cb| cb.call self }
+ self.text = ""
+ super
+ end
class PollMode < LogMode
def initialize
@new = true
class PollMode < LogMode
def initialize
@new = true
- super
- end
-
- def puts s=""
- self << s + "\n"
+ super "poll for new messages"
- puts unless @new
- @new = false
- puts "Poll started at #{Time.now}"
- PollManager.do_poll { |s| puts s }
+ unless @new
+ @new = false
+ self << "\n"
+ end
+ self << "Poll started at #{Time.now}\n"
+ PollManager.do_poll { |s| self << (s + "\n") }
## don't check that it's an Account, though; assume they know what they're
## doing.
if hook_reply_from && !(hook_reply_from.is_a? Person)
## don't check that it's an Account, though; assume they know what they're
## doing.
if hook_reply_from && !(hook_reply_from.is_a? Person)
- Redwood::log "reply-from returned non-Person, using default from."
+ info "reply-from returned non-Person, using default from."
hook_reply_from = nil
end
hook_reply_from = nil
end
bt = to.size > 1 ? "#{to.size} recipients" : to.to_s
if BufferManager.ask_yes_or_no "Really bounce to #{bt}?"
bt = to.size > 1 ? "#{to.size} recipients" : to.to_s
if BufferManager.ask_yes_or_no "Really bounce to #{bt}?"
- Redwood::log "Bounce Command: #{cmd}"
+ debug "bounce command: #{cmd}"
begin
IO.popen(cmd, 'w') do |sm|
sm.puts m.raw_message
end
raise SendmailCommandFailed, "Couldn't execute #{cmd}" unless $? == 0
rescue SystemCallError, SendmailCommandFailed => e
begin
IO.popen(cmd, 'w') do |sm|
sm.puts m.raw_message
end
raise SendmailCommandFailed, "Couldn't execute #{cmd}" unless $? == 0
rescue SystemCallError, SendmailCommandFailed => e
- Redwood::log "Problem sending mail: #{e.message}"
+ warn "problem sending mail: #{e.message}"
BufferManager.flash "Problem sending mail: #{e.message}"
end
end
BufferManager.flash "Problem sending mail: #{e.message}"
end
end
@thread = nil
@last_poll = nil
@polling = false
@thread = nil
@last_poll = nil
@polling = false
- end
-
- def buffer
- b, new = BufferManager.spawn_unless_exists("poll for new messages", :hidden => true, :system => true) { PollMode.new }
- b
end
def poll
return if @polling
@polling = true
end
def poll
return if @polling
@polling = true
HookManager.run "before-poll"
BufferManager.flash "Polling for new messages..."
HookManager.run "before-poll"
BufferManager.flash "Polling for new messages..."
- num, numi, from_and_subj, from_and_subj_inbox = buffer.mode.poll
+ num, numi, from_and_subj, from_and_subj_inbox = @mode.poll
if num > 0
BufferManager.flash "Loaded #{num.pluralize 'new message'}, #{numi} to inbox."
else
if num > 0
BufferManager.flash "Loaded #{num.pluralize 'new message'}, #{numi} to inbox."
else
begin
yield "Loading from #{source}... " unless source.done? || (source.respond_to?(:has_errors?) && source.has_errors?)
rescue SourceError => e
begin
yield "Loading from #{source}... " unless source.done? || (source.respond_to?(:has_errors?) && source.has_errors?)
rescue SourceError => e
- Redwood::log "problem getting messages from #{source}: #{e.message}"
+ warn "problem getting messages from #{source}: #{e.message}"
Redwood::report_broken_sources :force_to_top => true
next
end
Redwood::report_broken_sources :force_to_top => true
next
end
source.each do |offset, source_labels|
if source.has_errors?
source.each do |offset, source_labels|
if source.has_errors?
- Redwood::log "error loading messages from #{source}: #{source.error.message}"
+ warn "error loading messages from #{source}: #{source.error.message}"
yield m
end
rescue SourceError => e
yield m
end
rescue SourceError => e
- Redwood::log "problem getting messages from #{source}: #{e.message}"
+ warn "problem getting messages from #{source}: #{e.message}"
Redwood::report_broken_sources :force_to_top => true
end
end
Redwood::report_broken_sources :force_to_top => true
end
end
def initialize source_uri
@source = nil
@source_uri = source_uri
def initialize source_uri
@source = nil
@source_uri = source_uri
- Redwood::log "SentManager intialized with source uri: #@source_uri"
end
def source_id; @source.id; end
end
def source_id; @source.id; end
def default_source
@source = Recoverable.new SentLoader.new
def default_source
@source = Recoverable.new SentLoader.new
- Redwood::log "SentManager initializing default source: #@source."
@source_uri = @source.uri
@source
end
@source_uri = @source.uri
@source
end
header[k] = begin
Rfc2047.decode_to $encoding, v
rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e
header[k] = begin
Rfc2047.decode_to $encoding, v
rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e
- #Redwood::log "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
+ #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
unless @history.empty?
value = get_cursed_value
@i ||= @history.size
unless @history.empty?
value = get_cursed_value
@i ||= @history.size
- #Redwood::log "history before #{@history.inspect}"
+ #debug "history before #{@history.inspect}"
@history[@i] = value #unless value =~ /^\s*$/
@i = (@i + (c == Ncurses::KEY_UP ? -1 : 1)) % @history.size
@value = @history[@i]
@history[@i] = value #unless value =~ /^\s*$/
@i = (@i + (c == Ncurses::KEY_UP ? -1 : 1)) % @history.size
@value = @history[@i]
- #Redwood::log "history after #{@history.inspect}"
+ #debug "history after #{@history.inspect}"
set_cursed_value @value
Ncurses::Form::REQ_END_FIELD
end
set_cursed_value @value
Ncurses::Form::REQ_END_FIELD
end
begin
Iconv.iconv(target + "//IGNORE", charset, text + " ").join[0 .. -2]
rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::InvalidCharacter, Iconv::IllegalSequence => e
begin
Iconv.iconv(target + "//IGNORE", charset, text + " ").join[0 .. -2]
rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::InvalidCharacter, Iconv::IllegalSequence => e
- Redwood::log "warning: error (#{e.class.name}) decoding text from #{charset} to #{target}: #{text[0 ... 20]}"
+ info "couldn't transcode text from #{charset} to #{target} (\"#{text[0 ... 20]}\"...)"
field, name = $1, ($3 || $4)
case field
when "filename"
field, name = $1, ($3 || $4)
case field
when "filename"
- Redwood::log "filename - translated #{field}:#{name} to attachment:\"#{name.downcase}\""
+ debug "filename: translated #{field}:#{name} to attachment:\"#{name.downcase}\""
"attachment:\"#{name.downcase}\""
when "filetype"
"attachment:\"#{name.downcase}\""
when "filetype"
- Redwood::log "filetype - translated #{field}:#{name} to attachment_extension:#{name.downcase}"
+ debug "filetype: translated #{field}:#{name} to attachment_extension:#{name.downcase}"
"attachment_extension:#{name.downcase}"
end
end
"attachment_extension:#{name.downcase}"
end
end
if realdate
case field
when "after"
if realdate
case field
when "after"
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.end}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate.end}"
"date:#{realdate.end.to_i}..#{lastdate}"
when "before"
"date:#{realdate.end.to_i}..#{lastdate}"
when "before"
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate.begin}"
"date:#{firstdate}..#{realdate.end.to_i}"
else
"date:#{firstdate}..#{realdate.end.to_i}"
else
- Redwood::log "chronic: translated #{field}:#{datestr} to #{realdate}"
+ debug "chronic: translated #{field}:#{datestr} to #{realdate}"
"date:#{realdate.begin.to_i}..#{realdate.end.to_i}"
end
else
"date:#{realdate.begin.to_i}..#{realdate.end.to_i}"
end
else
m.attachments.each { |a| text << [a, PREFIX['attachment']] }
truncated_date = if m.date < MIN_DATE
m.attachments.each { |a| text << [a, PREFIX['attachment']] }
truncated_date = if m.date < MIN_DATE
- Redwood::log "warning: adjusting too-low date #{m.date} for indexing"
+ debug "warning: adjusting too-low date #{m.date} for indexing"
MIN_DATE
elsif m.date > MAX_DATE
MIN_DATE
elsif m.date > MAX_DATE
- Redwood::log "warning: adjusting too-high date #{m.date} for indexing"
+ debug "warning: adjusting too-high date #{m.date} for indexing"