imapget 0.0.8 → 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|