imap-backup 1.0.17 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1abac89e145c96edf61e40e5d5efc035c26e71fd
4
- data.tar.gz: 793eb63a28cbf0e3fd3921c7ce72eeee4663ff2d
3
+ metadata.gz: b55658c2e579a76e29322a69048814b31069926e
4
+ data.tar.gz: 43aa7303ba81aaa1318bcb208be7a8ada52437a9
5
5
  SHA512:
6
- metadata.gz: a469154e6ea6767d045dafa2c52ca04abd649e682fcffb992f4ec9c2ce399a569589d0dd2dfd75957d0757ac165f8e1439915f2e2ed0e0df6b93739e1e2ffe4a
7
- data.tar.gz: 0372d03f497dcc3f2cfe1806bc27f530141ccb73618a99e1f4ec37e05d518b7bf6b067cfac466a94967a4753afb37ff52193612fef2446b9c9c44a068e96caa6
6
+ metadata.gz: 19480ec2c72149a8fefc0eeb6930b673072940b92fa6deae405b2318ab3491894dd1b1e29118ac59640561b8b2396d3a21fb4ff82cc8f40ad0830a375a5bed52
7
+ data.tar.gz: 1e9d3f8d900c3eae69e64484d88e506816a4a791b3f260caab01642ffe53f79ce171f3246cf762e05e81f4390108c663e9b4f4f2a5b0e44169325aeae0dc512f
data/.travis.yml CHANGED
@@ -2,8 +2,13 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - 2.1.1
5
+ - 2.1.5
6
+ - 2.2.4
7
+ - 2.3.0
6
8
  branches:
7
9
  only:
8
10
  - master
11
+ before_install:
12
+ - gem update --system
13
+ - gem update bundler
9
14
  script: "bundle exec rake spec"
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # imap-backup [![Build Status](https://secure.travis-ci.org/joeyates/imap-backup.png)][Continuous Integration]
1
+ [![Build Status](https://secure.travis-ci.org/joeyates/imap-backup.png)][Continuous Integration]
2
+ [![Source Analysis](https://codeclimate.com/github/joeyates/imap-backup/badges/gpa.svg)](https://codeclimate.com/github/joeyates/imap-backup)
3
+ [![Test Coverage](https://codeclimate.com/github/joeyates/imap-backup/badges/coverage.svg)](https://codeclimate.com/github/joeyates/imap-backup/coverage)
4
+
5
+ # imap-backup
2
6
 
3
7
  *Backup GMail (or other IMAP) accounts to disk*
4
8
 
@@ -71,6 +75,35 @@ It connects to GMail by default, but you can also specify a server:
71
75
  }
72
76
  ```
73
77
 
78
+ ## Connection options
79
+
80
+ You can override the parameters passed to `Net::IMAP` with `connection_options`.
81
+
82
+ Specifically, if you are using a self-signed certificate and get SSL errors, e.g.
83
+ `certificate verify failed`, you can choose to not verify the TLS connection:
84
+
85
+ ```json
86
+ {
87
+ "accounts":
88
+ [
89
+ {
90
+ "username": "my.user@gmail.com",
91
+ "password": "secret",
92
+ "server": "my.imap.example.com",
93
+ "local_path": "/path/to/backup/root",
94
+ "folders": [
95
+ {"name": "[Gmail]/All Mail"},
96
+ {"name": "my_folder"}
97
+ ],
98
+ "connection_options": {
99
+ "ssl": {"verify_mode": 0},
100
+ "port": 993
101
+ }
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+
74
107
  ## Google Apps
75
108
 
76
109
  * Enable IMAP access to your account via the GMail interface (Settings/Forwarding and POP/IMAP),
data/bin/imap-backup CHANGED
@@ -7,11 +7,11 @@ $:.unshift(File.expand_path('../../lib/', __FILE__))
7
7
  require 'imap/backup'
8
8
 
9
9
  KNOWN_COMMANDS = [
10
- {:name => 'help', :help => 'Show usage'},
11
10
  {:name => 'setup', :help => 'Create/edit the configuration file'},
12
11
  {:name => 'backup', :help => 'Do the backup (default)'},
13
12
  {:name => 'folders', :help => 'List folders for all (or selected) accounts'},
14
13
  {:name => 'status', :help => 'List count of non backed-up emails per folder'},
14
+ {:name => 'help', :help => 'Show usage'},
15
15
  ]
16
16
 
17
17
  options = {:command => 'backup'}
@@ -27,7 +27,7 @@ opts = OptionParser.new do |opts|
27
27
  opts.separator 'Common options:'
28
28
 
29
29
  opts.on('-a', '--accounts ACCOUNT1[,ACCOUNT2,...]', Array, 'only these accounts') do |account|
30
- options[:account] = account
30
+ options[:accounts] = account
31
31
  end
32
32
 
33
33
  opts.on_tail("-h", "--help", "Show usage") do
@@ -60,6 +60,8 @@ end
60
60
  configuration.setup_logging
61
61
 
62
62
  case options[:command]
63
+ when 'setup'
64
+ Imap::Backup::Configuration::Setup.new.run
63
65
  when 'backup'
64
66
  configuration.each_connection do |connection|
65
67
  connection.run_backup
@@ -74,8 +76,6 @@ when 'folders'
74
76
  end
75
77
  folders.each { |f| puts "\t" + f.name }
76
78
  end
77
- when 'setup'
78
- Imap::Backup::Configuration::Setup.new.run
79
79
  when 'status'
80
80
  configuration.each_connection do |connection|
81
81
  puts connection.username
data/imap-backup.gemspec CHANGED
@@ -18,8 +18,14 @@ Gem::Specification.new do |gem|
18
18
 
19
19
  gem.add_runtime_dependency 'rake'
20
20
  gem.add_runtime_dependency 'highline'
21
- gem.add_runtime_dependency 'mail'
21
+ if RUBY_VERSION > '2'
22
+ gem.add_runtime_dependency 'mail'
23
+ else
24
+ gem.add_runtime_dependency 'mime-types', '~> 2.6'
25
+ gem.add_runtime_dependency 'mail', '~> 2.6.3'
26
+ end
22
27
 
28
+ gem.add_development_dependency 'codeclimate-test-reporter', '~> 0.4.8'
23
29
  gem.add_development_dependency 'rspec', '>= 3.0.0'
24
30
  gem.add_development_dependency 'simplecov'
25
31
  end
@@ -0,0 +1,40 @@
1
+ module Email; end
2
+
3
+ class Email::Provider
4
+ def self.for_address(address)
5
+ case
6
+ when address.end_with?('@gmail.com')
7
+ new(:gmail)
8
+ when address.end_with?('@fastmail.fm')
9
+ new(:fastmail)
10
+ else
11
+ new(:default)
12
+ end
13
+ end
14
+
15
+ attr_reader :provider
16
+
17
+ def initialize(provider)
18
+ @provider = provider
19
+ end
20
+
21
+ def options
22
+ case provider
23
+ when :gmail
24
+ {port: 993, ssl: true}
25
+ when :fastmail
26
+ {port: 993, ssl: true}
27
+ else
28
+ {port: 993, ssl: true}
29
+ end
30
+ end
31
+
32
+ def host
33
+ case provider
34
+ when :gmail
35
+ 'imap.gmail.com'
36
+ when :fastmail
37
+ 'mail.messagingengine.com'
38
+ end
39
+ end
40
+ end
data/lib/imap/backup.rb CHANGED
@@ -15,6 +15,7 @@ require 'imap/backup/serializer/base'
15
15
  require 'imap/backup/serializer/directory'
16
16
  require 'imap/backup/serializer/mbox'
17
17
  require 'imap/backup/version'
18
+ require 'email/provider'
18
19
 
19
20
  require 'logger'
20
21
 
@@ -6,21 +6,23 @@ module Imap::Backup
6
6
  class Account::Connection
7
7
  attr_reader :username
8
8
  attr_reader :local_path
9
+ attr_reader :connection_options
9
10
 
10
11
  def initialize(options)
11
12
  @username, @password = options[:username], options[:password]
12
13
  @local_path = options[:local_path]
13
14
  @backup_folders = options[:folders]
14
15
  @server = options[:server]
16
+ @connection_options = options[:connection_options] || {}
15
17
  @folders = nil
18
+ create_account_folder
16
19
  end
17
20
 
18
21
  def folders
19
22
  return @folders if @folders
20
- root = root_for(username)
21
- @folders = imap.list(root, '*')
23
+ @folders = imap.list('', '*')
22
24
  if @folders.nil?
23
- Imap::Backup.logger.warn "Unable to get folder list for account #{username}, (root '#{root}'"
25
+ Imap::Backup.logger.warn "Unable to get folder list for account #{username}"
24
26
  end
25
27
  @folders
26
28
  end
@@ -37,12 +39,9 @@ module Imap::Backup
37
39
  Imap::Backup.logger.debug "Running backup of account: #{username}"
38
40
  # start the connection so we get logging messages in the right order
39
41
  imap
40
- backup_folders.each do |folder|
41
- Imap::Backup.logger.debug "[#{folder[:name]}] running backup"
42
- f = Account::Folder.new(self, folder[:name])
43
- s = Serializer::Mbox.new(local_path, folder[:name])
44
- d = Downloader.new(f, s)
45
- d.run
42
+ each_folder do |folder, serializer|
43
+ Imap::Backup.logger.debug "[#{folder.name}] running backup"
44
+ Downloader.new(folder, serializer).run
46
45
  end
47
46
  end
48
47
 
@@ -50,13 +49,9 @@ module Imap::Backup
50
49
  imap.disconnect
51
50
  end
52
51
 
53
- def server
54
- @server ||= host_for(username)
55
- end
56
-
57
52
  def imap
58
53
  return @imap unless @imap.nil?
59
- options = options_for(server)
54
+ options = provider_options
60
55
  Imap::Backup.logger.debug "Creating IMAP instance: #{server}, options: #{options.inspect}"
61
56
  @imap = Net::IMAP.new(server, options)
62
57
  Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
@@ -67,6 +62,22 @@ module Imap::Backup
67
62
 
68
63
  private
69
64
 
65
+ def each_folder
66
+ backup_folders.each do |folder_info|
67
+ folder = Account::Folder.new(self, folder_info[:name])
68
+ serializer = Serializer::Mbox.new(local_path, folder_info[:name])
69
+ yield folder, serializer
70
+ end
71
+ end
72
+
73
+ def create_account_folder
74
+ Utils.make_folder(
75
+ Configuration::Store::CONFIGURATION_DIRECTORY,
76
+ File.basename(local_path),
77
+ Serializer::DIRECTORY_PERMISSIONS
78
+ )
79
+ end
80
+
70
81
  def password
71
82
  @password
72
83
  end
@@ -80,35 +91,18 @@ module Imap::Backup
80
91
  (folders || []).map { |f| {:name => f.name} }
81
92
  end
82
93
 
83
- def host_for(username)
84
- case username
85
- when /@gmail\.com/
86
- 'imap.gmail.com'
87
- when /@fastmail\.fm/
88
- 'mail.messagingengine.com'
89
- end
94
+ def provider
95
+ @provider ||= Email::Provider.for_address(username)
90
96
  end
91
97
 
92
- def root_for(username)
93
- case username
94
- when /@gmail\.com/
95
- '/'
96
- when /@fastmail\.fm/
97
- 'INBOX'
98
- else
99
- '/'
100
- end
98
+ def server
99
+ return @server if @server
100
+ return nil if provider.nil?
101
+ @server = provider.host
101
102
  end
102
103
 
103
- def options_for(server)
104
- case server
105
- when 'imap.gmail.com'
106
- {:port => 993, :ssl => true}
107
- when 'mail.messagingengine.com'
108
- {:port => 993, :ssl => true}
109
- else
110
- {:port => 993, :ssl => true}
111
- end
104
+ def provider_options
105
+ provider.options.merge(connection_options)
112
106
  end
113
107
  end
114
108
  end
@@ -138,12 +138,12 @@ Account:
138
138
  end
139
139
 
140
140
  def default_server(username)
141
- case username
142
- when /@gmail\.com/
143
- 'imap.gmail.com'
144
- when /@fastmail\.fm/
145
- 'mail.messagingengine.com'
141
+ provider = Email::Provider.for_address(username)
142
+ if provider.provider == :default
143
+ puts "Can't decide provider for email address '#{username}'"
144
+ return nil
146
145
  end
146
+ provider.host
147
147
  end
148
148
  end
149
149
  end
@@ -27,9 +27,6 @@ module Imap::Backup
27
27
  end
28
28
 
29
29
  def save
30
- if File.directory?(path)
31
- Utils.check_permissions path, 0700
32
- end
33
30
  mkdir_private path
34
31
  remove_modified_flags
35
32
  remove_deleted_accounts
@@ -1,6 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'rubygems' if RUBY_VERSION < '1.9'
3
- require 'json'
4
2
 
5
3
  module Imap::Backup
6
4
  class Downloader
@@ -33,6 +33,10 @@ module Imap::Backup
33
33
  Imap::Backup.logger.debug "[#{folder}] message #{uid} already downloaded - skipping"
34
34
  return
35
35
  end
36
+
37
+ # invalidate cache
38
+ @uids = nil
39
+
36
40
  body = message['RFC822']
37
41
  mboxrd_message = Email::Mboxrd::Message.new(body)
38
42
  mbox = imap = nil
@@ -2,7 +2,7 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 1
5
- MINOR = 0
6
- REVISION = 17
5
+ MINOR = 1
6
+ REVISION = 0
7
7
  VERSION = [MAJOR, MINOR, REVISION].map(&:to_s).join('.')
8
8
  end
data/spec/spec_helper.rb CHANGED
@@ -1,18 +1,17 @@
1
+ require "codeclimate-test-reporter"
1
2
  require 'rspec'
3
+
4
+ CodeClimate::TestReporter.start
5
+
2
6
  spec_path = File.dirname(__FILE__)
3
7
  $LOAD_PATH << File.expand_path('../lib', spec_path)
4
8
 
5
9
  support_glob = File.join(spec_path, 'support', '**', '*.rb')
6
10
  Dir[support_glob].each { |f| require f }
7
11
 
8
- if RUBY_VERSION < '1.9'
9
- require 'rspec/autorun'
10
- else
11
- require 'simplecov'
12
- SimpleCov.start do
13
- add_filter '/spec/'
14
- add_filter '/vendor/'
15
- end
12
+ require 'simplecov'
13
+ SimpleCov.start do
14
+ add_filter '/spec/'
16
15
  end
17
16
 
18
17
  require 'imap/backup'
@@ -10,7 +10,7 @@ describe Imap::Backup::Account::Connection do
10
10
  {:name => backup_folder}
11
11
  end
12
12
 
13
- let(:imap) { double('Net::IMAP', :login => nil, :list => imap_folders) }
13
+ let(:imap) { double('Net::IMAP', :login => nil, :list => imap_folders, :disconnect => nil) }
14
14
  let(:imap_folders) { [] }
15
15
  let(:options) do
16
16
  {
@@ -26,21 +26,18 @@ describe Imap::Backup::Account::Connection do
26
26
 
27
27
  before do
28
28
  allow(Net::IMAP).to receive(:new).and_return(imap)
29
+ allow(Imap::Backup::Utils).to receive(:make_folder)
29
30
  end
30
31
 
31
32
  subject { described_class.new(options) }
32
33
 
33
- shared_examples 'connects to IMAP' do |options|
34
- options ||= {}
35
- username = options[:username] || 'username@gmail.com'
36
- server = options[:server] || 'imap.gmail.com'
37
-
34
+ shared_examples 'connects to IMAP' do
38
35
  it 'sets up the IMAP connection' do
39
- expect(Net::IMAP).to have_received(:new).with(server, {:port => 993, :ssl => true})
36
+ expect(Net::IMAP).to have_received(:new)
40
37
  end
41
38
 
42
39
  it 'logs in to the imap server' do
43
- expect(imap).to have_received(:login).with(username, 'password')
40
+ expect(imap).to have_received(:login)
44
41
  end
45
42
  end
46
43
 
@@ -55,37 +52,20 @@ describe Imap::Backup::Account::Connection do
55
52
  end
56
53
  end
57
54
 
58
- context 'server' do
59
- context 'with a supplied value' do
60
- before do
61
- options.merge!(:server => 'imap.example.com')
62
- end
63
-
64
- it 'uses the supplied value' do
65
- expect(subject.server).to eq('imap.example.com')
66
- end
67
- end
68
-
69
- context 'without a supplied value' do
70
- it 'uses the guesses the value' do
71
- expect(subject.server).to eq('imap.gmail.com')
72
- end
73
- end
55
+ it 'creates the path' do
56
+ subject.username
57
+ expect(Imap::Backup::Utils).to have_received(:make_folder)
74
58
  end
75
59
  end
76
60
 
77
- [
78
- ['GMail', 'user@gmail.com', 'imap.gmail.com'],
79
- ['Fastmail', 'user@fastmail.fm', 'mail.messagingengine.com'],
80
- ].each do |service, email_username, server|
81
- context service do
82
- let(:username) { email_username }
61
+ describe '#imap' do
62
+ before { @result = subject.imap }
83
63
 
84
- before { allow(imap).to receive(:disconnect) }
85
- before { subject.disconnect }
86
-
87
- include_examples 'connects to IMAP', {:username => email_username, :server => server}
64
+ it 'returns the IMAP connection' do
65
+ expect(@result).to eq(imap)
88
66
  end
67
+
68
+ include_examples 'connects to IMAP'
89
69
  end
90
70
 
91
71
  context '#folders' do
@@ -122,19 +102,8 @@ describe Imap::Backup::Account::Connection do
122
102
  end
123
103
  end
124
104
 
125
- context '#disconnect' do
126
- before { allow(imap).to receive(:disconnect) }
127
- before { subject.disconnect }
128
-
129
- it 'disconnects from the server' do
130
- expect(imap).to have_received(:disconnect)
131
- end
132
-
133
- include_examples 'connects to IMAP'
134
- end
135
-
136
105
  context '#run_backup' do
137
- let(:folder) { double('folder') }
106
+ let(:folder) { double('folder', name: 'folder') }
138
107
  let(:serializer) { double('serializer') }
139
108
  let(:downloader) { double('downloader', :run => nil) }
140
109
 
@@ -197,4 +166,14 @@ describe Imap::Backup::Account::Connection do
197
166
  end
198
167
  end
199
168
  end
169
+
170
+ context '#disconnect' do
171
+ before { subject.disconnect }
172
+
173
+ it 'disconnects from the server' do
174
+ expect(imap).to have_received(:disconnect)
175
+ end
176
+
177
+ include_examples 'connects to IMAP'
178
+ end
200
179
  end
@@ -48,7 +48,7 @@ module Imap::Backup
48
48
  it 'requires 1 parameter' do
49
49
  expect do
50
50
  described_class.new
51
- end.to raise_error(ArgumentError, /0 for 1/)
51
+ end.to raise_error(ArgumentError, /wrong number/)
52
52
  end
53
53
 
54
54
  it 'expects a higline' do
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Email::Provider do
4
+ describe '.for_address' do
5
+ context 'known providers' do
6
+ [['gmail.com', :gmail], ['fastmail.fm', :fastmail]].each do |domain, provider|
7
+ it "recognizes #{provider}" do
8
+ address = "foo@#{domain}"
9
+ expect(described_class.for_address(address).provider).to eq(provider)
10
+ end
11
+ end
12
+ end
13
+
14
+ context 'with unknown providers' do
15
+ it 'returns a default provider' do
16
+ expect(described_class.for_address('foo@unknown.com').provider).to eq(:default)
17
+ end
18
+ end
19
+ end
20
+
21
+ subject { described_class.new(:gmail) }
22
+
23
+ describe '#options' do
24
+ it 'returns options' do
25
+ expect(subject.options).to eq(port: 993, ssl: true)
26
+ end
27
+ end
28
+
29
+ describe '#host' do
30
+ it 'returns host' do
31
+ expect(subject.host).to eq('imap.gmail.com')
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.17
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-14 00:00:00.000000000 Z
11
+ date: 2016-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codeclimate-test-reporter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.4.8
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.4.8
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -97,6 +111,7 @@ files:
97
111
  - bin/imap-backup
98
112
  - imap-backup.gemspec
99
113
  - lib/email/mboxrd/message.rb
114
+ - lib/email/provider.rb
100
115
  - lib/imap/backup.rb
101
116
  - lib/imap/backup/account/connection.rb
102
117
  - lib/imap/backup/account/folder.rb
@@ -129,6 +144,7 @@ files:
129
144
  - spec/unit/configuration/store_spec.rb
130
145
  - spec/unit/downloader_spec.rb
131
146
  - spec/unit/email/mboxrd/message_spec.rb
147
+ - spec/unit/email/provider_spec.rb
132
148
  - spec/unit/serializer/base_spec.rb
133
149
  - spec/unit/serializer/directory_spec.rb
134
150
  - spec/unit/serializer/mbox_spec.rb
@@ -152,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
168
  version: '0'
153
169
  requirements: []
154
170
  rubyforge_project:
155
- rubygems_version: 2.2.2
171
+ rubygems_version: 2.5.1
156
172
  signing_key:
157
173
  specification_version: 4
158
174
  summary: Backup GMail (or other IMAP) accounts to disk
@@ -173,6 +189,7 @@ test_files:
173
189
  - spec/unit/configuration/store_spec.rb
174
190
  - spec/unit/downloader_spec.rb
175
191
  - spec/unit/email/mboxrd/message_spec.rb
192
+ - spec/unit/email/provider_spec.rb
176
193
  - spec/unit/serializer/base_spec.rb
177
194
  - spec/unit/serializer/directory_spec.rb
178
195
  - spec/unit/serializer/mbox_spec.rb