imap-backup 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +1 -0
  3. data/Rakefile +0 -5
  4. data/imap-backup.gemspec +5 -5
  5. data/lib/email/mboxrd/message.rb +1 -1
  6. data/lib/imap/backup.rb +3 -1
  7. data/lib/imap/backup/account/connection.rb +9 -7
  8. data/lib/imap/backup/account/folder.rb +6 -4
  9. data/lib/imap/backup/configuration/account.rb +9 -7
  10. data/lib/imap/backup/configuration/asker.rb +7 -5
  11. data/lib/imap/backup/configuration/connection_tester.rb +5 -3
  12. data/lib/imap/backup/configuration/folder_chooser.rb +10 -8
  13. data/lib/imap/backup/configuration/list.rb +8 -6
  14. data/lib/imap/backup/configuration/setup.rb +7 -5
  15. data/lib/imap/backup/configuration/store.rb +7 -5
  16. data/lib/imap/backup/serializer/base.rb +2 -1
  17. data/lib/imap/backup/serializer/directory.rb +5 -3
  18. data/lib/imap/backup/serializer/mbox.rb +10 -5
  19. data/lib/imap/backup/utils.rb +23 -30
  20. data/lib/imap/backup/version.rb +7 -7
  21. data/spec/spec_helper.rb +10 -27
  22. data/spec/support/higline_test_helpers.rb +8 -0
  23. data/spec/support/silence_logging.rb +7 -0
  24. data/spec/unit/account/connection_spec.rb +1 -1
  25. data/spec/unit/account/folder_spec.rb +33 -52
  26. data/spec/unit/configuration/asker_spec.rb +46 -5
  27. data/spec/unit/configuration/folder_chooser_spec.rb +62 -102
  28. data/spec/unit/configuration/list_spec.rb +40 -48
  29. data/spec/unit/configuration/setup_spec.rb +41 -62
  30. data/spec/unit/configuration/store_spec.rb +107 -99
  31. data/spec/unit/downloader_spec.rb +8 -8
  32. data/spec/unit/email/mboxrd/message_spec.rb +9 -19
  33. data/spec/unit/serializer/base_spec.rb +7 -5
  34. data/spec/unit/serializer/directory_spec.rb +30 -38
  35. data/spec/unit/serializer/mbox_spec.rb +64 -58
  36. data/spec/unit/utils_spec.rb +67 -41
  37. metadata +107 -141
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21c07957777ccdca22ec2b9b98aa5ee23e03f9d4
4
+ data.tar.gz: 7f341ff59529f01e6d7c4ccb7eefa73610b68a57
5
+ SHA512:
6
+ metadata.gz: d9cbe70b6dbe617645a4a3fadbaa75a9fa73870b889d361adeb9a3cd9bcee91143736327db1050e6dc4c9383fabe9161413bb6061b5ce26831ed1c0897fa7c2f
7
+ data.tar.gz: ce740410c22cfea3f5119cd7a65196db97296d56416b662b575f049bba4e45669a66bccca78af52dcb915ab51ced4690e760246237df1ec85aa3ec10c7fb3669
@@ -4,4 +4,5 @@ rvm:
4
4
  - 1.9.2
5
5
  - 1.9.3
6
6
  - 2.0.0
7
+ - 2.1.1
7
8
  script: "bundle exec rake spec"
data/Rakefile CHANGED
@@ -14,9 +14,4 @@ if RUBY_VERSION < '1.9'
14
14
  t.rcov = true
15
15
  t.rcov_opts = ['--exclude', 'spec/,/gems/,vendor/']
16
16
  end
17
- else
18
- desc 'Run specs and create coverage output'
19
- RSpec::Core::RakeTask.new('spec:coverage') do |t|
20
- t.pattern = ['spec/gather_rspec_coverage.rb', 'spec/**/*_spec.rb']
21
- end
22
17
  end
@@ -1,18 +1,18 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.unshift(File.expand_path('../lib/', __FILE__))
2
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
3
3
  require 'imap/backup/version'
4
4
 
5
5
  Gem::Specification.new do |gem|
6
- gem.authors = ['Joe Yates']
7
- gem.email = ['joe.g.yates@gmail.com']
6
+ gem.name = 'imap-backup'
8
7
  gem.description = %q{Backup GMail, or any other IMAP email service, to disk.}
9
8
  gem.summary = %q{Backup GMail (or other IMAP) accounts to disk}
9
+ gem.authors = ['Joe Yates']
10
+ gem.email = ['joe.g.yates@gmail.com']
10
11
  gem.homepage = 'https://github.com/joeyates/imap-backup'
11
12
 
12
13
  gem.files = `git ls-files`.split($\)
13
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
15
  gem.test_files = gem.files.grep(%r{^spec/})
15
- gem.name = 'imap-backup'
16
16
  gem.require_paths = ['lib']
17
17
  gem.version = Imap::Backup::VERSION
18
18
 
@@ -25,7 +25,7 @@ module Email::Mboxrd
25
25
 
26
26
  def body
27
27
  mbox = @body.gsub(/\n(>*From)/, "\n>\\1")
28
- mbox + "\n" unless mbox.end_with?("\n")
28
+ mbox += "\n" unless mbox.end_with?("\n")
29
29
  mbox
30
30
  end
31
31
 
@@ -1,3 +1,5 @@
1
+ module Imap; end
2
+
1
3
  require 'imap/backup/utils'
2
4
  require 'imap/backup/account/connection'
3
5
  require 'imap/backup/account/folder'
@@ -30,6 +32,6 @@ module Imap::Backup
30
32
  end
31
33
 
32
34
  def self.logger
33
- Imap::Backup::Logger.instance.logger
35
+ Logger.instance.logger
34
36
  end
35
37
  end
@@ -1,7 +1,9 @@
1
1
  require 'net/imap'
2
2
 
3
- module Imap::Backup::Account
4
- class Connection
3
+ module Imap::Backup
4
+ module Account; end
5
+
6
+ class Account::Connection
5
7
  attr_reader :username, :local_path, :backup_folders, :server
6
8
 
7
9
  def initialize(options)
@@ -17,17 +19,17 @@ module Imap::Backup::Account
17
19
 
18
20
  def status
19
21
  backup_folders.map do |folder|
20
- f = Imap::Backup::Account::Folder.new(self, folder[:name])
21
- s = Imap::Backup::Serializer::Directory.new(local_path, folder[:name])
22
+ f = Account::Folder.new(self, folder[:name])
23
+ s = Serializer::Directory.new(local_path, folder[:name])
22
24
  {:name => folder[:name], :local => s.uids, :remote => f.uids}
23
25
  end
24
26
  end
25
27
 
26
28
  def run_backup
27
29
  backup_folders.each do |folder|
28
- f = Imap::Backup::Account::Folder.new(self, folder[:name])
29
- s = Imap::Backup::Serializer::Mbox.new(local_path, folder[:name])
30
- d = Imap::Backup::Downloader.new(f, s)
30
+ f = Account::Folder.new(self, folder[:name])
31
+ s = Serializer::Mbox.new(local_path, folder[:name])
32
+ d = Downloader.new(f, s)
31
33
  d.run
32
34
  end
33
35
  end
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Account
4
- class Folder
3
+ module Imap::Backup
4
+ module Account; end
5
+
6
+ class Account::Folder
5
7
  REQUESTED_ATTRIBUTES = ['RFC822', 'FLAGS', 'INTERNALDATE']
6
8
 
7
9
  def initialize(connection, folder)
@@ -12,7 +14,7 @@ module Imap::Backup::Account
12
14
  @connection.imap.examine(@folder)
13
15
  @connection.imap.uid_search(['ALL']).sort
14
16
  rescue Net::IMAP::NoResponseError => e
15
- warn "Folder '#{@folder}' does not exist"
17
+ Imap::Backup.logger.warn "Folder '#{@folder}' does not exist"
16
18
  []
17
19
  end
18
20
 
@@ -22,7 +24,7 @@ module Imap::Backup::Account
22
24
  message['RFC822'].force_encoding('utf-8') if RUBY_VERSION > '1.9'
23
25
  message
24
26
  rescue Net::IMAP::NoResponseError => e
25
- warn "Folder '#{@folder}' does not exist"
27
+ Imap::Backup.logger.warn "Folder '#{@folder}' does not exist"
26
28
  nil
27
29
  end
28
30
  end
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Configuration
4
- class Account < Struct.new(:store, :account, :highline)
3
+ module Imap::Backup
4
+ module Configuration; end
5
+
6
+ class Configuration::Account < Struct.new(:store, :account, :highline)
5
7
  def run
6
8
  catch :done do
7
9
  loop do
@@ -41,7 +43,7 @@ Account:
41
43
 
42
44
  def modify_email(menu)
43
45
  menu.choice('modify email') do
44
- username = Asker.email(username)
46
+ username = Configuration::Asker.email(username)
45
47
  puts "username: #{username}"
46
48
  others = store.data[:accounts].select { |a| a != account}.map { |a| a[:username] }
47
49
  puts "others: #{others.inspect}"
@@ -58,7 +60,7 @@ Account:
58
60
 
59
61
  def modify_password(menu)
60
62
  menu.choice('modify password') do
61
- password = Asker.password
63
+ password = Configuration::Asker.password
62
64
  if ! password.nil?
63
65
  account[:password] = password
64
66
  end
@@ -87,19 +89,19 @@ Account:
87
89
  true
88
90
  end
89
91
  end
90
- account[:local_path] = Asker.backup_path(account[:local_path], validator)
92
+ account[:local_path] = Configuration::Asker.backup_path(account[:local_path], validator)
91
93
  end
92
94
  end
93
95
 
94
96
  def choose_folders(menu)
95
97
  menu.choice('choose backup folders') do
96
- FolderChooser.new(account).run
98
+ Configuration::FolderChooser.new(account).run
97
99
  end
98
100
  end
99
101
 
100
102
  def test_connection(menu)
101
103
  menu.choice('test connection') do
102
- result = ConnectionTester.test(account)
104
+ result = Configuration::ConnectionTester.test(account)
103
105
  puts result
104
106
  highline.ask 'Press a key '
105
107
  end
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Configuration
4
- class Asker < Struct.new(:highline)
3
+ module Imap::Backup
4
+ module Configuration; end
5
+
6
+ class Configuration::Asker < Struct.new(:highline)
5
7
  EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
6
8
 
7
9
  def email(default = '')
@@ -33,15 +35,15 @@ module Imap::Backup::Configuration
33
35
  end
34
36
 
35
37
  def self.email(default = '')
36
- new(Setup.highline).email(default)
38
+ new(Configuration::Setup.highline).email(default)
37
39
  end
38
40
 
39
41
  def self.password
40
- new(Setup.highline).password
42
+ new(Configuration::Setup.highline).password
41
43
  end
42
44
 
43
45
  def self.backup_path(default, validator)
44
- new(Setup.highline).backup_path(default, validator)
46
+ new(Configuration::Setup.highline).backup_path(default, validator)
45
47
  end
46
48
  end
47
49
  end
@@ -1,9 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Configuration
4
- module ConnectionTester
3
+ module Imap::Backup
4
+ module Configuration; end
5
+
6
+ module Configuration::ConnectionTester
5
7
  def self.test(account)
6
- Imap::Backup::Account::Connection.new(account).imap
8
+ Account::Connection.new(account).imap
7
9
  return 'Connection successful'
8
10
  rescue Net::IMAP::NoResponseError
9
11
  return 'No response'
@@ -1,28 +1,30 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Configuration
4
- class FolderChooser
3
+ module Imap::Backup
4
+ module Configuration; end
5
+
6
+ class Configuration::FolderChooser
5
7
  def initialize(account)
6
8
  @account = account
7
9
  end
8
10
 
9
11
  def run
10
12
  begin
11
- @connection = Imap::Backup::Account::Connection.new(@account)
13
+ @connection = Account::Connection.new(@account)
12
14
  rescue => e
13
- puts 'Connection failed'
14
- Setup.highline.ask 'Press a key '
15
+ Imap::Backup.logger.warn 'Connection failed'
16
+ Configuration::Setup.highline.ask 'Press a key '
15
17
  return
16
18
  end
17
19
  @folders = @connection.folders
18
20
  if @folders.nil?
19
- puts 'Unable to get folder list'
20
- Setup.highline.ask 'Press a key '
21
+ Imap::Backup.logger.warn 'Unable to get folder list'
22
+ Configuration::Setup.highline.ask 'Press a key '
21
23
  return
22
24
  end
23
25
  loop do
24
26
  system('clear')
25
- Setup.highline.choose do |menu|
27
+ Configuration::Setup.highline.choose do |menu|
26
28
  menu.header = 'Add/remove folders'
27
29
  menu.index = :number
28
30
  @folders.each do |folder|
@@ -1,14 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap::Backup::Configuration
4
- class List
3
+ module Imap::Backup
4
+ module Configuration; end
5
+
6
+ class Configuration::List
5
7
  attr_reader :accounts
6
8
 
7
9
  def initialize(accounts = nil)
8
- if not Imap::Backup::Configuration::Store.exist?
9
- raise Imap::Backup::ConfigurationNotFound.new("Configuration file '#{Imap::Backup::Configuration::Store.default_pathname}' not found")
10
+ if not Configuration::Store.exist?
11
+ raise ConfigurationNotFound.new("Configuration file '#{Configuration::Store.default_pathname}' not found")
10
12
  end
11
- @config = Imap::Backup::Configuration::Store.new
13
+ @config = Configuration::Store.new
12
14
 
13
15
  if accounts.nil?
14
16
  @accounts = @config.data[:accounts]
@@ -19,7 +21,7 @@ module Imap::Backup::Configuration
19
21
 
20
22
  def each_connection
21
23
  @accounts.each do |account|
22
- connection = Imap::Backup::Account::Connection.new(account)
24
+ connection = Account::Connection.new(account)
23
25
  yield connection
24
26
  connection.disconnect
25
27
  end
@@ -2,8 +2,10 @@
2
2
  require 'rubygems' if RUBY_VERSION < '1.9'
3
3
  require 'highline'
4
4
 
5
- module Imap::Backup::Configuration
6
- class Setup
5
+ module Imap::Backup
6
+ module Configuration; end
7
+
8
+ class Configuration::Setup
7
9
  class << self
8
10
  attr_accessor :highline
9
11
  end
@@ -21,7 +23,7 @@ module Imap::Backup::Configuration
21
23
  end
22
24
  end
23
25
  menu.choice('add account') do
24
- username = Asker.email
26
+ username = Configuration::Asker.email
25
27
  edit_account username
26
28
  end
27
29
  menu.choice('save and exit') do
@@ -38,7 +40,7 @@ module Imap::Backup::Configuration
38
40
  private
39
41
 
40
42
  def config
41
- @config ||= Imap::Backup::Configuration::Store.new
43
+ @config ||= Configuration::Store.new
42
44
  end
43
45
 
44
46
  def setup_logging
@@ -66,7 +68,7 @@ module Imap::Backup::Configuration
66
68
  if account.nil?
67
69
  account = add_account(username)
68
70
  end
69
- Account.new(config, account, Imap::Backup::Configuration::Setup.highline).run
71
+ Configuration::Account.new(config, account, Configuration::Setup.highline).run
70
72
  end
71
73
  end
72
74
  end
@@ -2,8 +2,10 @@
2
2
  require 'rubygems' if RUBY_VERSION < '1.9'
3
3
  require 'json'
4
4
 
5
- module Imap::Backup::Configuration
6
- class Store
5
+ module Imap::Backup
6
+ module Configuration; end
7
+
8
+ class Configuration::Store
7
9
  CONFIGURATION_DIRECTORY = File.expand_path('~/.imap-backup')
8
10
 
9
11
  attr_reader :data
@@ -20,10 +22,10 @@ module Imap::Backup::Configuration
20
22
  def initialize(pathname = self.class.default_pathname)
21
23
  @pathname = pathname
22
24
  if File.directory?(path)
23
- Imap::Backup::Utils.check_permissions path, 0700
25
+ Utils.check_permissions path, 0700
24
26
  end
25
27
  if File.exist?(@pathname)
26
- Imap::Backup::Utils.check_permissions @pathname, 0600
28
+ Utils.check_permissions @pathname, 0600
27
29
  @data = JSON.parse(File.read(@pathname), :symbolize_names => true)
28
30
  else
29
31
  @data = {:accounts => []}
@@ -57,7 +59,7 @@ module Imap::Backup::Configuration
57
59
  if ! File.directory?(path)
58
60
  FileUtils.mkdir path
59
61
  end
60
- if Imap::Backup::Utils::stat(path) != 0700
62
+ if Utils::stat(path) != 0700
61
63
  FileUtils.chmod 0700, path
62
64
  end
63
65
  end
@@ -4,10 +4,11 @@ module Imap::Backup
4
4
  module Serializer
5
5
  DIRECTORY_PERMISSIONS = 0700
6
6
  FILE_PERMISSIONS = 0600
7
+
7
8
  class Base
8
9
  def initialize(path, folder)
9
10
  @path, @folder = path, folder
10
- Imap::Backup::Utils.check_permissions(@path, DIRECTORY_PERMISSIONS)
11
+ Utils.check_permissions(@path, DIRECTORY_PERMISSIONS)
11
12
  end
12
13
  end
13
14
  end
@@ -1,11 +1,13 @@
1
1
  # encoding: utf-8
2
2
  require 'fileutils'
3
3
 
4
- module Imap::Backup::Serializer
5
- class Directory < Base
4
+ module Imap::Backup
5
+ module Serializer; end
6
+
7
+ class Serializer::Directory < Serializer::Base
6
8
  def initialize(path, folder)
7
9
  super
8
- Imap::Backup::Utils.make_folder(@path, @folder, DIRECTORY_PERMISSIONS)
10
+ Utils.make_folder(@path, @folder, Serializer::DIRECTORY_PERMISSIONS)
9
11
  end
10
12
 
11
13
  def uids
@@ -2,8 +2,10 @@
2
2
  require 'csv'
3
3
  require 'email/mboxrd/message'
4
4
 
5
- module Imap::Backup::Serializer
6
- class Mbox < Base
5
+ module Imap::Backup
6
+ module Serializer; end
7
+
8
+ class Serializer::Mbox < Serializer::Base
7
9
  def initialize(path, folder)
8
10
  super
9
11
  create_containing_directory
@@ -27,13 +29,16 @@ module Imap::Backup::Serializer
27
29
  def save(uid, message)
28
30
  uid = uid.to_s
29
31
  return if uids.include?(uid)
30
- message = Email::Mboxrd::Message.new(message['RFC822'])
32
+ body = message['RFC822']
33
+ mboxrd_message = Email::Mboxrd::Message.new(body)
31
34
  mbox = imap = nil
32
35
  begin
33
36
  mbox = File.open(mbox_pathname, 'ab')
34
37
  imap = File.open(imap_pathname, 'ab')
35
- mbox.write message.to_s
38
+ mbox.write mboxrd_message.to_s
36
39
  imap.write uid + "\n"
40
+ rescue ArgumentError => e
41
+ Imap::Backup.logger.warn "Failed to save message #{uid}:\n#{body}. #{e}"
37
42
  ensure
38
43
  mbox.close if mbox
39
44
  imap.close if imap
@@ -52,7 +57,7 @@ module Imap::Backup::Serializer
52
57
  def create_containing_directory
53
58
  mbox_relative_path = File.dirname(mbox_relative_pathname)
54
59
  return if mbox_relative_path == '.'
55
- Imap::Backup::Utils.make_folder(@path, mbox_relative_path, DIRECTORY_PERMISSIONS)
60
+ Utils.make_folder(@path, mbox_relative_path, Serializer::DIRECTORY_PERMISSIONS)
56
61
  end
57
62
 
58
63
  def exist?