123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- # Mailbox name generator
- # Copyright (C) 2002-2016 John Goerzen & contributors
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- import re # For folderfilter.
- import json
- from threading import Lock
- from os import listdir, makedirs, path, unlink
- from sys import exc_info
- from configparser import NoSectionError
- _mbLock = Lock()
- _mbnames = None
- # Called at sync time for each folder.
- def add(accountname, folder_root, foldername):
- global _mbnames
- if _mbnames.is_enabled() is not True:
- return
- with _mbLock:
- _mbnames.addAccountFolder(accountname, folder_root, foldername)
- # Called once.
- def init(conf, ui, dry_run):
- global _mbnames
- if _mbnames is None:
- _mbnames = _Mbnames(conf, ui, dry_run)
- # Called once.
- def prune(accounts):
- global _mbnames
- if _mbnames.is_enabled() is True:
- _mbnames.prune(accounts)
- else:
- _mbnames.pruneAll()
- # Called once.
- def write():
- """Write the mbnames file."""
- global _mbnames
- if _mbnames.is_enabled() is not True:
- return
- if _mbnames.get_incremental() is not True:
- _mbnames.write()
- # Called as soon as all the folders are synced for the account.
- def writeIntermediateFile(accountname):
- """Write intermediate mbnames file."""
- global _mbnames
- if _mbnames.is_enabled() is not True:
- return
- _mbnames.writeIntermediateFile(accountname)
- if _mbnames.get_incremental() is True:
- _mbnames.write()
- class _IntermediateMbnames():
- """mbnames data for one account."""
- def __init__(self, accountname, folder_root, mbnamesdir, folderfilter,
- dry_run, ui):
- self.ui = ui
- self._foldernames = []
- self._accountname = accountname
- self._folder_root = folder_root
- self._folderfilter = folderfilter
- self._path = path.join(mbnamesdir, "%s.json" % accountname)
- self._dryrun = dry_run
- def add(self, foldername):
- if foldername not in self._foldernames:
- self._foldernames.append(foldername)
- def get_folder_root(self):
- return self._folder_root
- def write(self):
- """Write intermediate mbnames file in JSON format."""
- itemlist = []
- for foldername in self._foldernames:
- if self._folderfilter(self._accountname, foldername):
- itemlist.append({
- 'accountname': self._accountname,
- 'foldername': foldername,
- 'localfolders': self._folder_root,
- })
- if self._dryrun:
- self.ui.info("mbnames would write %s" % self._path)
- else:
- with open(
- self._path, "w", encoding='utf-8') as intermediateFD:
- json.dump(itemlist, intermediateFD)
- class _Mbnames:
- def __init__(self, config, ui, dry_run):
- self._config = config
- self.ui = ui
- self._dryrun = dry_run
- self._enabled = None
- # Keys: accountname, values: _IntermediateMbnames instance.
- self._intermediates = {}
- self._incremental = None
- self._mbnamesdir = None
- self._path = None
- self._folderfilter = lambda accountname, foldername: True
- self._func_sortkey = lambda d: (d['accountname'], d['foldername'])
- localeval = config.getlocaleval()
- mbnamesdir = path.join(config.getmetadatadir(), "mbnames")
- self._peritem = None
- self._header = None
- self._sep = None
- self._footer = None
- try:
- if not self._dryrun:
- makedirs(mbnamesdir)
- except OSError:
- pass
- self._mbnamesdir = mbnamesdir
- try:
- self._enabled = self._config.getdefaultboolean(
- "mbnames", "enabled", False)
- self._peritem = self._config.get("mbnames", "peritem", raw=1)
- self._header = localeval.eval(config.get("mbnames", "header"))
- self._sep = localeval.eval(config.get("mbnames", "sep"))
- self._footer = localeval.eval(config.get("mbnames", "footer"))
- xforms = [path.expanduser, path.expandvars]
- self._path = config.apply_xforms(
- config.get("mbnames", "filename"), xforms)
- if self._config.has_option("mbnames", "sort_keyfunc"):
- self._func_sortkey = localeval.eval(
- self._config.get("mbnames", "sort_keyfunc"), {'re': re})
- if self._config.has_option("mbnames", "folderfilter"):
- self._folderfilter = localeval.eval(
- self._config.get("mbnames", "folderfilter"), {'re': re})
- except NoSectionError:
- pass
- def _iterIntermediateFiles(self):
- for foo in listdir(self._mbnamesdir):
- foo = path.join(self._mbnamesdir, foo)
- if path.isfile(foo) and foo[-5:] == '.json':
- yield foo
- def _removeIntermediateFile(self, path):
- if self._dryrun:
- self.ui.info("mbnames would remove %s" % path)
- else:
- unlink(path)
- self.ui.info("removed %s" % path)
- def addAccountFolder(self, accountname, folder_root, foldername):
- """Add foldername entry for an account."""
- if accountname not in self._intermediates:
- self._intermediates[accountname] = _IntermediateMbnames(
- accountname,
- folder_root,
- self._mbnamesdir,
- self._folderfilter,
- self._dryrun,
- self.ui,
- )
- self._intermediates[accountname].add(foldername)
- def get_incremental(self):
- if self._incremental is None:
- self._incremental = self._config.getdefaultboolean(
- "mbnames", "incremental", False)
- return self._incremental
- def is_enabled(self):
- return self._enabled
- def prune(self, accounts):
- removals = False
- for intermediateFile in self._iterIntermediateFiles():
- filename = path.basename(intermediateFile)
- accountname = filename[:-5]
- if accountname not in accounts:
- removals = True
- self._removeIntermediateFile(intermediateFile)
- if removals is False:
- self.ui.info("no cache file to remove")
- def pruneAll(self):
- for intermediateFile in self._iterIntermediateFiles():
- self._removeIntermediateFile(intermediateFile)
- def write(self):
- itemlist = []
- for intermediateFile in self._iterIntermediateFiles():
- try:
- with open(
- intermediateFile, 'r', encoding="utf-8") as intermediateFD:
- for item in json.load(intermediateFD):
- itemlist.append(item)
- except (OSError, IOError) as e:
- self.ui.error("could not read intermediate mbnames file '%s':"
- "%s" % (intermediateFile, str(e)))
- except Exception as e:
- self.ui.error(
- e,
- exc_info()[2],
- ("intermediate mbnames file %s not properly read" %
- intermediateFile)
- )
- itemlist.sort(key=self._func_sortkey)
- itemlist = [self._peritem % d for d in itemlist]
- if self._dryrun:
- self.ui.info("mbnames would write %s" % self._path)
- else:
- try:
- with open(
- self._path, 'w', encoding='utf-8') as mbnamesFile:
- mbnamesFile.write(self._header)
- mbnamesFile.write(self._sep.join(itemlist))
- mbnamesFile.write(self._footer)
- except (OSError, IOError) as e:
- self.ui.error(
- e,
- exc_info()[2],
- "mbnames file %s not properly written" % self._path
- )
- def writeIntermediateFile(self, accountname):
- try:
- self._intermediates[accountname].write()
- except (OSError, IOError) as e:
- self.ui.error(
- e,
- exc_info()[2],
- "intermediate mbnames file %s not properly written" % self._path
- )
|