commit 7a0d0da45ee6671d2bdd74f0917d3716aaebbb13
parent d934d6f817a4de0327e442de58de1d2b511b5182
Author: Damien Leone <damien.leone@fensalir.fr>
Date: Mon, 28 Jun 2010 00:00:11 +0200
Synchronize local modifications back to a Maildir source
Add the "sync_back_to_maildir" option which is false by default. It
works by updating a mail's filename according to the its current
labels in sup.
This commit also adds the :forwarded and :replied hidden reserved
labels to bring a better Maildir support according to [0]. These
labels are automatically added when replying, forwarding or bouncing a
message.
Keep in mind that deleting a mail in sup will set its 'T' Maildir flag
and might be deleted from the disk by software such as offlineimap.
Only messages that have been modified after having applied this commit
will be synchronized.
[0] http://cr.yp.to/proto/maildir.html
Conflicts:
lib/sup.rb
Diffstat:
9 files changed, 83 insertions(+), 39 deletions(-)
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -286,7 +286,8 @@ EOS
:wrap_width => 0,
:slip_rows => 0,
:col_jump => 2,
- :stem_language => "english"
+ :stem_language => "english",
+ :sync_back_to_maildir => false
}
if File.exists? filename
config = Redwood::load_yaml_obj filename
diff --git a/lib/sup/index.rb b/lib/sup/index.rb
@@ -452,14 +452,19 @@ EOS
query
end
+ def save_message m
+ return unless m.dirty?
+ if @sync_worker
+ @sync_queue << m
+ else
+ update_message_state m
+ end
+ m.clear_dirty
+ end
+
def save_thread t
t.each_dirty_message do |m|
- if @sync_worker
- @sync_queue << m
- else
- update_message_state m
- end
- m.clear_dirty
+ save_message m
end
end
@@ -626,6 +631,8 @@ EOS
end
def sync_message m, overwrite
+ m.sync_back if $config[:sync_back_to_maildir] and m.source.is_a? Maildir
+
doc = synchronize { find_doc(m.id) }
existed = doc != nil
doc ||= Xapian::Document.new
diff --git a/lib/sup/label.rb b/lib/sup/label.rb
@@ -5,10 +5,10 @@ class LabelManager
## labels that have special semantics. user will be unable to
## add/remove these via normal label mechanisms.
- RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox, :attachment ]
+ RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox, :attachment, :forwarded, :replied ]
## labels that will typically be hidden from the user
- HIDDEN_RESERVED_LABELS = [ :starred, :unread, :attachment ]
+ HIDDEN_RESERVED_LABELS = [ :starred, :unread, :attachment, :forwarded, :replied ]
def initialize fn
@fn = fn
diff --git a/lib/sup/maildir.rb b/lib/sup/maildir.rb
@@ -71,6 +71,21 @@ class Maildir < Source
with_file_for(id) { |f| RMail::Parser.read f }
end
+ def sync_back id, labels
+ flags = ""
+
+ ## Flags must be stored in ASCII order according to Maildir
+ ## documentation
+ flags += "D" if labels.member? :draft
+ flags += "F" if labels.member? :starred
+ flags += "P" if labels.member? :forwarded
+ flags += "R" if labels.member? :replied
+ flags += "S" unless labels.member? :unread
+ flags += "T" if labels.member? :deleted
+
+ maildir_mark_file id, flags
+ end
+
def raw_header id
ret = ""
with_file_for(id) do |f|
@@ -149,7 +164,10 @@ class Maildir < Source
def maildir_labels id
(seen?(id) ? [] : [:unread]) +
(trashed?(id) ? [:deleted] : []) +
- (flagged?(id) ? [:starred] : [])
+ (flagged?(id) ? [:starred] : []) +
+ (passed?(id) ? [:forwarded] : []) +
+ (replied?(id) ? [:replied] : []) +
+ (draft?(id) ? [:draft] : [])
end
def draft? id; maildir_data(id)[2].include? "D"; end
@@ -159,13 +177,6 @@ class Maildir < Source
def seen? id; maildir_data(id)[2].include? "S"; end
def trashed? id; maildir_data(id)[2].include? "T"; end
- def mark_draft id; maildir_mark_file id, "D" unless draft? id; end
- def mark_flagged id; maildir_mark_file id, "F" unless flagged? id; end
- def mark_passed id; maildir_mark_file id, "P" unless passed? id; end
- def mark_replied id; maildir_mark_file id, "R" unless replied? id; end
- def mark_seen id; maildir_mark_file id, "S" unless seen? id; end
- def mark_trashed id; maildir_mark_file id, "T" unless trashed? id; end
-
def valid? id
File.exists? File.join(@dir, id)
end
@@ -192,21 +203,24 @@ private
[($1 || id), ($2 || "2"), ($3 || "")]
end
- ## not thread-safe on msg
- def maildir_mark_file msg, flag
- orig_path = @ids_to_fns[msg]
- orig_base, orig_fn = File.split(orig_path)
- new_base = orig_base.slice(0..-4) + 'cur'
- tmp_base = orig_base.slice(0..-4) + 'tmp'
- md_base, md_ver, md_flags = maildir_data msg
- md_flags += flag; md_flags = md_flags.split(//).sort.join.squeeze
- new_path = File.join new_base, "#{md_base}:#{md_ver},#{md_flags}"
- tmp_path = File.join tmp_base, "#{md_base}:#{md_ver},#{md_flags}"
- File.link orig_path, tmp_path
- File.unlink orig_path
- File.link tmp_path, new_path
- File.unlink tmp_path
- @ids_to_fns[msg] = new_path
+ def maildir_mark_file orig_path, flags
+ @mutex.synchronize do
+ new_base = (flags.include?("S")) ? "cur" : "new"
+ md_base, md_ver, md_flags = maildir_data orig_path
+ return orig_path if md_flags == flags
+
+ new_loc = File.join new_base, "#{md_base}:#{md_ver},#{flags}"
+ orig_path = File.join @dir, orig_path
+ new_path = File.join @dir, new_loc
+ tmp_path = File.join @dir, "tmp", "#{md_base}:#{md_ver},#{flags}"
+
+ File.link orig_path, tmp_path
+ File.unlink orig_path
+ File.link tmp_path, new_path
+ File.unlink tmp_path
+
+ new_loc
+ end
end
end
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -285,6 +285,10 @@ EOS
location.each_raw_message_line &b
end
+ def sync_back
+ location.sync_back @labels
+ end
+
## returns all the content from a message that will be indexed
def indexable_content
load_from_source!
@@ -696,6 +700,11 @@ class Location
source.raw_message info
end
+ def sync_back labels
+ new_info = source.sync_back(@info, labels) if source.respond_to? :sync_back
+ @info = new_info if new_info
+ end
+
## much faster than raw_message
def each_raw_message_line &b
source.each_raw_message_line info, &b
diff --git a/lib/sup/modes/forward-mode.rb b/lib/sup/modes/forward-mode.rb
@@ -7,9 +7,10 @@ class ForwardMode < EditMessageMode
"From" => AccountManager.default_account.full_address,
}
+ @m = opts[:message]
header["Subject"] =
- if opts[:message]
- "Fwd: " + opts[:message].subj
+ if @m
+ "Fwd: " + @m.subj
elsif opts[:attachments]
"Fwd: " + opts[:attachments].keys.join(", ")
end
@@ -19,8 +20,8 @@ class ForwardMode < EditMessageMode
header["Bcc"] = opts[:bcc].map { |p| p.full_address }.join(", ") if opts[:bcc]
body =
- if opts[:message]
- forward_body_lines(opts[:message])
+ if @m
+ forward_body_lines @m
elsif opts[:attachments]
["Note: #{opts[:attachments].size.pluralize 'attachment'}."]
end
@@ -68,6 +69,12 @@ protected
m.quotable_header_lines + [""] + m.quotable_body_lines +
["--- End forwarded message ---"]
end
+
+ def send_message
+ return unless super # super returns true if the mail has been sent
+ @m.add_label :forwarded
+ Index.save_message @m
+ end
end
end
diff --git a/lib/sup/modes/reply-mode.rb b/lib/sup/modes/reply-mode.rb
@@ -217,6 +217,12 @@ protected
update
end
end
+
+ def send_message
+ return unless super # super returns true if the mail has been sent
+ @m.add_label :replied
+ Index.save_message @m
+ end
end
end
diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb
@@ -246,6 +246,8 @@ EOS
sm.puts m.raw_message
end
raise SendmailCommandFailed, "Couldn't execute #{cmd}" unless $? == 0
+ m.add_label :forwarded
+ Index.save_message m
rescue SystemCallError, SendmailCommandFailed => e
warn "problem sending mail: #{e.message}"
BufferManager.flash "Problem sending mail: #{e.message}"
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
@@ -209,9 +209,7 @@ EOS
m.locations.delete Location.new(source, args[:old_info])
m.locations.push Location.new(source, args[:new_info])
## Update labels that might have been modified remotely
- [:unread, :starred, :deleted].each do |l|
- m.labels.delete l
- end
+ m.labels -= [:draft, :starred, :forwarded, :replied, :unread, :deleted]
m.labels += args[:labels]
yield :update, m
Index.sync_message m, true