imapget 0.0.8 → 0.1.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog +10 -0
- data/README +8 -9
- data/Rakefile +12 -9
- data/bin/imapget +3 -88
- data/example/config.yaml +4 -4
- data/lib/imapget/cli.rb +125 -0
- data/lib/imapget/version.rb +3 -3
- data/lib/imapget.rb +134 -113
- metadata +66 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf0abd8585721204d2c457a24b593605dd1cd4fb
|
4
|
+
data.tar.gz: a7de9a98f18771603df466c0b055de8a83a704a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76671c4d3a6c52713650c08ac824f208dcb66fbc1026c7fe2791ec2d9a52ce9ace41eed80aa9edcd96a2f223f080cb2c6ce7daa25aac2cb9662138951d45f777
|
7
|
+
data.tar.gz: 89c22c6aa52da221f20712b598adbac58eaf320daa3ec60129639d465fd483b2b86b48ac3ae30760c42ad2096da8b3595580fdfde81115c62f75746d5cf848e6
|
data/ChangeLog
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
= Revision history for imapget
|
4
4
|
|
5
|
+
== 0.1.0 [unreleased]
|
6
|
+
|
7
|
+
* Added options <tt>--dupes</tt> and <tt>--uniq</tt>.
|
8
|
+
* Added STARTTLS support.
|
9
|
+
* Added LOGIN as fallback for AUTHENTICATE.
|
10
|
+
* Added +batch_size+ option.
|
11
|
+
* Dropped +use_ssl+ options; use +ssl+ instead.
|
12
|
+
* Dropped support for Ruby 1.8.
|
13
|
+
* Internal refactoring.
|
14
|
+
|
5
15
|
== 0.0.1 [2009-05-20]
|
6
16
|
|
7
17
|
* Birthday :-)
|
data/README
CHANGED
@@ -2,22 +2,21 @@
|
|
2
2
|
|
3
3
|
== VERSION
|
4
4
|
|
5
|
-
This documentation refers to imapget version 0.0
|
5
|
+
This documentation refers to imapget version 0.1.0
|
6
6
|
|
7
7
|
|
8
8
|
== DESCRIPTION
|
9
9
|
|
10
|
-
Downloads IMAP mails locally. It deletes local mails that
|
11
|
-
on the server, but never ever modifies anything on the
|
12
|
-
different from your typical *sync* tool.
|
10
|
+
Downloads IMAP mails locally. It deletes local mails that are marked as deleted
|
11
|
+
on the server, but, while in download mode, never ever modifies anything on the
|
12
|
+
server! Thus, it's different from your typical *sync* tool.
|
13
13
|
|
14
14
|
|
15
15
|
== LINKS
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
RubyGem:: http://rubygems.org/gems/imapget
|
17
|
+
Documentation:: https://blackwinter.github.io/imapget/
|
18
|
+
Source code:: https://github.com/blackwinter/imapget
|
19
|
+
RubyGem:: https://rubygems.org/gems/imapget
|
21
20
|
|
22
21
|
|
23
22
|
== AUTHORS
|
@@ -27,7 +26,7 @@ RubyGem:: http://rubygems.org/gems/imapget
|
|
27
26
|
|
28
27
|
== LICENSE AND COPYRIGHT
|
29
28
|
|
30
|
-
Copyright (C) 2009-
|
29
|
+
Copyright (C) 2009-2014 Jens Wille
|
31
30
|
|
32
31
|
imapget is free software: you can redistribute it and/or modify it under
|
33
32
|
the terms of the GNU Affero General Public License as published by the Free
|
data/Rakefile
CHANGED
@@ -4,15 +4,18 @@ begin
|
|
4
4
|
require 'hen'
|
5
5
|
|
6
6
|
Hen.lay! {{
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
7
|
+
gem: {
|
8
|
+
name: %q{imapget},
|
9
|
+
version: IMAPGet::VERSION,
|
10
|
+
summary: %q{Get IMAP mails.},
|
11
|
+
description: %q{Download e-mails from IMAP server.},
|
12
|
+
author: %q{Jens Wille},
|
13
|
+
email: %q{jens.wille@gmail.com},
|
14
|
+
license: %q{AGPL-3.0},
|
15
|
+
homepage: :blackwinter,
|
16
|
+
dependencies: %w[highline] << ['ruby-nuggets', '>= 0.9.8'],
|
17
|
+
|
18
|
+
required_ruby_version: '>= 1.9.3'
|
16
19
|
}
|
17
20
|
}}
|
18
21
|
rescue LoadError => err
|
data/bin/imapget
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# #
|
6
6
|
# imapget -- Download IMAP mails #
|
7
7
|
# #
|
8
|
-
# Copyright (C) 2009-
|
8
|
+
# Copyright (C) 2009-2014 Jens Wille #
|
9
9
|
# #
|
10
10
|
# Authors: #
|
11
11
|
# Jens Wille <jens.wille@gmail.com> #
|
@@ -26,90 +26,5 @@
|
|
26
26
|
###############################################################################
|
27
27
|
#++
|
28
28
|
|
29
|
-
require '
|
30
|
-
|
31
|
-
|
32
|
-
require 'rubygems'
|
33
|
-
require 'highline/import'
|
34
|
-
|
35
|
-
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
36
|
-
|
37
|
-
require 'imapget'
|
38
|
-
|
39
|
-
USAGE = "Usage: #{$0} [-h|--help] [options] [profile]..."
|
40
|
-
|
41
|
-
options = {
|
42
|
-
:config => 'config.yaml',
|
43
|
-
:directory => nil,
|
44
|
-
:check => false
|
45
|
-
}
|
46
|
-
|
47
|
-
OptionParser.new { |opts|
|
48
|
-
opts.banner = USAGE
|
49
|
-
|
50
|
-
opts.separator ''
|
51
|
-
opts.separator 'Options:'
|
52
|
-
|
53
|
-
opts.on('-c', '--config YAML', "Config file [Default: #{options[:config]}#{' (currently not present)' unless File.readable?(options[:config])}]") { |f|
|
54
|
-
options[:config] = f
|
55
|
-
}
|
56
|
-
|
57
|
-
opts.separator ''
|
58
|
-
|
59
|
-
opts.on('-d', '--directory PATH', "Target directory to store mails [Default: BASE_DIR/<profile>]") { |d|
|
60
|
-
options[:directory] = d
|
61
|
-
}
|
62
|
-
|
63
|
-
opts.separator ''
|
64
|
-
|
65
|
-
opts.on('-C', '--check', "Only check include/exclude statements") {
|
66
|
-
options[:check] = true
|
67
|
-
}
|
68
|
-
|
69
|
-
opts.separator ''
|
70
|
-
opts.separator 'Generic options:'
|
71
|
-
|
72
|
-
opts.on('-h', '--help', 'Print this help message and exit') {
|
73
|
-
abort opts.to_s
|
74
|
-
}
|
75
|
-
|
76
|
-
opts.on('--version', 'Print program version and exit') {
|
77
|
-
abort "#{File.basename($0)} v#{IMAPGet::VERSION}"
|
78
|
-
}
|
79
|
-
}.parse!
|
80
|
-
|
81
|
-
abort "Config file not found: #{options[:config]}\n#{USAGE}" unless File.readable?(options[:config])
|
82
|
-
|
83
|
-
global_config = YAML.load_file(options[:config])
|
84
|
-
default_config = global_config.reject { |k, _| k.is_a?(String) }
|
85
|
-
|
86
|
-
profiles = ARGV.empty? ? global_config.keys.select { |k| k.is_a?(String) }.sort : ARGV
|
87
|
-
|
88
|
-
profiles.each { |profile|
|
89
|
-
unless config = global_config[profile]
|
90
|
-
warn "Profile not found: #{profile}"
|
91
|
-
next
|
92
|
-
end
|
93
|
-
|
94
|
-
config.update(default_config) { |key, old, new| old }
|
95
|
-
|
96
|
-
next if config[:skip]
|
97
|
-
|
98
|
-
unless host = config[:host]
|
99
|
-
warn "No host for profile #{profile}"
|
100
|
-
next
|
101
|
-
end
|
102
|
-
|
103
|
-
config[:user] ||= ask("User for #{profile} on #{host}: ")
|
104
|
-
config[:password] ||= ask("Password for #{config[:user]}@#{host}: ") { |q| q.echo = false }
|
105
|
-
|
106
|
-
imapget = IMAPGet.new(config)
|
107
|
-
|
108
|
-
if options[:check]
|
109
|
-
imapget.each { |mailbox|
|
110
|
-
puts mailbox.name
|
111
|
-
}
|
112
|
-
else
|
113
|
-
imapget.get(options[:directory] || File.join(config[:base_dir] || '.', profile))
|
114
|
-
end
|
115
|
-
}
|
29
|
+
require 'imapget/cli'
|
30
|
+
IMAPGet::CLI.execute
|
data/example/config.yaml
CHANGED
data/lib/imapget/cli.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# imapget -- Download IMAP mails #
|
5
|
+
# #
|
6
|
+
# Copyright (C) 2009-2014 Jens Wille #
|
7
|
+
# #
|
8
|
+
# Authors: #
|
9
|
+
# Jens Wille <jens.wille@gmail.com> #
|
10
|
+
# #
|
11
|
+
# imapget is free software; you can redistribute it and/or modify it under #
|
12
|
+
# the terms of the GNU Affero General Public License as published by the Free #
|
13
|
+
# Software Foundation; either version 3 of the License, or (at your option) #
|
14
|
+
# any later version. #
|
15
|
+
# #
|
16
|
+
# imapget is distributed in the hope that it will be useful, but WITHOUT ANY #
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
18
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
19
|
+
# more details. #
|
20
|
+
# #
|
21
|
+
# You should have received a copy of the GNU Affero General Public License #
|
22
|
+
# along with imapget. If not, see <http://www.gnu.org/licenses/>. #
|
23
|
+
# #
|
24
|
+
###############################################################################
|
25
|
+
#++
|
26
|
+
|
27
|
+
require 'nuggets/cli'
|
28
|
+
|
29
|
+
require 'imapget'
|
30
|
+
|
31
|
+
class IMAPGet
|
32
|
+
|
33
|
+
class CLI < Nuggets::CLI
|
34
|
+
|
35
|
+
class << self
|
36
|
+
|
37
|
+
def defaults
|
38
|
+
super.merge(
|
39
|
+
config: 'config.yaml',
|
40
|
+
directory: nil,
|
41
|
+
check: false,
|
42
|
+
dupe: false,
|
43
|
+
uniq: false
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def usage
|
50
|
+
"#{super} [profile]..."
|
51
|
+
end
|
52
|
+
|
53
|
+
def run(arguments)
|
54
|
+
profiles, default_config = config.partition { |k,| k.is_a?(String) }
|
55
|
+
|
56
|
+
(arguments.empty? ? profiles.map(&:first) : arguments).each { |profile|
|
57
|
+
next unless profile_config = profile_config(profiles.assoc(profile))
|
58
|
+
|
59
|
+
imapget = IMAPGet.new(profile_config)
|
60
|
+
|
61
|
+
if options[:check]
|
62
|
+
imapget.each { |mailbox| puts mailbox.name }
|
63
|
+
elsif options[:dupe]
|
64
|
+
imapget.each { |mailbox| imapget.dupes(mailbox.name) }
|
65
|
+
elsif options[:uniq]
|
66
|
+
imapget.each { |mailbox| imapget.uniq!(mailbox.name) {
|
67
|
+
next unless agree('Really delete duplicates? ')
|
68
|
+
} }
|
69
|
+
else
|
70
|
+
imapget.get(options[:directory] ||
|
71
|
+
File.join(profile_config[:base_dir] || '.', profile))
|
72
|
+
end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def merge_config(*)
|
79
|
+
end
|
80
|
+
|
81
|
+
def opts(opts)
|
82
|
+
opts.on('-d', '--directory PATH', "Path to directory to store mails in [Default: BASE_DIR/<profile>]") { |d|
|
83
|
+
options[:directory] = d
|
84
|
+
}
|
85
|
+
|
86
|
+
opts.separator ''
|
87
|
+
|
88
|
+
opts.on('-C', '--check', "Only check include/exclude statements; don't download any mails") {
|
89
|
+
options[:check] = true
|
90
|
+
}
|
91
|
+
|
92
|
+
opts.on('-D', '--dupes', "Only check for duplicate mails; don't download any mails") {
|
93
|
+
options[:dupe] = true
|
94
|
+
}
|
95
|
+
|
96
|
+
opts.on('-U', '--uniq', "Only delete duplicate mails; don't download any mails") {
|
97
|
+
options[:uniq] = true
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def profile_config(profile, profiles, default_config)
|
102
|
+
unless config = profiles.assoc(profile)
|
103
|
+
"No such profile: #{profile}"
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
config = config.last
|
108
|
+
default_config.each { |key, value| config[key] ||= value }
|
109
|
+
|
110
|
+
return if config[:skip]
|
111
|
+
|
112
|
+
unless host = config[:host]
|
113
|
+
warn "No host for profile: #{profile}"
|
114
|
+
return
|
115
|
+
end
|
116
|
+
|
117
|
+
config[:user] ||= ask("User for #{profile} on #{host}: ")
|
118
|
+
config[:password] ||= askpass("Password for #{config[:user]}@#{host}: ")
|
119
|
+
|
120
|
+
config
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
data/lib/imapget/version.rb
CHANGED
@@ -3,14 +3,14 @@ class IMAPGet
|
|
3
3
|
module Version
|
4
4
|
|
5
5
|
MAJOR = 0
|
6
|
-
MINOR =
|
7
|
-
TINY =
|
6
|
+
MINOR = 1
|
7
|
+
TINY = 0
|
8
8
|
|
9
9
|
class << self
|
10
10
|
|
11
11
|
# Returns array representation.
|
12
12
|
def to_a
|
13
|
-
[MAJOR, MINOR, TINY]
|
13
|
+
[MAJOR, MINOR, TINY] << 'pre1'
|
14
14
|
end
|
15
15
|
|
16
16
|
# Short-cut for version string.
|
data/lib/imapget.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# #
|
4
4
|
# imapget -- Download IMAP mails #
|
5
5
|
# #
|
6
|
-
# Copyright (C) 2009-
|
6
|
+
# Copyright (C) 2009-2014 Jens Wille #
|
7
7
|
# #
|
8
8
|
# Authors: #
|
9
9
|
# Jens Wille <jens.wille@gmail.com> #
|
@@ -27,165 +27,186 @@
|
|
27
27
|
require 'time'
|
28
28
|
require 'net/imap'
|
29
29
|
require 'fileutils'
|
30
|
-
require 'enumerator' unless [].respond_to?(:each_slice)
|
31
30
|
|
32
|
-
|
31
|
+
require_relative 'imapget/version'
|
33
32
|
|
34
33
|
class IMAPGet
|
35
34
|
|
36
35
|
include Enumerable
|
37
36
|
|
38
|
-
|
39
|
-
DEFAULT_STRATEGY
|
40
|
-
|
41
|
-
|
42
|
-
DEFAULT_DELIM = '/'
|
37
|
+
DEFAULT_BATCH_SIZE = 100
|
38
|
+
DEFAULT_STRATEGY = :exclude
|
39
|
+
DEFAULT_LOG_LEVEL = 2
|
40
|
+
DEFAULT_DELIMITER = '/'
|
43
41
|
|
44
42
|
FETCH_ATTR = %w[FLAGS INTERNALDATE RFC822]
|
45
|
-
FETCH_SIZE = 100
|
46
43
|
|
47
44
|
attr_reader :imap, :strategy, :incl, :excl
|
48
|
-
attr_accessor :log_level
|
45
|
+
attr_accessor :batch_size, :log_level
|
49
46
|
|
50
47
|
def initialize(options)
|
51
|
-
@
|
48
|
+
@batch_size = options.fetch(:batch_size, DEFAULT_BATCH_SIZE)
|
49
|
+
@log_level = options.fetch(:log_level, DEFAULT_LOG_LEVEL)
|
50
|
+
|
51
|
+
@imap = Net::IMAP.new(options[:host], options)
|
52
|
+
@imap.starttls(options[:starttls]) if options[:starttls]
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
inclexcl options
|
54
|
+
authenticate(options)
|
55
|
+
inclexcl(options)
|
56
56
|
end
|
57
57
|
|
58
58
|
def inclexcl(options)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
Regexp.new(value.map { |v|
|
69
|
-
"\\A#{Regexp.escape(Net::IMAP.encode_utf7(v))}(?:#{delim}|\\z)"
|
70
|
-
}.join('|'))
|
71
|
-
end
|
59
|
+
delim = Regexp.escape(options.fetch(:delim, DEFAULT_DELIMITER))
|
60
|
+
|
61
|
+
%w[include exclude].each { |key|
|
62
|
+
instance_variable_set("@#{key[0, 4]}", case value = options[key.to_sym]
|
63
|
+
when String then Regexp.new(value)
|
64
|
+
when Array then Regexp.new(value.map { |v|
|
65
|
+
"\\A#{Regexp.escape(Net::IMAP.encode_utf7(v))}(?:#{delim}|\\z)"
|
66
|
+
}.join('|'))
|
67
|
+
end)
|
72
68
|
}
|
73
69
|
|
74
|
-
@
|
75
|
-
@strategy = options[:strategy] || DEFAULT_STRATEGY
|
70
|
+
@strategy = options.fetch(:strategy, DEFAULT_STRATEGY)
|
76
71
|
end
|
77
72
|
|
78
73
|
def mailboxes
|
79
|
-
@mailboxes ||= imap.list('', '*')
|
74
|
+
@mailboxes ||= imap.list('', '*').sort
|
80
75
|
end
|
81
76
|
|
82
77
|
def each
|
83
|
-
mailboxes.each { |mailbox|
|
84
|
-
name = mailbox.name
|
85
|
-
|
86
|
-
case strategy
|
87
|
-
when :include
|
88
|
-
next if (excl && name =~ excl) || !(incl && name =~ incl)
|
89
|
-
when :exclude
|
90
|
-
next if (excl && name =~ excl) && !(incl && name =~ incl)
|
91
|
-
end
|
92
|
-
|
93
|
-
yield mailbox if self.include?(mailbox)
|
94
|
-
}
|
78
|
+
mailboxes.each { |mailbox| yield mailbox if include?(mailbox) }
|
95
79
|
end
|
96
80
|
|
97
|
-
def include?(
|
98
|
-
!exclude?(
|
81
|
+
def include?(mailbox)
|
82
|
+
!exclude?(mailbox)
|
99
83
|
end
|
100
84
|
|
101
|
-
def exclude?(
|
102
|
-
name = case
|
103
|
-
when
|
104
|
-
when
|
105
|
-
else
|
106
|
-
raise TypeError, "MailboxList or String expected, got #{mailbox_or_name.class}"
|
85
|
+
def exclude?(mailbox)
|
86
|
+
name = case mailbox
|
87
|
+
when String then mailbox
|
88
|
+
when Net::IMAP::MailboxList then mailbox.name
|
89
|
+
else raise TypeError, "MailboxList or String expected, got #{mailbox.class}"
|
107
90
|
end
|
108
91
|
|
109
92
|
case strategy
|
110
|
-
when :include
|
111
|
-
|
112
|
-
when :exclude
|
113
|
-
(excl && name =~ excl) && !(incl && name =~ incl)
|
93
|
+
when :include then (excl && name =~ excl) || !(incl && name =~ incl)
|
94
|
+
when :exclude then (excl && name =~ excl) && !(incl && name =~ incl)
|
114
95
|
end
|
115
96
|
end
|
116
97
|
|
117
|
-
def get(
|
118
|
-
each { |mailbox|
|
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
|
-
|
98
|
+
def get(dir)
|
99
|
+
each { |mailbox| get_name(name = mailbox.name,
|
100
|
+
File.join(dir, Net::IMAP.decode_utf7(name))) }
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :download, :get
|
104
|
+
|
105
|
+
def get_name(name, dir)
|
106
|
+
log "#{'=' * 10} #{name} #{'=' * 10}"
|
107
|
+
|
108
|
+
imap.examine(name)
|
109
|
+
|
110
|
+
imap.uid_search("SINCE #{mtime(dir)}").each_slice(batch_size) { |batch|
|
111
|
+
fetch_batch(batch) { |mail| fetch(mail, dir, name) }
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
alias_method :download_name, :get_name
|
116
|
+
|
117
|
+
def size(name, attr = 'MESSAGES')
|
118
|
+
imap.status(name, [attr]).values.first
|
119
|
+
end
|
120
|
+
|
121
|
+
def dupes(name, quiet = false)
|
122
|
+
imap.examine(name)
|
123
|
+
|
124
|
+
hash = Hash.new { |h, k| h[k] = [] }
|
125
|
+
|
126
|
+
1.step(size(name), step = batch_size) { |i|
|
127
|
+
fetch_batch([[i, step]]) { |mail|
|
128
|
+
hash[mail.attr['RFC822']] << mail.attr['UID'] unless deleted?(mail)
|
145
129
|
}
|
146
130
|
}
|
131
|
+
|
132
|
+
dupes = hash.flat_map { |_, uids| uids.drop(1) if uids.size > 1 }.compact
|
133
|
+
|
134
|
+
log "#{name}: #{dupes.size}" unless quiet
|
135
|
+
|
136
|
+
dupes
|
147
137
|
end
|
148
138
|
|
149
|
-
|
139
|
+
def delete!(name, uids)
|
140
|
+
unless uids.empty?
|
141
|
+
log "#{name}: #{uids.size}"
|
150
142
|
|
151
|
-
|
143
|
+
yield if block_given?
|
144
|
+
|
145
|
+
imap.select(name)
|
146
|
+
count = imap.store(uids, '+FLAGS', [Net::IMAP::DELETED]).size
|
152
147
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
options[:use_ssl],
|
158
|
-
options[:certs],
|
159
|
-
options[:verify]
|
160
|
-
)
|
148
|
+
log "#{count} DELETED!"
|
149
|
+
|
150
|
+
count
|
151
|
+
end
|
161
152
|
end
|
162
153
|
|
154
|
+
def uniq!(name)
|
155
|
+
delete!(name, dupes(name, true))
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
163
160
|
def authenticate(options)
|
164
|
-
|
165
|
-
imap.capability.
|
166
|
-
options[:user], options[:password]
|
167
|
-
)
|
168
|
-
end
|
169
|
-
|
170
|
-
def fetch(uids, fetch_size = FETCH_SIZE, fetch_attr = FETCH_ATTR)
|
171
|
-
uids.each_slice(fetch_size) { |slice|
|
172
|
-
imap.uid_fetch(slice, fetch_attr).each { |mail|
|
173
|
-
yield mail, *['UID', *fetch_attr].map { |a|
|
174
|
-
v = mail.attr[a]
|
175
|
-
v = Time.parse(v) if v && a == 'INTERNALDATE'
|
176
|
-
v
|
177
|
-
}
|
178
|
-
}
|
161
|
+
if auth = options.fetch(:auth) {
|
162
|
+
imap.capability.grep(/\AAUTH=(.*)/) { $1 }.first
|
179
163
|
}
|
164
|
+
imap.authenticate(auth, options[:user], options[:password])
|
165
|
+
else
|
166
|
+
imap.login(options[:user], options[:password])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def fetch_batch(batch, fetch_attr = FETCH_ATTR, &block)
|
171
|
+
if batch.size == 1
|
172
|
+
case set = batch.first
|
173
|
+
when Array
|
174
|
+
batch = set.first .. (set.first + set.last - 1)
|
175
|
+
when Range
|
176
|
+
batch = set.first .. (set.last - 1) if set.exclude_end?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
imap.uid_fetch(batch, fetch_attr).each(&block)
|
180
181
|
end
|
181
182
|
|
182
|
-
def
|
183
|
-
|
184
|
-
|
183
|
+
def fetch(mail, dir, name)
|
184
|
+
uid = mail.attr['UID'].to_s
|
185
|
+
file = File.join(dir, uid)
|
186
|
+
exists = File.exists?(file)
|
187
|
+
|
188
|
+
if deleted?(mail)
|
189
|
+
if exists
|
190
|
+
info "#{dir}: #{name} <- #{uid}"
|
191
|
+
File.unlink(file)
|
192
|
+
end
|
185
193
|
else
|
186
|
-
|
187
|
-
|
188
|
-
|
194
|
+
date = mail.attr['INTERNALDATE']
|
195
|
+
|
196
|
+
unless exists && date && File.mtime(file) >= Time.parse(date)
|
197
|
+
info "#{dir}: #{name} -> #{uid}"
|
198
|
+
File.write(file, mail.attr['RFC822'])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def deleted?(mail)
|
204
|
+
mail.attr['FLAGS'].include?(Net::IMAP::DELETED)
|
205
|
+
end
|
206
|
+
|
207
|
+
def mtime(dir)
|
208
|
+
Net::IMAP.format_date((File.directory?(dir) ?
|
209
|
+
File.mtime(dir) : (FileUtils.mkdir_p(dir); Time.at(0))).utc)
|
189
210
|
end
|
190
211
|
|
191
212
|
def log(msg, level = 1)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imapget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.1.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Wille
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -24,7 +24,49 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-nuggets
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.8
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.9.8
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hen
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Download e-mails from IMAP server.
|
28
70
|
email: jens.wille@gmail.com
|
29
71
|
executables:
|
30
72
|
- imapget
|
@@ -34,22 +76,34 @@ extra_rdoc_files:
|
|
34
76
|
- COPYING
|
35
77
|
- ChangeLog
|
36
78
|
files:
|
37
|
-
- lib/imapget.rb
|
38
|
-
- lib/imapget/version.rb
|
39
|
-
- bin/imapget
|
40
79
|
- COPYING
|
41
80
|
- ChangeLog
|
42
81
|
- README
|
43
82
|
- Rakefile
|
83
|
+
- bin/imapget
|
44
84
|
- example/config.yaml
|
85
|
+
- lib/imapget.rb
|
86
|
+
- lib/imapget/cli.rb
|
87
|
+
- lib/imapget/version.rb
|
45
88
|
homepage: http://github.com/blackwinter/imapget
|
46
89
|
licenses:
|
47
90
|
- AGPL-3.0
|
48
91
|
metadata: {}
|
49
|
-
post_install_message:
|
92
|
+
post_install_message: |2+
|
93
|
+
|
94
|
+
imapget-0.1.0 [unreleased]:
|
95
|
+
|
96
|
+
* Added options <tt>--dupes</tt> and <tt>--uniq</tt>.
|
97
|
+
* Added STARTTLS support.
|
98
|
+
* Added LOGIN as fallback for AUTHENTICATE.
|
99
|
+
* Added +batch_size+ option.
|
100
|
+
* Dropped +use_ssl+ options; use +ssl+ instead.
|
101
|
+
* Dropped support for Ruby 1.8.
|
102
|
+
* Internal refactoring.
|
103
|
+
|
50
104
|
rdoc_options:
|
51
105
|
- "--title"
|
52
|
-
- imapget Application documentation (v0.0.
|
106
|
+
- imapget Application documentation (v0.1.0.pre1)
|
53
107
|
- "--charset"
|
54
108
|
- UTF-8
|
55
109
|
- "--line-numbers"
|
@@ -62,15 +116,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
116
|
requirements:
|
63
117
|
- - ">="
|
64
118
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
119
|
+
version: 1.9.3
|
66
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
121
|
requirements:
|
68
|
-
- - "
|
122
|
+
- - ">"
|
69
123
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
124
|
+
version: 1.3.1
|
71
125
|
requirements: []
|
72
126
|
rubyforge_project:
|
73
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.2.2
|
74
128
|
signing_key:
|
75
129
|
specification_version: 4
|
76
130
|
summary: Get IMAP mails.
|