/usr/bin/sup-sync is in sup-mail 0.22.1-2.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | #!/usr/bin/ruby
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
require 'uri'
require 'trollop'
require "sup"
PROGRESS_UPDATE_INTERVAL = 15 # seconds
class Float
def to_s; sprintf '%.2f', self; end
def to_time_s; infinite? ? "unknown" : super end
end
class Numeric
def to_time_s
i = to_i
sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
end
end
class Set
def to_s; to_a * ',' end
end
def time
startt = Time.now
yield
Time.now - startt
end
opts = Trollop::options do
version "sup-sync (sup #{Redwood::VERSION})"
banner <<EOS
Synchronizes the Sup index with one or more message sources by adding
messages, deleting messages, or changing message state in the index as
appropriate.
"Message state" means read/unread, archived/inbox, starred/unstarred,
and all user-defined labels on each message.
"Default source state" refers to any state that a source itself has
keeps about a message. Sup-sync uses this information when adding a
new message to the index. The source state is typically limited to
read/unread, archived/inbox status and a single label based on the
source name. Messages using the default source state are placed in
the inbox (i.e. not archived) and unstarred.
Usage:
sup-sync [options] <source>*
where <source>* is zero or more source URIs. If no sources are given,
sync from all usual sources. Supported source URI schemes can be seen
by running "sup-add --help".
Options controlling HOW message state is altered:
EOS
opt :asis, "If the message is already in the index, preserve its state. Otherwise, use default source state. (Default.)", :short => :none
opt :restore, "Restore message state from a dump file created with sup-dump. If a message is not in this dumpfile, act as --asis.", :type => String, :short => :none
opt :discard, "Discard any message state in the index and use the default source state. Dangerous!", :short => :none
opt :archive, "When using the default source state, mark messages as archived.", :short => "-x"
opt :read, "When using the default source state, mark messages as read."
opt :extra_labels, "When using the default source state, also apply these user-defined labels (a comma-separated list)", :default => "", :short => :none
text <<EOS
Other options:
EOS
opt :verbose, "Print message ids as they're processed."
opt :optimize, "As the final operation, optimize the index."
opt :all_sources, "Scan over all sources.", :short => :none
opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
opt :version, "Show version information", :short => :none
conflicts :asis, :restore, :discard
end
op = [:asis, :restore, :discard].find { |x| opts[x] } || :asis
Redwood::start
index = Redwood::Index.init
restored_state = if opts[:restore]
dump = {}
puts "Loading state dump from #{opts[:restore]}..."
IO.foreach opts[:restore] do |l|
l =~ /^(\S+) \((.*?)\)$/ or raise "Can't read dump line: #{l.inspect}"
mid, labels = $1, $2
dump[mid] = labels.to_set_of_symbols
end
puts "Read #{dump.size} entries from dump file."
dump
else
{}
end
seen = {}
index.lock_interactively or exit
begin
index.load
if(s = Redwood::SourceManager.source_for Redwood::SentManager.source_uri)
Redwood::SentManager.source = s
else
Redwood::SourceManager.add_source Redwood::SentManager.default_source
end
sources = if opts[:all_sources]
Redwood::SourceManager.sources
elsif ARGV.empty?
Redwood::SourceManager.usual_sources
else
ARGV.map do |uri|
Redwood::SourceManager.source_for uri or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
end
end
sources.each do |source|
puts "Scanning #{source}..."
num_added = num_updated = num_deleted = num_scanned = num_restored = 0
last_info_time = start_time = Time.now
Redwood::PollManager.poll_from source do |action,m,old_m,progress|
num_scanned += 1
if action == :delete
num_deleted += 1
puts "Deleting #{m.id}" if opts[:verbose]
elsif action == :add
seen[m.id] = true
## tweak source labels according to commandline arguments if necessary
m.labels.delete :inbox if opts[:archive]
m.labels.delete :unread if opts[:read]
m.labels += opts[:extra_labels].to_set_of_symbols(",")
## decide what to do based on message labels and the operation we're performing
dothis = case
when (op == :restore) && restored_state[m.id]
if old_m && (old_m.labels != restored_state[m.id])
num_restored += 1
m.labels = restored_state[m.id]
:update_message_state
elsif old_m.nil?
num_restored += 1
m.labels = restored_state[m.id]
:add_message
else
# labels are the same; don't do anything
end
when op == :discard
if old_m && (old_m.labels != m.labels)
:update_message_state
else
# labels are the same; don't do anything
end
else
if old_m
:update_message
else
:add_message
end
end
## now, actually do the operation
case dothis
when :add_message
puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose]
num_added += 1
when :update_message
puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose]
num_updated += 1
when :update_message_state
puts "Changing flags for #{source}##{m.source_info} from #{old_m.labels} to #{m.labels}" if opts[:verbose]
num_updated += 1
end
else fail "sup-sync cannot handle :update's"
end
if Time.now - last_info_time > PROGRESS_UPDATE_INTERVAL
last_info_time = Time.now
elapsed = last_info_time - start_time
pctdone = progress * 100.0
remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
printf "## scanned %dm (~%.0f%%) @ %.1fm/s. %s elapsed, ~%s remaining\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s
end
next if opts[:dry_run]
end
puts "Scanned #{num_scanned}, added #{num_added}, updated #{num_updated}, deleted #{num_deleted} messages from #{source}."
puts "Restored state on #{num_restored} (#{100.0 * num_restored / num_scanned}%) messages." if num_restored > 0
end
index.save
if opts[:optimize]
puts "Optimizing index..."
optt = time { index.optimize unless opts[:dry_run] }
puts "Optimized index of size #{index.size} in #{optt}s."
end
rescue Redwood::FatalSourceError => e
$stderr.puts "Sorry, I couldn't communicate with a source: #{e.message}"
rescue Exception => e
File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
raise
ensure
Redwood::finish
index.unlock
end
|