repository: Base: fix folder structure comparison

481efa95f6 is wrong to assume the local nametrans is set up and working.

Now, we correctly check that the local folder name is the same once both
nametrans are applied to itself. This check is only done when we have to create
the folder on remote.

Github-ref: https://github.com/OfflineIMAP/offlineimap/issues/413
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Nicolas Sebrecht 2016-11-29 21:18:12 +01:00
parent 2d70857e76
commit 2e2684d445
1 changed files with 38 additions and 41 deletions

View File

@ -184,24 +184,43 @@ class BaseRepository(CustomConfig.ConfigHelperMixin):
remote_repo = self remote_repo = self
remote_hash, local_hash = {}, {} remote_hash, local_hash = {}, {}
# Create hashes with the names, but convert the local folder names
# into the remote folder names:
# - for remote, keys are: name_A -> name_A
# - for local, keys are: name_X -> (nametrans + separator) -> name_Y
for folder in remote_repo.getfolders(): for folder in remote_repo.getfolders():
remote_hash[folder.getname()] = folder remote_hash[folder.getname()] = folder
for folder in local_repo.getfolders(): for folder in local_repo.getfolders():
remote_name = folder.getvisiblename().replace( local_hash[folder.getname()] = folder
local_repo.getsep(), remote_repo.getsep())
local_hash[remote_name] = folder # Create new folders from remote to local.
for remote_name, remote_folder in remote_hash.items():
# Don't create on local_repo, if it is readonly.
if not local_repo.should_create_folders():
break
# Apply remote nametrans and fix serparator.
local_name = folder.getvisiblename().replace(
remote_repo.getsep(), local_repo.getsep())
if remote_folder.sync_this and not local_name in local_hash.keys():
try:
local_repo.makefolder(local_name)
# Need to refresh list.
local_repo.forgetfolders()
except OfflineImapError as e:
self.ui.error(e, exc_info()[2],
"Creating folder %s on repository %s"%
(local_name, local_repo))
raise
status_repo.makefolder(local_name.replace(
local_repo.getsep(), status_repo.getsep()))
# Create new folders from local to remote. # Create new folders from local to remote.
for remote_name, local_folder in local_hash.items(): for local_name, local_folder in local_hash.items():
if not remote_repo.should_create_folders(): if not remote_repo.should_create_folders():
# Don't create missing folder on readonly repo. # Don't create missing folder on readonly repo.
break break
# Apply reverse nametrans and fix serparator.
remote_name = folder.getvisiblename().replace(
local_repo.getsep(), remote_repo.getsep())
if local_folder.sync_this and not remote_name in remote_hash.keys(): if local_folder.sync_this and not remote_name in remote_hash.keys():
# Would the remote filter out the new folder name? In this case # Would the remote filter out the new folder name? In this case
# don't create it. # don't create it.
@ -211,22 +230,22 @@ class BaseRepository(CustomConfig.ConfigHelperMixin):
(remote_name, self)) (remote_name, self))
continue continue
# nametrans sanity check! # nametrans sanity check! Does remote nametrans lead to the
# Does nametrans back&forth lead to identical names? # original local name?
# #
# Apply reverse nametrans to see if we end up with the same # Apply remote nametrans to see if we end up with the same
# name: # name. We have:
# - for remote, keys are: A -> A # - remote_name: local_name -> reverse nametrans + separator
# - for local, keys are: X -> (nametrans + separator) -> Y # We want local_name == loop_name from:
# We want B == X in: A -> remote (nametrans + separator) -> B # - remote_name -> remote (nametrans + separator) -> loop_name
# #
# Get IMAPFolder and see if the reverse nametrans works fine. # Get IMAPFolder and see if the reverse nametrans works fine.
# TODO: getfolder() works only because we succeed in getting # TODO: getfolder() works only because we succeed in getting
# inexisting folders which I would like to change. Take care! # inexisting folders which I would like to change. Take care!
tmpremotefolder = remote_repo.getfolder(remote_name) tmp_remotefolder = remote_repo.getfolder(remote_name)
new_localname = tmpremotefolder.getvisiblename().replace( loop_name = tmp_remotefolder.getvisiblename().replace(
remote_repo.getsep(), local_repo.getsep()) remote_repo.getsep(), local_repo.getsep())
if local_folder.getname() != new_localname: if local_name != loop_name:
raise OfflineImapError("INFINITE FOLDER CREATION DETECTED! " raise OfflineImapError("INFINITE FOLDER CREATION DETECTED! "
"Folder '%s' (repository '%s') would be created as fold" "Folder '%s' (repository '%s') would be created as fold"
"er '%s' (repository '%s'). The latter becomes '%s' in " "er '%s' (repository '%s'). The latter becomes '%s' in "
@ -238,11 +257,10 @@ class BaseRepository(CustomConfig.ConfigHelperMixin):
"other side."% "other side."%
(local_folder.getname(), local_repo, remote_name, (local_folder.getname(), local_repo, remote_name,
remote_repo, remote_repo,
new_localname), loop_name),
OfflineImapError.ERROR.REPO) OfflineImapError.ERROR.REPO)
# End sanity check, actually create the folder. # End sanity check, actually create the folder.
local_name = local_folder.getname()
try: try:
remote_repo.makefolder(remote_name) remote_repo.makefolder(remote_name)
# Need to refresh list. # Need to refresh list.
@ -254,27 +272,6 @@ class BaseRepository(CustomConfig.ConfigHelperMixin):
status_repo.makefolder(local_name.replace( status_repo.makefolder(local_name.replace(
local_repo.getsep(), status_repo.getsep())) local_repo.getsep(), status_repo.getsep()))
# Find and create new folders from remote to local.
for remote_name, remote_folder in remote_hash.items():
# Don't create on local_repo, if it is readonly.
if not local_repo.should_create_folders():
break
if remote_folder.sync_this and not remote_name in local_hash.keys():
try:
local_name = remote_folder.getvisiblename().replace(
remote_repo.getsep(), local_repo.getsep())
local_repo.makefolder(local_name)
# Need to refresh list.
local_repo.forgetfolders()
except OfflineImapError as e:
self.ui.error(e, exc_info()[2],
"Creating folder %s on repository %s"%
(local_name, local_repo))
raise
status_repo.makefolder(local_name.replace(
local_repo.getsep(), status_repo.getsep()))
# Find deleted folders. # Find deleted folders.
# TODO: We don't delete folders right now. # TODO: We don't delete folders right now.
return None return None