sup 0.11 → 0.12

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sup might be problematic. Click here for more details.

@@ -3,6 +3,7 @@ Rich Lane <rlane at the club.cc.cmu dot edus>
3
3
  Ismo Puustinen <ismo at the iki dot fis>
4
4
  Nicolas Pouillard <nicolas.pouillard at the gmail dot coms>
5
5
  Eric Sherman <hyperbolist at the gmail dot coms>
6
+ Michael Stapelberg <michael at the stapelberg dot des>
6
7
  Ben Walton <bwalton at the artsci.utoronto dot cas>
7
8
  Mike Stipicevic <stipim at the rpi dot edus>
8
9
  Marcus Williams <marcus-sup at the bar-coded dot nets>
@@ -10,34 +11,44 @@ Lionel Ott <white.magic at the gmx dot des>
10
11
  Tero Tilus <tero at the tilus dot nets>
11
12
  Ingmar Vanhassel <ingmar at the exherbo dot orgs>
12
13
  Mark Alexander <marka at the pobox dot coms>
14
+ Gaute Hope <eg at the gaute.vetsj dot coms>
13
15
  Christopher Warrington <chrisw at the rice dot edus>
16
+ W. Trevor King <wking at the drexel dot edus>
17
+ Gaudenz Steinlin <gaudenz at the soziologie dot chs>
14
18
  Richard Brown <rbrown at the exherbo dot orgs>
15
19
  Marc Hartstein <marc.hartstein at the alum.vassar dot edus>
20
+ Sascha Silbe <sascha-pgp at the silbe dot orgs>
16
21
  Israel Herraiz <israel.herraiz at the gmail dot coms>
17
22
  Anthony Martinez <pi+sup at the pihost dot uss>
18
- Michael Stapelberg <michael at the stapelberg dot des>
23
+ Hamish Downer <dmishd at the gmail dot coms>
19
24
  Bo Borgerson <gigabo at the gmail dot coms>
20
25
  William Erik Baxter <web at the superscript dot coms>
26
+ Michael Hamann <michael at the content-space dot des>
21
27
  Grant Hollingworth <grant at the antiflux dot orgs>
22
28
  Adeodato Simó <dato at the net.com.org dot ess>
23
29
  Daniel Schoepe <daniel.schoepe at the googlemail dot coms>
30
+ Jason Petsod <jason at the petsod dot orgs>
24
31
  Steve Goldman <sgoldman at the tower-research dot coms>
25
32
  Edward Z. Yang <ezyang at the MIT dot EDUs>
26
33
  Decklin Foster <decklin at the red-bean dot coms>
27
34
  Cameron Matheson <cam+sup at the cammunism dot orgs>
28
35
  Carl Worth <cworth at the cworth dot orgs>
36
+ Jeff Balogh <its.jeff.balogh at the gmail dot coms>
29
37
  Andrew Pimlott <andrew at the pimlott dot nets>
30
38
  Alex Vandiver <alexmv at the mit dot edus>
31
- Jeff Balogh <its.jeff.balogh at the gmail dot coms>
32
39
  Peter Harkins <ph at the malaprop dot orgs>
33
40
  Kornilios Kourtis <kkourt at the cslab.ece.ntua dot grs>
34
- Michael Hamann <michael at the content-space dot des>
35
41
  Giorgio Lando <patroclo7 at the gmail dot coms>
42
+ Damien Leone <damien.leone at the fensalir dot frs>
36
43
  Benoît PIERRE <benoit.pierre at the gmail dot coms>
44
+ Alvaro Herrera <alvherre at the alvh.no-ip dot orgs>
37
45
  Jonah <Jonah at the GoodCoffee dot cas>
46
+ Adam Lloyd <adam at the alloy-d dot nets>
47
+ Todd Eisenberger <teisenbe at the andrew.cmu dot edus>
38
48
  ian <ian at the lorf dot orgs>
39
49
  Steven Walter <swalter at the monarch.(none)>
40
- Alex Vandiver <alex at the chmrr dot nets>
41
- Stefan Lundström <lundst at the snabb.(none)>
50
+ ian <itaylor at the uark dot edus>
42
51
  Jon M. Dugan <jdugan at the es dot nets>
52
+ Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
53
+ Stefan Lundström <lundst at the snabb.(none)>
43
54
  Kirill Smelkov <kirr at the landau.phys.spbu dot rus>
@@ -1,3 +1,12 @@
1
+ == 0.12 / 2011-01-13
2
+ * Remove deprecated IMAP, IMAPS, and mbox+ssh sources
3
+ * Inline GPG support
4
+ * Robust maildir support
5
+ * sup-dump compatibility between Sup versions
6
+ * New hook: sendmail
7
+ * Better Ruby 1.9/UTF8 support
8
+ * As always, many bugfixes and tweaks.
9
+
1
10
  == 0.11 / 2010-03-07
2
11
  * Remove deprecated Ferret backend.
3
12
  * Add deprecation notices to IMAP, IMAPS, and mbox+ssh sources.
@@ -1,3 +1,12 @@
1
+ Release 0.12:
2
+
3
+ Deprecated remote sources have been removed.
4
+
5
+ Maildir support has been improved to gracefully handle messages that
6
+ move or disappear. The "out of sync" errors should no longer occur.
7
+
8
+ Inline GPG is now supported.
9
+
1
10
  Release 0.11:
2
11
 
3
12
  The deprecated Ferret index has been removed.
data/bin/sup CHANGED
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  require 'fileutils'
14
14
  require 'trollop'
15
- require "sup"; Redwood::check_library_version_against "0.11"
15
+ require "sup"; Redwood::check_library_version_against "0.12"
16
16
 
17
17
  if ENV['SUP_PROFILE']
18
18
  require 'ruby-prof'
@@ -49,7 +49,7 @@ No variables.
49
49
  No return value.
50
50
  EOS
51
51
 
52
- Redwood::HookManager.register "shutdown", <<EOS
52
+ Redwood::HookManager.register "shutdown", <<EOS
53
53
  Executes when sup is shutting down. May be run when sup is crashing,
54
54
  so don\'t do anything too important. Run before the label, contacts,
55
55
  and people are saved.
@@ -58,6 +58,7 @@ No return value.
58
58
  EOS
59
59
 
60
60
  if $opts[:list_hooks]
61
+ Redwood.start
61
62
  Redwood::HookManager.print_hooks
62
63
  exit
63
64
  end
@@ -86,6 +87,7 @@ global_keymap = Keymap.new do |k|
86
87
  k.add :nothing, "Do nothing", :ctrl_g
87
88
  k.add :recall_draft, "Edit most recent draft message", 'R'
88
89
  k.add :show_inbox, "Show the Inbox buffer", 'I'
90
+ k.add :clear_hooks, "Clear all hooks", 'H'
89
91
  k.add :show_console, "Show the Console buffer", '~'
90
92
 
91
93
  ## Submap for less often used keybindings
@@ -94,8 +96,6 @@ global_keymap = Keymap.new do |k|
94
96
  kk.add :run_keybindings_hook, "Rerun keybindings hook", 'k'
95
97
  end
96
98
  end
97
-
98
- Redwood::Keymap.run_hook global_keymap
99
99
 
100
100
  ## the following magic enables wide characters when used with a ruby
101
101
  ## ncurses.so that's been compiled against libncursesw. (note the w.) why
@@ -154,26 +154,6 @@ begin
154
154
  Index.load
155
155
  Index.start_sync_worker unless $opts[:no_threads]
156
156
 
157
- if Redwood::SourceManager.sources.any? { |x| x.is_a? Redwood::MBox::SSHLoader }
158
- $stderr.puts <<EOS
159
- mbox+ssh sources are deprecated and will be removed in the next release.
160
- Running rsync in your before-poll hook is a good alternative.
161
-
162
- Press enter to continue.
163
- EOS
164
- $stdin.gets
165
- end
166
-
167
- if Redwood::SourceManager.sources.any? { |x| x.is_a? Redwood::IMAP }
168
- $stderr.puts <<EOS
169
- IMAP sources are deprecated and will be removed in the next release.
170
- Running offlineimap or fetchmail in your before-poll hook is a good alternative.
171
-
172
- Press enter to continue.
173
- EOS
174
- $stdin.gets
175
- end
176
-
177
157
  $die = false
178
158
  trap("TERM") { |x| $die = true }
179
159
  trap("WINCH") { |x| BufferManager.sigwinch_happened! }
@@ -192,6 +172,7 @@ EOS
192
172
  end
193
173
 
194
174
  HookManager.run "startup"
175
+ Redwood::Keymap.run_hook global_keymap
195
176
 
196
177
  debug "starting curses"
197
178
  Redwood::Logger.remove_sink $stderr
@@ -318,6 +299,8 @@ EOS
318
299
  SearchResultsMode.spawn_from_query "is:unread"
319
300
  when :list_labels
320
301
  labels = LabelManager.all_labels.map { |l| LabelManager.string_for l }
302
+ labels = labels.each { |l| l.force_encoding 'UTF-8' if l.methods.include?(:encoding) }
303
+
321
304
  user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
322
305
  unless user_label.nil?
323
306
  if user_label.empty?
@@ -350,6 +333,8 @@ EOS
350
333
  end
351
334
  when :show_inbox
352
335
  BufferManager.raise_to_front ibuf
336
+ when :clear_hooks
337
+ HookManager.clear
353
338
  when :show_console
354
339
  b, new = bm.spawn_unless_exists("Console", :system => true) { ConsoleMode.new }
355
340
  b.mode.run
@@ -4,7 +4,7 @@ require 'uri'
4
4
  require 'rubygems'
5
5
  require 'highline/import'
6
6
  require 'trollop'
7
- require "sup"; Redwood::check_library_version_against "0.11"
7
+ require "sup"; Redwood::check_library_version_against "0.12"
8
8
 
9
9
  $opts = Trollop::options do
10
10
  version "sup-add (sup #{Redwood::VERSION})"
@@ -97,9 +97,9 @@ begin
97
97
  source =
98
98
  case parsed_uri.scheme
99
99
  when "maildir"
100
- Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
100
+ Redwood::Maildir.new uri, !$opts[:unusual], $opts[:archive], nil, labels
101
101
  when "mbox"
102
- Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
102
+ Redwood::MBox.new uri, !$opts[:unusual], $opts[:archive], nil, labels
103
103
  when nil
104
104
  Trollop::die "Sources must be specified with an URI"
105
105
  else
@@ -57,7 +57,7 @@ def add_source
57
57
  return if fn.nil? || fn.empty?
58
58
 
59
59
  $last_fn = fn
60
- [Redwood::MBox::Loader.suggest_labels_for(fn),
60
+ [Redwood::MBox.suggest_labels_for(fn),
61
61
  { :scheme => "mbox", :path => fn }]
62
62
  when :maildir
63
63
  $last_fn ||= ENV["MAIL"]
@@ -1,17 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rubygems'
4
+ require 'xapian'
4
5
  require 'trollop'
5
- require "sup"; Redwood::check_library_version_against "0.11"
6
+ require 'set'
7
+
8
+ BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
6
9
 
7
10
  $opts = Trollop::options do
8
- version "sup-dump (sup #{Redwood::VERSION})"
11
+ version "sup-dump"
9
12
  banner <<EOS
10
13
  Dumps all message state from the sup index to standard out. You can
11
14
  later use sup-sync --restored --restore <filename> to recover the index.
12
15
 
13
- This tool is primarily useful in the event that a Ferret upgrade breaks
14
- the index format. This happened, for example, at Ferret version 0.11.
16
+ This tool is primarily useful in the event that a Sup upgrade breaks index
17
+ format compatibility.
15
18
 
16
19
  Usage:
17
20
  sup-dump > <filename>
@@ -19,10 +22,20 @@ Usage:
19
22
  EOS
20
23
  end
21
24
 
22
- index = Redwood::Index.init
23
- Redwood::SourceManager.init
24
- index.load
25
+ xapian = Xapian::Database.new File.join(BASE_DIR, 'xapian')
26
+ version = xapian.get_metadata 'rescue-version'
27
+ version = '0' if version.empty?
25
28
 
26
- index.each_message :load_spam => true, :load_deleted => true, :load_killed => true do |m|
27
- puts "#{m.id} (#{m.labels.to_a.sort_by { |l| l.to_s } * ' '})"
29
+ case version
30
+ when '0'
31
+ xapian.postlist('Kmail').each do |x|
32
+ begin
33
+ entry = Marshal.load(xapian.document(x.docid).data)
34
+ puts "#{entry[:message_id]} (#{entry[:labels].sort_by { |l| l.to_s } * ' '})"
35
+ rescue
36
+ $stderr.puts "failed to dump document #{x.docid}"
37
+ end
38
+ end
39
+ else
40
+ abort "this sup-dump version doesn't understand your index"
28
41
  end
@@ -58,17 +58,7 @@ ARGV.each do |fn|
58
58
  next if Redwood::SourceManager.source_for fn
59
59
 
60
60
  ## TODO: merge this code with the same snippet in import
61
- source =
62
- case fn
63
- when %r!^imaps?://!
64
- print "Username for #{fn}: "
65
- username = $stdin.gets.chomp
66
- print "Password for #{fn} (warning: cleartext): "
67
- password = $stdin.gets.chomp
68
- Redwood::IMAP.new(fn, username, password, nil, !$opts[:unusual], $opts[:archive])
69
- else
70
- Redwood::MBox::Loader.new(fn, nil, !$opts[:unusual], $opts[:archive])
71
- end
61
+ source = Redwood::MBox.new(fn, nil, !$opts[:unusual], $opts[:archive])
72
62
 
73
63
  source_ids = Hash.new 0
74
64
  count = 0
@@ -3,7 +3,7 @@
3
3
  require 'uri'
4
4
  require 'rubygems'
5
5
  require 'trollop'
6
- require "sup"; Redwood::check_library_version_against "0.11"
6
+ require "sup"; Redwood::check_library_version_against "0.12"
7
7
 
8
8
  PROGRESS_UPDATE_INTERVAL = 15 # seconds
9
9
 
@@ -53,16 +53,6 @@ where <source>* is zero or more source URIs. If no sources are given,
53
53
  sync from all usual sources. Supported source URI schemes can be seen
54
54
  by running "sup-add --help".
55
55
 
56
- Options controlling WHICH messages sup-sync operates on:
57
- EOS
58
- opt :new, "Operate on new messages only. Don't scan over the entire source. (Default.)", :short => :none
59
- opt :changed, "Scan over the entire source for messages that have been deleted, altered, or moved from another source."
60
- opt :restored, "Operate only on those messages included in a dump file as specified by --restore which have changed state."
61
- opt :all, "Operate on all messages in the source, regardless of newness or changedness."
62
- opt :start_at, "For --changed, --restored and --all, start at a particular offset.", :type => :int
63
-
64
- text <<EOS
65
-
66
56
  Options controlling HOW message state is altered:
67
57
  EOS
68
58
  opt :asis, "If the message is already in the index, preserve its state. Otherwise, use default source state. (Default.)", :short => :none
@@ -82,16 +72,9 @@ EOS
82
72
  opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
83
73
  opt :version, "Show version information", :short => :none
84
74
 
85
- conflicts :changed, :all, :new, :restored
86
75
  conflicts :asis, :restore, :discard
87
76
  end
88
- Trollop::die :restored, "requires --restore" if opts[:restored] unless opts[:restore]
89
- if opts[:start_at]
90
- Trollop::die :start_at, "must be non-negative" if opts[:start_at] < 0
91
- Trollop::die :start_at, "requires either --changed, --restored or --all" unless opts[:changed] || opts[:restored] || opts[:all]
92
- end
93
77
 
94
- target = [:new, :changed, :all, :restored].find { |x| opts[x] } || :new
95
78
  op = [:asis, :restore, :discard].find { |x| opts[x] } || :asis
96
79
 
97
80
  Redwood::start
@@ -116,6 +99,12 @@ index.lock_interactively or exit
116
99
  begin
117
100
  index.load
118
101
 
102
+ if(s = Redwood::SourceManager.source_for Redwood::SentManager.source_uri)
103
+ Redwood::SentManager.source = s
104
+ else
105
+ Redwood::SourceManager.add_source Redwood::SentManager.default_source
106
+ end
107
+
119
108
  sources = if opts[:all_sources]
120
109
  Redwood::SourceManager.sources
121
110
  elsif ARGV.empty?
@@ -126,134 +115,80 @@ begin
126
115
  end
127
116
  end
128
117
 
129
- ## for all target specifications except for only-new messages, reset the
130
- ## source to the beginning (or to the user-specified starting point.)
131
- unless target == :new
132
- if opts[:start_at]
133
- Trollop::die :start_at, "can only be used on one source" unless sources.size == 1
134
- sources.first.seek_to! opts[:start_at]
135
- sources.first.correct_offset! if sources.first.respond_to?(:correct_offset!)
136
- else
137
- sources.each { |s| s.reset! }
138
- end
139
- end
140
-
141
118
  sources.each do |source|
142
119
  puts "Scanning #{source}..."
143
120
  num_added = num_updated = num_scanned = num_restored = 0
144
121
  last_info_time = start_time = Time.now
145
122
 
146
- Redwood::PollManager.each_message_from source do |m|
147
- num_scanned += 1
148
- seen[m.id] = true
149
- old_m = index.build_message m.id
150
-
151
- case target
152
- when :changed
153
- ## skip this message if we're operating only on changed messages, the
154
- ## message is in the index, and it's unchanged from what the source is
155
- ## reporting.
156
- next if old_m && old_m.source.id == m.source.id && old_m.source_info == m.source_info
157
- when :restored
158
- ## skip if we're operating on restored messages, and this one
159
- ## ain't (or we wouldn't be making a change)
160
- next unless old_m && restored_state[m.id] && restored_state[m.id] != old_m.labels
161
- when :new
162
- ## nothing to do; we'll consider all messages starting at the start offset, which
163
- ## hasn't been changed.
164
- when :all
165
- ## nothing to do; we'll consider all messages starting at the start offset, which
166
- ## was reset to the beginning above.
167
- end
168
-
169
- ## tweak source labels according to commandline arguments if necessary
170
- m.labels.delete :inbox if opts[:archive]
171
- m.labels.delete :unread if opts[:read]
172
- m.labels += opts[:extra_labels].to_set_of_symbols(",")
173
-
174
- ## decide what to do based on message labels and the operation we're performing
175
- dothis, new_labels = case
176
- when (op == :restore) && restored_state[m.id]
177
- if old_m && (old_m.labels != restored_state[m.id])
178
- num_restored += 1
179
- [:update_message_state, restored_state[m.id]]
180
- elsif old_m.nil?
181
- num_restored += 1
182
- m.labels = restored_state[m.id]
183
- :add_message
184
- else
185
- # labels are the same; don't do anything
186
- end
187
- when op == :discard
188
- if old_m && (old_m.labels != m.labels)
189
- [:update_message_state, m.labels]
190
- else
191
- # labels are the same; don't do anything
192
- end
193
- else
194
- ## duplicate behavior of poll mode: if index_state is non-nil, this is a newer
195
- ## version of an older message, so merge in any new labels except :unread and
196
- ## :inbox.
197
- ##
198
- ## TODO: refactor such that this isn't duplicated
199
- if old_m
200
- m.labels = old_m.labels + (m.labels - [:unread, :inbox])
201
- :update_message
123
+ Redwood::PollManager.poll_from source do |action,m,old_m,progress|
124
+ if action == :delete
125
+ puts "Deleting #{m.id}"
126
+ elsif action == :add
127
+ num_scanned += 1
128
+ seen[m.id] = true
129
+
130
+ ## tweak source labels according to commandline arguments if necessary
131
+ m.labels.delete :inbox if opts[:archive]
132
+ m.labels.delete :unread if opts[:read]
133
+ m.labels += opts[:extra_labels].to_set_of_symbols(",")
134
+
135
+ ## decide what to do based on message labels and the operation we're performing
136
+ dothis = case
137
+ when (op == :restore) && restored_state[m.id]
138
+ if old_m && (old_m.labels != restored_state[m.id])
139
+ num_restored += 1
140
+ m.labels = restored_state[m.id]
141
+ :update_message_state
142
+ elsif old_m.nil?
143
+ num_restored += 1
144
+ m.labels = restored_state[m.id]
145
+ :add_message
146
+ else
147
+ # labels are the same; don't do anything
148
+ end
149
+ when op == :discard
150
+ if old_m && (old_m.labels != m.labels)
151
+ :update_message_state
152
+ else
153
+ # labels are the same; don't do anything
154
+ end
202
155
  else
203
- :add_message
156
+ if old_m
157
+ :update_message
158
+ else
159
+ :add_message
160
+ end
204
161
  end
205
- end
206
162
 
207
- ## now, actually do the operation
208
- case dothis
209
- when :add_message
210
- puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose]
211
- index.add_message m unless opts[:dry_run]
212
- num_added += 1
213
- when :update_message
214
- puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose]
215
- index.update_message m unless opts[:dry_run]
216
- num_updated += 1
217
- when :update_message_state
218
- puts "Changing flags for #{source}##{m.source_info} from #{m.labels} to #{new_labels}" if opts[:verbose]
219
- m.labels = new_labels
220
- index.update_message_state m unless opts[:dry_run]
221
- num_updated += 1
222
- end
163
+ ## now, actually do the operation
164
+ case dothis
165
+ when :add_message
166
+ puts "Adding new message #{source}##{m.source_info} with labels #{m.labels}" if opts[:verbose]
167
+ num_added += 1
168
+ when :update_message
169
+ puts "Updating message #{source}##{m.source_info}; labels #{old_m.labels} => #{m.labels}; offset #{old_m.source_info} => #{m.source_info}" if opts[:verbose]
170
+ num_updated += 1
171
+ when :update_message_state
172
+ puts "Changing flags for #{source}##{m.source_info} from #{old_m.labels} to #{m.labels}" if opts[:verbose]
173
+ num_updated += 1
174
+ end
223
175
 
224
- if Time.now - last_info_time > PROGRESS_UPDATE_INTERVAL
225
- last_info_time = Time.now
226
- elapsed = last_info_time - start_time
227
- pctdone = source.respond_to?(:pct_done) ? source.pct_done : 100.0 * (source.cur_offset.to_f - source.start_offset).to_f / (source.end_offset - source.start_offset).to_f
228
- remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
229
- printf "## read %dm (~%.0f%%) @ %.1fm/s. %s elapsed, ~%s remaining, offset #{source.cur_offset}\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s
176
+ if Time.now - last_info_time > PROGRESS_UPDATE_INTERVAL
177
+ last_info_time = Time.now
178
+ elapsed = last_info_time - start_time
179
+ pctdone = progress * 100.0
180
+ remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
181
+ printf "## read %dm (~%.0f%%) @ %.1fm/s. %s elapsed, ~%s remaining\n", num_scanned, pctdone, num_scanned / elapsed, elapsed.to_time_s, remaining.to_time_s
182
+ end
183
+ else fail
230
184
  end
185
+ next if opts[:dry_run]
231
186
  end
232
187
 
233
188
  puts "Scanned #{num_scanned}, added #{num_added}, updated #{num_updated} messages from #{source}."
234
189
  puts "Restored state on #{num_restored} (#{100.0 * num_restored / num_scanned}%) messages." if num_restored > 0
235
190
  end
236
191
 
237
- ## delete any messages in the index that claim they're from one of
238
- ## these sources, but that we didn't see.
239
- if (target == :all || target == :changed)
240
- puts "Deleting missing messages from the index..."
241
- num_del, num_scanned = 0, 0
242
- sources.each do |source|
243
- raise "no source id for #{source}" unless source.id
244
- index.each_message :source_id => source.id, :load_spam => true, :load_deleted => true, :load_killed => true do |m|
245
- num_scanned += 1
246
- unless seen[m.id]
247
- next unless m.source_info >= opts[:start_at] if opts[:start_at]
248
- puts "Deleting #{m.id}" if opts[:verbose]
249
- index.delete m.id unless opts[:dry_run]
250
- num_del += 1
251
- end
252
- end
253
- end
254
- puts "Deleted #{num_del} / #{num_scanned} messages"
255
- end
256
-
257
192
  index.save
258
193
 
259
194
  if opts[:optimize]