imap-backup 1.3.0 → 1.4.0
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/.gitignore +1 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +2 -1
 - data/.travis.yml +1 -1
 - data/README.md +12 -0
 - data/bin/imap-backup +11 -6
 - data/docker-compose.yml +15 -0
 - data/imap-backup.gemspec +3 -2
 - data/lib/email/mboxrd/message.rb +30 -4
 - data/lib/email/provider.rb +2 -2
 - data/lib/imap/backup.rb +0 -1
 - data/lib/imap/backup/account/connection.rb +29 -15
 - data/lib/imap/backup/account/folder.rb +3 -3
 - data/lib/imap/backup/configuration/account.rb +15 -12
 - data/lib/imap/backup/configuration/asker.rb +3 -1
 - data/lib/imap/backup/configuration/folder_chooser.rb +5 -2
 - data/lib/imap/backup/configuration/list.rb +12 -9
 - data/lib/imap/backup/configuration/setup.rb +5 -3
 - data/lib/imap/backup/configuration/store.rb +4 -4
 - data/lib/imap/backup/downloader.rb +6 -2
 - data/lib/imap/backup/serializer/base.rb +2 -2
 - data/lib/imap/backup/serializer/mbox.rb +16 -7
 - data/lib/imap/backup/utils.rb +8 -3
 - data/lib/imap/backup/version.rb +1 -1
 - data/spec/features/backup_spec.rb +27 -0
 - data/spec/features/helper.rb +2 -0
 - data/spec/features/support/backup_directory.rb +27 -0
 - data/spec/features/support/email_server.rb +40 -0
 - data/spec/features/support/shared/connection_context.rb +12 -0
 - data/spec/features/support/shared/message_fixtures.rb +6 -0
 - data/spec/fixtures/connection.yml +7 -0
 - data/spec/support/fixtures.rb +6 -0
 - data/spec/unit/account/connection_spec.rb +19 -7
 - data/spec/unit/account/folder_spec.rb +15 -5
 - data/spec/unit/configuration/account_spec.rb +18 -8
 - data/spec/unit/configuration/asker_spec.rb +6 -3
 - data/spec/unit/configuration/connection_tester_spec.rb +1 -1
 - data/spec/unit/configuration/folder_chooser_spec.rb +21 -11
 - data/spec/unit/configuration/list_spec.rb +13 -6
 - data/spec/unit/configuration/setup_spec.rb +5 -3
 - data/spec/unit/configuration/store_spec.rb +13 -8
 - data/spec/unit/downloader_spec.rb +3 -1
 - data/spec/unit/email/mboxrd/message_spec.rb +32 -13
 - data/spec/unit/email/provider_spec.rb +7 -3
 - data/spec/unit/serializer/base_spec.rb +3 -2
 - data/spec/unit/serializer/mbox_spec.rb +9 -6
 - data/spec/unit/utils_spec.rb +15 -13
 - metadata +35 -5
 - data/lib/imap/backup/serializer/directory.rb +0 -43
 - data/spec/unit/serializer/directory_spec.rb +0 -69
 
| 
         @@ -9,7 +9,7 @@ module Imap::Backup 
     | 
|
| 
       9 
9 
     | 
    
         
             
                end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                def setup_logging
         
     | 
| 
       12 
     | 
    
         
            -
                  return if  
     | 
| 
      
 12 
     | 
    
         
            +
                  return if !config_exists?
         
     | 
| 
       13 
13 
     | 
    
         
             
                  Imap::Backup.setup_logging config
         
     | 
| 
       14 
14 
     | 
    
         
             
                end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
         @@ -25,8 +25,9 @@ module Imap::Backup 
     | 
|
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                def config
         
     | 
| 
       27 
27 
     | 
    
         
             
                  return @config if @config
         
     | 
| 
       28 
     | 
    
         
            -
                  if  
     | 
| 
       29 
     | 
    
         
            -
                     
     | 
| 
      
 28 
     | 
    
         
            +
                  if !config_exists?
         
     | 
| 
      
 29 
     | 
    
         
            +
                    path = Configuration::Store.default_pathname
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise ConfigurationNotFound, "Configuration file '#{path}' not found"
         
     | 
| 
       30 
31 
     | 
    
         
             
                  end
         
     | 
| 
       31 
32 
     | 
    
         
             
                  @config = Configuration::Store.new
         
     | 
| 
       32 
33 
     | 
    
         
             
                end
         
     | 
| 
         @@ -36,12 +37,14 @@ module Imap::Backup 
     | 
|
| 
       36 
37 
     | 
    
         
             
                end
         
     | 
| 
       37 
38 
     | 
    
         | 
| 
       38 
39 
     | 
    
         
             
                def accounts
         
     | 
| 
       39 
     | 
    
         
            -
                   
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
                  @accounts ||=
         
     | 
| 
      
 41 
     | 
    
         
            +
                    if required_accounts.nil?
         
     | 
| 
      
 42 
     | 
    
         
            +
                      config.accounts
         
     | 
| 
      
 43 
     | 
    
         
            +
                    else
         
     | 
| 
      
 44 
     | 
    
         
            +
                      config.accounts.select do |account|
         
     | 
| 
      
 45 
     | 
    
         
            +
                        required_accounts.include?(account[:username])
         
     | 
| 
      
 46 
     | 
    
         
            +
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
       45 
48 
     | 
    
         
             
                end
         
     | 
| 
       46 
49 
     | 
    
         
             
              end
         
     | 
| 
       47 
50 
     | 
    
         
             
            end
         
     | 
| 
         @@ -69,10 +69,10 @@ module Imap::Backup 
     | 
|
| 
       69 
69 
     | 
    
         
             
                end
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                def default_account_config(username)
         
     | 
| 
       72 
     | 
    
         
            -
                   
     | 
| 
      
 72 
     | 
    
         
            +
                  {
         
     | 
| 
       73 
73 
     | 
    
         
             
                    username: username,
         
     | 
| 
       74 
74 
     | 
    
         
             
                    password: "",
         
     | 
| 
       75 
     | 
    
         
            -
                    local_path: File.join(config.path, username. 
     | 
| 
      
 75 
     | 
    
         
            +
                    local_path: File.join(config.path, username.tr("@", "_")),
         
     | 
| 
       76 
76 
     | 
    
         
             
                    folders: []
         
     | 
| 
       77 
77 
     | 
    
         
             
                  }
         
     | 
| 
       78 
78 
     | 
    
         
             
                end
         
     | 
| 
         @@ -83,7 +83,9 @@ module Imap::Backup 
     | 
|
| 
       83 
83 
     | 
    
         
             
                    account = default_account_config(username)
         
     | 
| 
       84 
84 
     | 
    
         
             
                    config.accounts << account
         
     | 
| 
       85 
85 
     | 
    
         
             
                  end
         
     | 
| 
       86 
     | 
    
         
            -
                  Configuration::Account.new( 
     | 
| 
      
 86 
     | 
    
         
            +
                  Configuration::Account.new(
         
     | 
| 
      
 87 
     | 
    
         
            +
                    config, account, Configuration::Setup.highline
         
     | 
| 
      
 88 
     | 
    
         
            +
                  ).run
         
     | 
| 
       87 
89 
     | 
    
         
             
                end
         
     | 
| 
       88 
90 
     | 
    
         
             
              end
         
     | 
| 
       89 
91 
     | 
    
         
             
            end
         
     | 
| 
         @@ -29,7 +29,7 @@ module Imap::Backup 
     | 
|
| 
       29 
29 
     | 
    
         
             
                  remove_modified_flags
         
     | 
| 
       30 
30 
     | 
    
         
             
                  remove_deleted_accounts
         
     | 
| 
       31 
31 
     | 
    
         
             
                  File.open(pathname, "w") { |f| f.write(JSON.pretty_generate(data)) }
         
     | 
| 
       32 
     | 
    
         
            -
                  FileUtils.chmod  
     | 
| 
      
 32 
     | 
    
         
            +
                  FileUtils.chmod 0o600, pathname
         
     | 
| 
       33 
33 
     | 
    
         
             
                end
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                def accounts
         
     | 
| 
         @@ -53,7 +53,7 @@ module Imap::Backup 
     | 
|
| 
       53 
53 
     | 
    
         
             
                def data
         
     | 
| 
       54 
54 
     | 
    
         
             
                  return @data if @data
         
     | 
| 
       55 
55 
     | 
    
         
             
                  if File.exist?(pathname)
         
     | 
| 
       56 
     | 
    
         
            -
                    Utils.check_permissions pathname,  
     | 
| 
      
 56 
     | 
    
         
            +
                    Utils.check_permissions pathname, 0o600
         
     | 
| 
       57 
57 
     | 
    
         
             
                    contents = File.read(pathname)
         
     | 
| 
       58 
58 
     | 
    
         
             
                    @data = JSON.parse(contents, symbolize_names: true)
         
     | 
| 
       59 
59 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -76,8 +76,8 @@ module Imap::Backup 
     | 
|
| 
       76 
76 
     | 
    
         
             
                  if !File.directory?(path)
         
     | 
| 
       77 
77 
     | 
    
         
             
                    FileUtils.mkdir path
         
     | 
| 
       78 
78 
     | 
    
         
             
                  end
         
     | 
| 
       79 
     | 
    
         
            -
                  if Utils::stat(path) !=  
     | 
| 
       80 
     | 
    
         
            -
                    FileUtils.chmod  
     | 
| 
      
 79 
     | 
    
         
            +
                  if Utils::stat(path) != 0o700
         
     | 
| 
      
 80 
     | 
    
         
            +
                    FileUtils.chmod 0o700, path
         
     | 
| 
       81 
81 
     | 
    
         
             
                  end
         
     | 
| 
       82 
82 
     | 
    
         
             
                end
         
     | 
| 
       83 
83 
     | 
    
         
             
              end
         
     | 
| 
         @@ -13,10 +13,14 @@ module Imap::Backup 
     | 
|
| 
       13 
13 
     | 
    
         
             
                  uids.each do |uid|
         
     | 
| 
       14 
14 
     | 
    
         
             
                    message = folder.fetch(uid)
         
     | 
| 
       15 
15 
     | 
    
         
             
                    if message.nil?
         
     | 
| 
       16 
     | 
    
         
            -
                      Imap::Backup.logger.debug 
     | 
| 
      
 16 
     | 
    
         
            +
                      Imap::Backup.logger.debug(
         
     | 
| 
      
 17 
     | 
    
         
            +
                        "[#{folder.name}] #{uid} - not available - skipped"
         
     | 
| 
      
 18 
     | 
    
         
            +
                      )
         
     | 
| 
       17 
19 
     | 
    
         
             
                      next
         
     | 
| 
       18 
20 
     | 
    
         
             
                    end
         
     | 
| 
       19 
     | 
    
         
            -
                    Imap::Backup.logger.debug 
     | 
| 
      
 21 
     | 
    
         
            +
                    Imap::Backup.logger.debug(
         
     | 
| 
      
 22 
     | 
    
         
            +
                      "[#{folder.name}] #{uid} - #{message['RFC822'].size} bytes"
         
     | 
| 
      
 23 
     | 
    
         
            +
                    )
         
     | 
| 
       20 
24 
     | 
    
         
             
                    serializer.save(uid, message)
         
     | 
| 
       21 
25 
     | 
    
         
             
                  end
         
     | 
| 
       22 
26 
     | 
    
         
             
                end
         
     | 
| 
         @@ -17,7 +17,7 @@ module Imap::Backup 
     | 
|
| 
       17 
17 
     | 
    
         
             
                  return @uids if @uids
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
                  @uids = []
         
     | 
| 
       20 
     | 
    
         
            -
                  return @uids if  
     | 
| 
      
 20 
     | 
    
         
            +
                  return @uids if !exist?
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                  CSV.foreach(imap_pathname) do |row|
         
     | 
| 
       23 
23 
     | 
    
         
             
                    @uids << row[0]
         
     | 
| 
         @@ -29,7 +29,9 @@ module Imap::Backup 
     | 
|
| 
       29 
29 
     | 
    
         
             
                def save(uid, message)
         
     | 
| 
       30 
30 
     | 
    
         
             
                  uid = uid.to_s
         
     | 
| 
       31 
31 
     | 
    
         
             
                  if uids.include?(uid)
         
     | 
| 
       32 
     | 
    
         
            -
                    Imap::Backup.logger.debug 
     | 
| 
      
 32 
     | 
    
         
            +
                    Imap::Backup.logger.debug(
         
     | 
| 
      
 33 
     | 
    
         
            +
                      "[#{folder}] message #{uid} already downloaded - skipping"
         
     | 
| 
      
 34 
     | 
    
         
            +
                    )
         
     | 
| 
       33 
35 
     | 
    
         
             
                    return
         
     | 
| 
       34 
36 
     | 
    
         
             
                  end
         
     | 
| 
       35 
37 
     | 
    
         | 
| 
         @@ -42,10 +44,15 @@ module Imap::Backup 
     | 
|
| 
       42 
44 
     | 
    
         
             
                  begin
         
     | 
| 
       43 
45 
     | 
    
         
             
                    mbox = File.open(mbox_pathname, "ab")
         
     | 
| 
       44 
46 
     | 
    
         
             
                    imap = File.open(imap_pathname, "ab")
         
     | 
| 
       45 
     | 
    
         
            -
                    mbox.write mboxrd_message. 
     | 
| 
      
 47 
     | 
    
         
            +
                    mbox.write mboxrd_message.to_serialized
         
     | 
| 
       46 
48 
     | 
    
         
             
                    imap.write uid + "\n"
         
     | 
| 
       47 
49 
     | 
    
         
             
                  rescue => e
         
     | 
| 
       48 
     | 
    
         
            -
                     
     | 
| 
      
 50 
     | 
    
         
            +
                    message = <<-ERROR.gsub(/^\s*/m, "")
         
     | 
| 
      
 51 
     | 
    
         
            +
                      [#{folder}] failed to save message #{uid}:
         
     | 
| 
      
 52 
     | 
    
         
            +
                      #{body}. #{e}:
         
     | 
| 
      
 53 
     | 
    
         
            +
                      #{e.backtrace.join("\n")}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    ERROR
         
     | 
| 
      
 55 
     | 
    
         
            +
                    Imap::Backup.logger.warn message
         
     | 
| 
       49 
56 
     | 
    
         
             
                  ensure
         
     | 
| 
       50 
57 
     | 
    
         
             
                    mbox.close if mbox
         
     | 
| 
       51 
58 
     | 
    
         
             
                    imap.close if imap
         
     | 
| 
         @@ -57,14 +64,16 @@ module Imap::Backup 
     | 
|
| 
       57 
64 
     | 
    
         
             
                def assert_files
         
     | 
| 
       58 
65 
     | 
    
         
             
                  mbox = mbox_exist?
         
     | 
| 
       59 
66 
     | 
    
         
             
                  imap = imap_exist?
         
     | 
| 
       60 
     | 
    
         
            -
                  raise ".imap file missing" if mbox && ( 
     | 
| 
       61 
     | 
    
         
            -
                  raise ".mbox file missing" if imap && ( 
     | 
| 
      
 67 
     | 
    
         
            +
                  raise ".imap file missing" if mbox && (!imap)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  raise ".mbox file missing" if imap && (!mbox)
         
     | 
| 
       62 
69 
     | 
    
         
             
                end
         
     | 
| 
       63 
70 
     | 
    
         | 
| 
       64 
71 
     | 
    
         
             
                def create_containing_directory
         
     | 
| 
       65 
72 
     | 
    
         
             
                  mbox_relative_path = File.dirname(mbox_relative_pathname)
         
     | 
| 
       66 
73 
     | 
    
         
             
                  return if mbox_relative_path == "."
         
     | 
| 
       67 
     | 
    
         
            -
                  Utils.make_folder( 
     | 
| 
      
 74 
     | 
    
         
            +
                  Utils.make_folder(
         
     | 
| 
      
 75 
     | 
    
         
            +
                    path, mbox_relative_path, Serializer::DIRECTORY_PERMISSIONS
         
     | 
| 
      
 76 
     | 
    
         
            +
                  )
         
     | 
| 
       68 
77 
     | 
    
         
             
                end
         
     | 
| 
       69 
78 
     | 
    
         | 
| 
       70 
79 
     | 
    
         
             
                def exist?
         
     | 
    
        data/lib/imap/backup/utils.rb
    CHANGED
    
    | 
         @@ -5,9 +5,14 @@ module Imap::Backup 
     | 
|
| 
       5 
5 
     | 
    
         
             
                def self.check_permissions(filename, limit)
         
     | 
| 
       6 
6 
     | 
    
         
             
                  actual = stat(filename)
         
     | 
| 
       7 
7 
     | 
    
         
             
                  return nil if actual.nil?
         
     | 
| 
       8 
     | 
    
         
            -
                  mask = ~limit &  
     | 
| 
      
 8 
     | 
    
         
            +
                  mask = ~limit & 0o777
         
     | 
| 
       9 
9 
     | 
    
         
             
                  if actual & mask != 0
         
     | 
| 
       10 
     | 
    
         
            -
                     
     | 
| 
      
 10 
     | 
    
         
            +
                    message = format(
         
     | 
| 
      
 11 
     | 
    
         
            +
                      "Permissions on '%<filename>s' " \
         
     | 
| 
      
 12 
     | 
    
         
            +
                        "should be 0%<limit>o, not 0%<actual>o",
         
     | 
| 
      
 13 
     | 
    
         
            +
                      filename: filename, limit: limit, actual: actual
         
     | 
| 
      
 14 
     | 
    
         
            +
                    )
         
     | 
| 
      
 15 
     | 
    
         
            +
                    raise message
         
     | 
| 
       11 
16 
     | 
    
         
             
                  end
         
     | 
| 
       12 
17 
     | 
    
         
             
                end
         
     | 
| 
       13 
18 
     | 
    
         | 
| 
         @@ -15,7 +20,7 @@ module Imap::Backup 
     | 
|
| 
       15 
20 
     | 
    
         
             
                  return nil unless File.exist?(filename)
         
     | 
| 
       16 
21 
     | 
    
         | 
| 
       17 
22 
     | 
    
         
             
                  stat = File.stat(filename)
         
     | 
| 
       18 
     | 
    
         
            -
                  stat.mode &  
     | 
| 
      
 23 
     | 
    
         
            +
                  stat.mode & 0o777
         
     | 
| 
       19 
24 
     | 
    
         
             
                end
         
     | 
| 
       20 
25 
     | 
    
         | 
| 
       21 
26 
     | 
    
         
             
                def self.make_folder(base_path, path, permissions)
         
     | 
    
        data/lib/imap/backup/version.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "features/helper"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe "backup", type: :feature, docker: true do
         
     | 
| 
      
 4 
     | 
    
         
            +
              include_context "imap-backup connection"
         
     | 
| 
      
 5 
     | 
    
         
            +
              include_context "message-fixtures"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              let(:messages_as_mbox) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
              let(:folder) { "INBOX" }
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              before do
         
     | 
| 
      
 13 
     | 
    
         
            +
                send_email folder, msg1
         
     | 
| 
      
 14 
     | 
    
         
            +
                send_email folder, msg2
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              after do
         
     | 
| 
      
 18 
     | 
    
         
            +
                FileUtils.rm_rf local_backup_path
         
     | 
| 
      
 19 
     | 
    
         
            +
                delete_emails folder
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it "downloads messages" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                connection.run_backup
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                expect(mbox_content(folder)).to eq(messages_as_mbox)
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module BackupDirectoryHelpers
         
     | 
| 
      
 2 
     | 
    
         
            +
              def message_as_mbox_entry(options)
         
     | 
| 
      
 3 
     | 
    
         
            +
                from = fixture("connection")[:username]
         
     | 
| 
      
 4 
     | 
    
         
            +
                subject = options[:subject]
         
     | 
| 
      
 5 
     | 
    
         
            +
                body = options[:body]
         
     | 
| 
      
 6 
     | 
    
         
            +
                body_and_headers = <<-EOT.gsub("\n", "\r\n")
         
     | 
| 
      
 7 
     | 
    
         
            +
            From: #{from}
         
     | 
| 
      
 8 
     | 
    
         
            +
            Subject: #{subject}
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            #{body}
         
     | 
| 
      
 11 
     | 
    
         
            +
                EOT
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                "From #{from}\n#{body_and_headers}\n"
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def mbox_content(name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                File.read(mbox_path(name))
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def mbox_path(name)
         
     | 
| 
      
 21 
     | 
    
         
            +
                File.join(local_backup_path, name + ".mbox")
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 26 
     | 
    
         
            +
              config.include BackupDirectoryHelpers, type: :feature
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module EmailServerHelpers
         
     | 
| 
      
 2 
     | 
    
         
            +
              REQUESTED_ATTRIBUTES = ["RFC822", "FLAGS", "INTERNALDATE"]
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def send_email(folder, options)
         
     | 
| 
      
 5 
     | 
    
         
            +
                from = options[:from] || "address@example.org"
         
     | 
| 
      
 6 
     | 
    
         
            +
                subject = options[:subject]
         
     | 
| 
      
 7 
     | 
    
         
            +
                body = options[:body]
         
     | 
| 
      
 8 
     | 
    
         
            +
                message = <<-EOT
         
     | 
| 
      
 9 
     | 
    
         
            +
            From: #{from}
         
     | 
| 
      
 10 
     | 
    
         
            +
            Subject: #{subject}
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            #{body}
         
     | 
| 
      
 13 
     | 
    
         
            +
                EOT
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                imap.append(folder, message, nil, nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def delete_emails(folder)
         
     | 
| 
      
 19 
     | 
    
         
            +
                imap.select(folder)
         
     | 
| 
      
 20 
     | 
    
         
            +
                uids = imap.uid_search(["ALL"]).sort
         
     | 
| 
      
 21 
     | 
    
         
            +
                imap.store(1 .. uids.size, "+FLAGS", [:Deleted])
         
     | 
| 
      
 22 
     | 
    
         
            +
                imap.expunge
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def imap
         
     | 
| 
      
 26 
     | 
    
         
            +
                @imap ||=
         
     | 
| 
      
 27 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 28 
     | 
    
         
            +
                    connection = fixture("connection")
         
     | 
| 
      
 29 
     | 
    
         
            +
                    imap = Net::IMAP.new(
         
     | 
| 
      
 30 
     | 
    
         
            +
                      connection[:server], connection[:connection_options]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    )
         
     | 
| 
      
 32 
     | 
    
         
            +
                    imap.login(connection[:username], connection[:password])
         
     | 
| 
      
 33 
     | 
    
         
            +
                    imap
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 39 
     | 
    
         
            +
              config.include EmailServerHelpers, type: :feature
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            shared_context "imap-backup connection" do
         
     | 
| 
      
 2 
     | 
    
         
            +
              let(:local_backup_path) { Dir.mktmpdir(nil, "tmp") }
         
     | 
| 
      
 3 
     | 
    
         
            +
              let(:default_connection) { fixture("connection") }
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:backup_folders) { nil }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:connection_options) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                default_connection.merge({
         
     | 
| 
      
 7 
     | 
    
         
            +
                  local_path: local_backup_path,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  folders: backup_folders
         
     | 
| 
      
 9 
     | 
    
         
            +
                })
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
              let(:connection) { Imap::Backup::Account::Connection.new(connection_options) }
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -9,7 +9,9 @@ describe Imap::Backup::Account::Connection do 
     | 
|
| 
       9 
9 
     | 
    
         
             
                {name: backup_folder}
         
     | 
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              let(:imap)  
     | 
| 
      
 12 
     | 
    
         
            +
              let(:imap) do
         
     | 
| 
      
 13 
     | 
    
         
            +
                double("Net::IMAP", login: nil, disconnect: nil)
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
       13 
15 
     | 
    
         
             
              let(:imap_folders) { [] }
         
     | 
| 
       14 
16 
     | 
    
         
             
              let(:options) do
         
     | 
| 
       15 
17 
     | 
    
         
             
                {
         
     | 
| 
         @@ -22,9 +24,15 @@ describe Imap::Backup::Account::Connection do 
     | 
|
| 
       22 
24 
     | 
    
         
             
              let(:local_path) { "local_path" }
         
     | 
| 
       23 
25 
     | 
    
         
             
              let(:backup_folders) { [self.class.folder_config] }
         
     | 
| 
       24 
26 
     | 
    
         
             
              let(:username) { "username@gmail.com" }
         
     | 
| 
      
 27 
     | 
    
         
            +
              let(:root_info) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                instance_double(Net::IMAP::MailboxList, name: root_name)
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              let(:root_name) { "foo" }
         
     | 
| 
       25 
31 
     | 
    
         | 
| 
       26 
32 
     | 
    
         
             
              before do
         
     | 
| 
       27 
33 
     | 
    
         
             
                allow(Net::IMAP).to receive(:new).and_return(imap)
         
     | 
| 
      
 34 
     | 
    
         
            +
                allow(imap).to receive(:list).with("", "") { [root_info] }
         
     | 
| 
      
 35 
     | 
    
         
            +
                allow(imap).to receive(:list).with(root_name, "*") { imap_folders }
         
     | 
| 
       28 
36 
     | 
    
         
             
                allow(Imap::Backup::Utils).to receive(:make_folder)
         
     | 
| 
       29 
37 
     | 
    
         
             
              end
         
     | 
| 
       30 
38 
     | 
    
         | 
| 
         @@ -68,9 +76,9 @@ describe Imap::Backup::Account::Connection do 
     | 
|
| 
       68 
76 
     | 
    
         
             
              end
         
     | 
| 
       69 
77 
     | 
    
         | 
| 
       70 
78 
     | 
    
         
             
              context "#folders" do
         
     | 
| 
       71 
     | 
    
         
            -
                let(:imap_folders)  
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                 
     | 
| 
      
 79 
     | 
    
         
            +
                let(:imap_folders) do
         
     | 
| 
      
 80 
     | 
    
         
            +
                  [instance_double(Net::IMAP::MailboxList)]
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
       74 
82 
     | 
    
         | 
| 
       75 
83 
     | 
    
         
             
                it "returns the list of folders" do
         
     | 
| 
       76 
84 
     | 
    
         
             
                  expect(subject.folders).to eq(imap_folders)
         
     | 
| 
         @@ -80,12 +88,14 @@ describe Imap::Backup::Account::Connection do 
     | 
|
| 
       80 
88 
     | 
    
         
             
              context "#status" do
         
     | 
| 
       81 
89 
     | 
    
         
             
                let(:folder) { double("folder", uids: [remote_uid]) }
         
     | 
| 
       82 
90 
     | 
    
         
             
                let(:local_uid) { "local_uid" }
         
     | 
| 
       83 
     | 
    
         
            -
                let(:serializer)  
     | 
| 
      
 91 
     | 
    
         
            +
                let(:serializer) do
         
     | 
| 
      
 92 
     | 
    
         
            +
                  instance_double(Imap::Backup::Serializer::Mbox, uids: [local_uid])
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
       84 
94 
     | 
    
         
             
                let(:remote_uid) { "remote_uid" }
         
     | 
| 
       85 
95 
     | 
    
         | 
| 
       86 
96 
     | 
    
         
             
                before do
         
     | 
| 
       87 
97 
     | 
    
         
             
                  allow(Imap::Backup::Account::Folder).to receive(:new).and_return(folder)
         
     | 
| 
       88 
     | 
    
         
            -
                  allow(Imap::Backup::Serializer:: 
     | 
| 
      
 98 
     | 
    
         
            +
                  allow(Imap::Backup::Serializer::Mbox).to receive(:new) { serializer }
         
     | 
| 
       89 
99 
     | 
    
         
             
                end
         
     | 
| 
       90 
100 
     | 
    
         | 
| 
       91 
101 
     | 
    
         
             
                it "should return the names of folders" do
         
     | 
| 
         @@ -127,7 +137,9 @@ describe Imap::Backup::Account::Connection do 
     | 
|
| 
       127 
137 
     | 
    
         
             
                end
         
     | 
| 
       128 
138 
     | 
    
         | 
| 
       129 
139 
     | 
    
         
             
                context "without supplied backup_folders" do
         
     | 
| 
       130 
     | 
    
         
            -
                  let(:imap_folders)  
     | 
| 
      
 140 
     | 
    
         
            +
                  let(:imap_folders) do
         
     | 
| 
      
 141 
     | 
    
         
            +
                    [instance_double(Net::IMAP::MailboxList, name: "foo")]
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
       131 
143 
     | 
    
         | 
| 
       132 
144 
     | 
    
         
             
                  before do
         
     | 
| 
       133 
145 
     | 
    
         
             
                    allow(Imap::Backup::Account::Folder).to receive(:new).
         
     | 
| 
         @@ -3,9 +3,15 @@ require "spec_helper" 
     | 
|
| 
       3 
3 
     | 
    
         
             
            describe Imap::Backup::Account::Folder do
         
     | 
| 
       4 
4 
     | 
    
         
             
              let(:imap) { double("Net::IMAP", examine: nil) }
         
     | 
| 
       5 
5 
     | 
    
         
             
              let(:connection) { double("Imap::Backup::Account::Connection", imap: imap) }
         
     | 
| 
       6 
     | 
    
         
            -
              let(:missing_mailbox_data)  
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
               
     | 
| 
      
 6 
     | 
    
         
            +
              let(:missing_mailbox_data) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                double("Data", text: "Unknown Mailbox: my_folder")
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
              let(:missing_mailbox_response) do
         
     | 
| 
      
 10 
     | 
    
         
            +
                double("Response", data: missing_mailbox_data)
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
              let(:missing_mailbox_error) do
         
     | 
| 
      
 13 
     | 
    
         
            +
                Net::IMAP::NoResponseError.new(missing_mailbox_response)
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
       9 
15 
     | 
    
         | 
| 
       10 
16 
     | 
    
         
             
              subject { described_class.new(connection, "my_folder") }
         
     | 
| 
       11 
17 
     | 
    
         | 
| 
         @@ -19,7 +25,9 @@ describe Imap::Backup::Account::Folder do 
     | 
|
| 
       19 
25 
     | 
    
         
             
                end
         
     | 
| 
       20 
26 
     | 
    
         | 
| 
       21 
27 
     | 
    
         
             
                context "with missing mailboxes" do
         
     | 
| 
       22 
     | 
    
         
            -
                  before  
     | 
| 
      
 28 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 29 
     | 
    
         
            +
                    allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
       23 
31 
     | 
    
         | 
| 
       24 
32 
     | 
    
         
             
                  it "returns an empty array" do
         
     | 
| 
       25 
33 
     | 
    
         
             
                    expect(subject.uids).to eq([])
         
     | 
| 
         @@ -49,7 +57,9 @@ describe Imap::Backup::Account::Folder do 
     | 
|
| 
       49 
57 
     | 
    
         
             
                end
         
     | 
| 
       50 
58 
     | 
    
         | 
| 
       51 
59 
     | 
    
         
             
                context "if the mailbox doesn't exist" do
         
     | 
| 
       52 
     | 
    
         
            -
                  before  
     | 
| 
      
 60 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 61 
     | 
    
         
            +
                    allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
       53 
63 
     | 
    
         | 
| 
       54 
64 
     | 
    
         
             
                  it "is nil" do
         
     | 
| 
       55 
65 
     | 
    
         
             
                    expect(subject.fetch(123)).to be_nil
         
     |