Continued porting the web browser. QtWebEngine

Fri, 26 Feb 2016 20:16:59 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 26 Feb 2016 20:16:59 +0100
branch
QtWebEngine
changeset 4774
2c6ffa778c3b
parent 4773
cad470dfd807
child 4776
7f4c8e5f3385

Continued porting the web browser.

- started adding the Sync stuff

Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/DirectorySyncHandler.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/FtpSyncHandler.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncAssistantDialog.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncCheckPage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncCheckPage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncDataPage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncDataPage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncDirectorySettingsPage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncDirectorySettingsPage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncEncryptionPage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncEncryptionPage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncFtpSettingsPage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncFtpSettingsPage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncGlobals.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncHandler.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncHostTypePage.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncHostTypePage.ui file | annotate | diff | comparison | revisions
WebBrowser/Sync/SyncManager.py file | annotate | diff | comparison | revisions
WebBrowser/Sync/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserWindow.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/Preferences/__init__.py	Thu Feb 25 20:00:37 2016 +0100
+++ b/Preferences/__init__.py	Fri Feb 26 20:16:59 2016 +0100
@@ -1033,6 +1033,25 @@
         "DownloadManagerSize": QSize(400, 300),
         "DownloadManagerPosition": QPoint(),
         "DownloadManagerDownloads": [],
+        # Sync
+        "SyncEnabled": False,
+        "SyncBookmarks": True,
+        "SyncHistory": True,
+        "SyncPasswords": False,
+        "SyncUserAgents": True,
+        "SyncSpeedDial": True,
+        "SyncEncryptData": False,
+        "SyncEncryptionKey": "",
+        "SyncEncryptionKeyLength": 32,      # 16, 24 or 32
+        "SyncEncryptPasswordsOnly": False,
+        "SyncType": 0,
+        "SyncFtpServer": "",
+        "SyncFtpUser": "",
+        "SyncFtpPassword": "",
+        "SyncFtpPath": "",
+        "SyncFtpPort": 21,
+        "SyncFtpIdleTimeout": 30,
+        "SyncDirectoryPath": "",
         # Flash Cookie Manager: identical to helpDefaults
         # PIM:                  identical to helpDefaults
         # VirusTotal:           identical to helpDefaults
@@ -2715,10 +2734,10 @@
             feeds.append((url, title, icon))
         prefClass.settings.endArray()
         return feeds
-##    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
-##        from Utilities.crypto import pwConvert
-##        return pwConvert(prefClass.settings.value(
-##            "WebBrowser/" + key, prefClass.helpDefaults[key]), encode=False)
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        return pwConvert(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]), encode=False)
 ##    elif key == "HelpViewerType":
 ##        # special treatment to adjust for missing QtWebKit
 ##        value = int(prefClass.settings.value(
@@ -2727,38 +2746,29 @@
 ##            value = prefClass.helpDefaults[key]
 ##        return value
 ##    elif key in ["DiskCacheSize", "AcceptCookies",
-##                 "KeepCookiesUntil", "StartupBehavior", "HistoryLimit",
+##                 "KeepCookiesUntil", "StartupBehavior",
 ##                 "OfflineStorageDatabaseQuota",
 ##                 "OfflineWebApplicationCacheQuota", "CachePolicy",
-##                 "DownloadManagerRemovePolicy", "AdBlockUpdatePeriod",
-##                 "SearchLanguage", "SyncType", "SyncFtpPort",
-##                 "SyncFtpIdleTimeout", "SyncEncryptionKeyLength"]:
+##                 "AdBlockUpdatePeriod",
+##                  ]:
     elif key in ["StartupBehavior", "MinimumFontSize",
                  "MinimumLogicalFontSize", "HistoryLimit",
-                 "DownloadManagerRemovePolicy",]:
+                 "DownloadManagerRemovePolicy","SyncType", "SyncFtpPort",
+                 "SyncFtpIdleTimeout", "SyncEncryptionKeyLength",
+                 "SearchLanguage",]:
         return int(prefClass.settings.value(
             "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
-##    elif key in ["SingleHelpWindow", "SaveGeometry", "WebSearchSuggestions",
-##                 "DiskCacheEnabled", "FilterTrackingCookies",
+##    elif key in ["DiskCacheEnabled", "FilterTrackingCookies",
 ##                 "PrintBackgrounds", "AdBlockEnabled", "AutoLoadImages",
-##                 "JavaEnabled", "JavaScriptEnabled",
-##                 "JavaScriptCanOpenWindows", "JavaScriptCanCloseWindows",
-##                 "JavaScriptCanAccessClipboard",
+##                 "JavaEnabled",
+##                 "JavaScriptCanCloseWindows",
 ##                 "PluginsEnabled", "DnsPrefetchEnabled",
 ##                 "OfflineStorageDatabaseEnabled",
 ##                 "OfflineWebApplicationCacheEnabled", "LocalStorageEnabled",
-##                 "ShowPreview", "AccessKeysEnabled", "VirusTotalEnabled",
-##                 "VirusTotalSecure", "DoNotTrack", "SendReferer",
-##                 "SpatialNavigationEnabled", "LinksIncludedInFocusChain",
-##                 "LocalContentCanAccessRemoteUrls",
-##                 "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
-##                 "SiteSpecificQuirksEnabled", "SyncEnabled", "SyncBookmarks",
-##                 "SyncHistory", "SyncPasswords", "SyncUserAgents",
-##                 "SyncSpeedDial", "SyncEncryptData",
-##                 "SyncEncryptPasswordsOnly",
-##                 "WarnOnMultipleClose", "ClickToFlashEnabled",
-##                 "FlashCookiesDeleteOnStartExit", "FlashCookieAutoRefresh",
-##                 "FlashCookieNotify",
+##                 "ShowPreview", "AccessKeysEnabled",
+##                 "DoNotTrack", "SendReferer",
+##                 "SiteSpecificQuirksEnabled",
+##                 "ClickToFlashEnabled",
 ##                 ]:
     elif key in ["SingleHelpWindow", "SaveGeometry", "JavaScriptEnabled",
                  "JavaScriptCanOpenWindows", "JavaScriptCanAccessClipboard",
@@ -2768,13 +2778,15 @@
                  "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
                  "ScrollAnimatorEnabled", "ErrorPageEnabled",
                  "WarnOnMultipleClose", "WebSearchSuggestions",
+                 "SyncEnabled", "SyncBookmarks", "SyncHistory",
+                 "SyncPasswords", "SyncUserAgents", "SyncSpeedDial",
+                 "SyncEncryptData",
                  ]:
         return toBool(prefClass.settings.value(
             "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
 ##    elif key in ["AdBlockSubscriptions", "AdBlockExceptions",
 ##                 "ClickToFlashWhitelist", "SendRefererWhitelist",
-##                 "GreaseMonkeyDisabledScripts", "NoCacheHosts",
-##                 "FlashCookiesWhitelist", "FlashCookiesBlacklist",
+##                 "NoCacheHosts",
     elif key in ["GreaseMonkeyDisabledScripts",
                  ]:
         return toList(prefClass.settings.value(
@@ -2837,10 +2849,10 @@
             prefClass.settings.setValue("Icon", v[2])
             index += 1
         prefClass.settings.endArray()
-##    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
-##        from Utilities.crypto import pwConvert
-##        prefClass.settings.setValue(
-##            "WebBrowser/" + key, pwConvert(value, encode=True))
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        prefClass.settings.setValue(
+            "WebBrowser/" + key, pwConvert(value, encode=True))
     else:
         prefClass.settings.setValue("WebBrowser/" + key, value)
     
@@ -3486,6 +3498,16 @@
                 newPassword
             )
         )
+    for key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        prefClass.settings.setValue(
+            "WebBrowser/" + key,
+            pwRecode(
+                prefClass.settings.value("WebBrowser/" + key,
+                                         prefClass.webBrowserDefaults[key]),
+                oldPassword,
+                newPassword
+            )
+        )
 
 
 initPreferences()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/DirectorySyncHandler.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using a shared directory.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QByteArray, QFileInfo, QCoreApplication
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+
+class DirectorySyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using a shared directory.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(DirectorySyncHandler, self).__init__(parent)
+        self.__forceUpload = False
+        
+        self.__remoteFilesFound = []
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__forceUpload = forceUpload
+        
+        self.__remoteFilesFound = []
+        
+        # check the existence of the shared directory; create it, if it is
+        # not there
+        if not os.path.exists(Preferences.getWebBrowser("SyncDirectoryPath")):
+            try:
+                os.makedirs(Preferences.getWebBrowser("SyncDirectoryPath"))
+            except OSError as err:
+                self.syncError.emit(
+                    self.tr("Error creating the shared directory.\n{0}")
+                    .format(str(err)))
+                return
+        
+        self.__initialSync()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        try:
+            f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "rb")
+            data = f.read()
+            f.close()
+        except IOError as err:
+            self.syncStatus.emit(
+                type_,
+                self.tr("Cannot read remote file.\n{0}").format(str(err)))
+            self.syncFinished.emit(type_, False, True)
+            return
+        
+        QCoreApplication.processEvents()
+        ok, error = self.writeFile(QByteArray(data), fileName, type_,
+                                   timestamp)
+        if not ok:
+            self.syncStatus.emit(type_, error)
+        self.syncFinished.emit(type_, ok, True)
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        """
+        QCoreApplication.processEvents()
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+            return
+        else:
+            try:
+                f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "wb")
+                f.write(bytes(data))
+                f.close()
+            except IOError as err:
+                self.syncStatus.emit(
+                    type_,
+                    self.tr("Cannot write remote file.\n{0}").format(
+                        str(err)))
+                self.syncFinished.emit(type_, False, False)
+                return
+            
+        self.syncFinished.emit(type_, True, False)
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+                os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])) and \
+                QFileInfo(fileName).lastModified() <= QFileInfo(
+                    os.path.join(
+                        Preferences.getWebBrowser("SyncDirectoryPath"),
+                        self._remoteFiles[type_])).lastModified():
+            self.__downloadFile(
+                type_, fileName,
+                QFileInfo(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])).lastModified().toTime_t())
+        else:
+            if os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])):
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        QCoreApplication.processEvents()
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+##        QCoreApplication.processEvents()
+##        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        # TODO: SpeedDial
+##        QCoreApplication.processEvents()
+##        # Speed Dial Settings
+##        if Preferences.getWebBrowser("SyncSpeedDial"):
+##            self.__initialSyncFile(
+##                "speeddial",
+##                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+        self.syncMessage.emit(self.tr("Synchronization finished"))
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        self.__uploadFile(type_, fileName)
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    # TODO: SpeedDial
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        """
+##        self.__syncFile(
+##            "speeddial",
+##            WebBrowserWindow.speedDial().getFileName())
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        """
+        # nothing to do
+        return
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/FtpSyncHandler.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,414 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using FTP.
+"""
+
+from __future__ import unicode_literals
+
+import ftplib
+import io
+
+from PyQt5.QtCore import pyqtSignal, QTimer, QFileInfo, QCoreApplication, \
+    QByteArray
+
+from E5Network.E5Ftp import E5Ftp, E5FtpProxyType, E5FtpProxyError
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+from Utilities.FtpUtilities import FtpDirLineParser, FtpDirLineParserError
+
+
+class FtpSyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using FTP.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(FtpSyncHandler, self).__init__(parent)
+        
+        self.__state = "idle"
+        self.__forceUpload = False
+        self.__connected = False
+        
+        self.__remoteFilesFound = {}
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__state = "initializing"
+        self.__forceUpload = forceUpload
+        
+        self.__dirLineParser = FtpDirLineParser()
+        self.__remoteFilesFound = {}
+        
+        self.__idleTimer = QTimer(self)
+        self.__idleTimer.setInterval(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout") * 1000)
+        self.__idleTimer.timeout.connect(self.__idleTimeout)
+        
+        self.__ftp = E5Ftp()
+        
+        # do proxy setup
+        if not Preferences.getUI("UseProxy"):
+            proxyType = E5FtpProxyType.NoProxy
+        else:
+            proxyType = Preferences.getUI("ProxyType/Ftp")
+        if proxyType != E5FtpProxyType.NoProxy:
+            self.__ftp.setProxy(
+                proxyType,
+                Preferences.getUI("ProxyHost/Ftp"),
+                Preferences.getUI("ProxyPort/Ftp"))
+            if proxyType != E5FtpProxyType.NonAuthorizing:
+                self.__ftp.setProxyAuthentication(
+                    Preferences.getUI("ProxyUser/Ftp"),
+                    Preferences.getUI("ProxyPassword/Ftp"),
+                    Preferences.getUI("ProxyAccount/Ftp"))
+        
+        QTimer.singleShot(0, self.__doFtpCommands)
+    
+    def __doFtpCommands(self):
+        """
+        Private slot executing the sequence of FTP commands.
+        """
+        try:
+            ok = self.__connectAndLogin()
+            if ok:
+                self.__changeToStore()
+                self.__ftp.retrlines("LIST", self.__dirListCallback)
+                self.__initialSync()
+                self.__state = "idle"
+                self.__idleTimer.start()
+        except (ftplib.all_errors + (E5FtpProxyError,)) as err:
+            self.syncError.emit(str(err))
+    
+    def __connectAndLogin(self):
+        """
+        Private method to connect to the FTP server and log in.
+        
+        @return flag indicating a successful log in (boolean)
+        """
+        self.__ftp.connect(
+            Preferences.getWebBrowser("SyncFtpServer"),
+            Preferences.getWebBrowser("SyncFtpPort"),
+            timeout=5)
+        self.__ftp.login(
+            Preferences.getWebBrowser("SyncFtpUser"),
+            Preferences.getWebBrowser("SyncFtpPassword"))
+        self.__connected = True
+        return True
+    
+    def __changeToStore(self):
+        """
+        Private slot to change to the storage directory.
+        
+        This action will create the storage path on the server, if it
+        does not exist. Upon return, the current directory of the server
+        is the sync directory.
+        """
+        storePathList = \
+            Preferences.getWebBrowser("SyncFtpPath")\
+            .replace("\\", "/").split("/")
+        if storePathList[0] == "":
+            storePathList.pop(0)
+        while storePathList:
+            path = storePathList[0]
+            try:
+                self.__ftp.cwd(path)
+            except ftplib.error_perm as err:
+                code = err.args[0].strip()[:3]
+                if code == "550":
+                    # path does not exist, create it
+                    self.__ftp.mkd(path)
+                    self.__ftp.cwd(path)
+                else:
+                    raise
+            storePathList.pop(0)
+    
+    def __dirListCallback(self, line):
+        """
+        Private slot handling the receipt of directory listing lines.
+        
+        @param line the received line of the directory listing (string)
+        """
+        try:
+            urlInfo = self.__dirLineParser.parseLine(line)
+        except FtpDirLineParserError:
+            # silently ignore parser errors
+            urlInfo = None
+        
+        if urlInfo and urlInfo.isValid() and urlInfo.isFile():
+            if urlInfo.name() in self._remoteFiles.values():
+                self.__remoteFilesFound[urlInfo.name()] = \
+                    urlInfo.lastModified()
+        
+        QCoreApplication.processEvents()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        buffer = io.BytesIO()
+        try:
+            self.__ftp.retrbinary(
+                "RETR {0}".format(self._remoteFiles[type_]),
+                lambda x: self.__downloadFileCallback(buffer, x))
+            ok, error = self.writeFile(
+                QByteArray(buffer.getvalue()), fileName, type_, timestamp)
+            if not ok:
+                self.syncStatus.emit(type_, error)
+            self.syncFinished.emit(type_, ok, True)
+        except ftplib.all_errors as err:
+            self.syncStatus.emit(type_, str(err))
+            self.syncFinished.emit(type_, False, True)
+    
+    def __downloadFileCallback(self, buffer, data):
+        """
+        Private method receiving the downloaded data.
+        
+        @param buffer reference to the buffer (io.BytesIO)
+        @param data byte string to store in the buffer (bytes)
+        @return number of bytes written to the buffer (integer)
+        """
+        res = buffer.write(data)
+        QCoreApplication.processEvents()
+        return res
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        @return flag indicating success (boolean)
+        """
+        res = False
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+        else:
+            buffer = io.BytesIO(data.data())
+            try:
+                self.__ftp.storbinary(
+                    "STOR {0}".format(self._remoteFiles[type_]),
+                    buffer,
+                    callback=lambda x: QCoreApplication.processEvents())
+                self.syncFinished.emit(type_, True, False)
+                res = True
+            except ftplib.all_errors as err:
+                self.syncStatus.emit(type_, str(err))
+                self.syncFinished.emit(type_, False, False)
+        return res
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+           self._remoteFiles[type_] in self.__remoteFilesFound:
+            if QFileInfo(fileName).lastModified() < \
+               self.__remoteFilesFound[self._remoteFiles[type_]]:
+                self.__downloadFile(
+                    type_, fileName,
+                    self.__remoteFilesFound[self._remoteFiles[type_]]
+                        .toTime_t())
+            else:
+                self.syncStatus.emit(
+                    type_, self.tr("No synchronization required."))
+                self.syncFinished.emit(type_, True, True)
+        else:
+            if self._remoteFiles[type_] not in self.__remoteFilesFound:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        # TODO: SpeedDial
+        # Speed Dial Settings
+##        if Preferences.getWebBrowser("SyncSpeedDial"):
+##            self.__initialSyncFile(
+##                "speeddial",
+##                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if self.__state == "initializing":
+            return
+        
+        # use idle timeout to check, if we are still connected
+        if self.__connected:
+            self.__idleTimeout()
+        if not self.__connected or self.__ftp.sock is None:
+            ok = self.__connectAndLogin()
+            if not ok:
+                self.syncStatus.emit(
+                    type_, self.tr("Cannot log in to FTP host."))
+                return
+        
+        # upload the changed file
+        self.__state = "uploading"
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        if self.__uploadFile(type_, fileName):
+            self.syncStatus.emit(
+                type_, self.tr("Synchronization finished."))
+        self.__state = "idle"
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    # TODO: SpeedDial
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        """
+##        self.__syncFile(
+##            "speeddial",
+##            WebBrowserWindow.speedDial().getFileName())
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        """
+        if self.__idleTimer.isActive():
+            self.__idleTimer.stop()
+        
+        try:
+            if self.__connected:
+                self.__ftp.quit()
+        except ftplib.all_errors:
+            pass    # ignore FTP errors because we are shutting down anyway
+        self.__connected = False
+    
+    def __idleTimeout(self):
+        """
+        Private slot to prevent a disconnect from the server.
+        """
+        if self.__state == "idle" and self.__connected:
+            try:
+                self.__ftp.voidcmd("NOOP")
+            except ftplib.Error as err:
+                code = err.args[0].strip()[:3]
+                if code == "421":
+                    self.__connected = False
+            except IOError:
+                self.__connected = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncAssistantDialog.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a wizard dialog to enter the synchronization data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizard
+
+import UI.PixmapCache
+import Globals
+
+
+class SyncAssistantDialog(QWizard):
+    """
+    Class implementing a wizard dialog to enter the synchronization data.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncAssistantDialog, self).__init__(parent)
+        
+        from . import SyncGlobals
+
+        from .SyncDataPage import SyncDataPage
+        from .SyncEncryptionPage import SyncEncryptionPage
+        from .SyncHostTypePage import SyncHostTypePage
+        from .SyncFtpSettingsPage import SyncFtpSettingsPage
+        from .SyncDirectorySettingsPage import SyncDirectorySettingsPage
+        from .SyncCheckPage import SyncCheckPage
+
+        self.setPage(SyncGlobals.PageData, SyncDataPage(self))
+        self.setPage(SyncGlobals.PageEncryption, SyncEncryptionPage(self))
+        self.setPage(SyncGlobals.PageType, SyncHostTypePage(self))
+        self.setPage(SyncGlobals.PageFTPSettings, SyncFtpSettingsPage(self))
+        self.setPage(SyncGlobals.PageDirectorySettings,
+                     SyncDirectorySettingsPage(self))
+        self.setPage(SyncGlobals.PageCheck, SyncCheckPage(self))
+        
+        self.setPixmap(QWizard.LogoPixmap,
+                       UI.PixmapCache.getPixmap("ericWeb48.png"))
+        self.setPixmap(QWizard.WatermarkPixmap,
+                       UI.PixmapCache.getPixmap("eric256.png"))
+        self.setPixmap(QWizard.BackgroundPixmap,
+                       UI.PixmapCache.getPixmap("eric256.png"))
+        
+        self.setMinimumSize(650, 450)
+        if Globals.isWindowsPlatform():
+            self.setWizardStyle(QWizard.ModernStyle)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncCheckPage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization status wizard page.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QByteArray, QTimer
+from PyQt5.QtGui import QMovie
+from PyQt5.QtWidgets import QWizardPage
+
+from . import SyncGlobals
+
+from .Ui_SyncCheckPage import Ui_SyncCheckPage
+
+import Preferences
+import UI.PixmapCache
+
+from eric6config import getConfig
+
+
+class SyncCheckPage(QWizardPage, Ui_SyncCheckPage):
+    """
+    Class implementing the synchronization status wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncCheckPage, self).__init__(parent)
+        self.setupUi(self)
+    
+    def initializePage(self):
+        """
+        Public method to initialize the page.
+        """
+        self.syncErrorLabel.hide()
+        
+        forceUpload = self.field("ReencryptData")
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        syncMgr = WebBrowserWindow.syncManager()
+        syncMgr.syncError.connect(self.__syncError)
+        syncMgr.syncStatus.connect(self.__updateMessages)
+        syncMgr.syncFinished.connect(self.__updateLabels)
+        
+        if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+            self.handlerLabel.setText(self.tr("FTP"))
+            self.infoLabel.setText(self.tr("Host:"))
+            self.infoDataLabel.setText(
+                Preferences.getWebBrowser("SyncFtpServer"))
+        elif Preferences.getWebBrowser("SyncType") == \
+                SyncGlobals.SyncTypeDirectory:
+            self.handlerLabel.setText(self.tr("Shared Directory"))
+            self.infoLabel.setText(self.tr("Directory:"))
+            self.infoDataLabel.setText(
+                Preferences.getWebBrowser("SyncDirectoryPath"))
+        else:
+            self.handlerLabel.setText(self.tr("No Synchronization"))
+            self.hostLabel.setText("")
+        
+        self.bookmarkMsgLabel.setText("")
+        self.historyMsgLabel.setText("")
+        self.passwordsMsgLabel.setText("")
+        self.userAgentsMsgLabel.setText("")
+        self.speedDialMsgLabel.setText("")
+        
+        if not syncMgr.syncEnabled():
+            self.bookmarkLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.historyLabel.setPixmap(UI.PixmapCache.getPixmap("syncNo.png"))
+            self.passwordsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.userAgentsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.speedDialLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            return
+        
+        animationFile = os.path.join(getConfig("ericPixDir"), "loading.gif")
+        
+        # bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__makeAnimatedLabel(animationFile, self.bookmarkLabel)
+        else:
+            self.bookmarkLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # history
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__makeAnimatedLabel(animationFile, self.historyLabel)
+        else:
+            self.historyLabel.setPixmap(UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__makeAnimatedLabel(animationFile, self.passwordsLabel)
+        else:
+            self.passwordsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # user agent settings
+        if Preferences.getWebBrowser("SyncUserAgents"):
+            self.__makeAnimatedLabel(animationFile, self.userAgentsLabel)
+        else:
+            self.userAgentsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # speed dial settings
+        if Preferences.getWebBrowser("SyncSpeedDial"):
+            self.__makeAnimatedLabel(animationFile, self.speedDialLabel)
+        else:
+            self.speedDialLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        QTimer.singleShot(
+            0, lambda: syncMgr.loadSettings(forceUpload=forceUpload))
+    
+    def __makeAnimatedLabel(self, fileName, label):
+        """
+        Private slot to create an animated label.
+        
+        @param fileName name of the file containing the animation (string)
+        @param label reference to the label to be animated (QLabel)
+        """
+        movie = QMovie(fileName, QByteArray(), label)
+        movie.setSpeed(100)
+        label.setMovie(movie)
+        movie.start()
+    
+    def __updateMessages(self, type_, msg):
+        """
+        Private slot to update the synchronization status info.
+        
+        @param type_ type of synchronization data (string)
+        @param msg synchronization message (string)
+        """
+        if type_ == "bookmarks":
+            self.bookmarkMsgLabel.setText(msg)
+        elif type_ == "history":
+            self.historyMsgLabel.setText(msg)
+        elif type_ == "passwords":
+            self.passwordsMsgLabel.setText(msg)
+        elif type_ == "useragents":
+            self.userAgentsMsgLabel.setText(msg)
+        elif type_ == "speeddial":
+            self.speedDialMsgLabel.setText(msg)
+    
+    def __updateLabels(self, type_, status, download):
+        """
+        Private slot to handle a finished synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param status flag indicating success (boolean)
+        @param download flag indicating a download of a file (boolean)
+        """
+        if type_ == "bookmarks":
+            if status:
+                self.bookmarkLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.bookmarkLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "history":
+            if status:
+                self.historyLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.historyLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "passwords":
+            if status:
+                self.passwordsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.passwordsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "useragents":
+            if status:
+                self.userAgentsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.userAgentsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "speeddial":
+            if status:
+                self.speedDialLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.speedDialLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+    
+    def __syncError(self, message):
+        """
+        Private slot to handle general synchronization issues.
+        
+        @param message error message (string)
+        """
+        self.syncErrorLabel.show()
+        self.syncErrorLabel.setText(self.tr(
+            '<font color="#FF0000"><b>Error:</b> {0}</font>').format(message))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncCheckPage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncCheckPage</class>
+ <widget class="QWizardPage" name="SyncCheckPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Synchronization status</string>
+  </property>
+  <property name="subTitle">
+   <string>This page shows the status of the current synchronization process.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Synchronization Data</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Sync Handler:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="handlerLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string notr="true">handler</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="infoLabel">
+        <property name="text">
+         <string notr="true">Host:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="infoDataLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string notr="true">host</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Synchronization Status</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Bookmarks:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="bookmarkLabel"/>
+      </item>
+      <item row="0" column="2" colspan="2">
+       <widget class="QLabel" name="bookmarkMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>History:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="historyLabel"/>
+      </item>
+      <item row="1" column="2" colspan="2">
+       <widget class="QLabel" name="historyMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Passwords:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLabel" name="passwordsLabel"/>
+      </item>
+      <item row="2" column="2" colspan="2">
+       <widget class="QLabel" name="passwordsMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>User Agent Settings:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QLabel" name="userAgentsLabel"/>
+      </item>
+      <item row="3" column="2" colspan="2">
+       <widget class="QLabel" name="userAgentsMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Speed Dial Settings:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1" colspan="2">
+       <widget class="QLabel" name="speedDialLabel"/>
+      </item>
+      <item row="4" column="3">
+       <widget class="QLabel" name="speedDialMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" colspan="4">
+       <widget class="QLabel" name="syncErrorLabel">
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>81</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDataPage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization data wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncDataPage import Ui_SyncDataPage
+
+import Preferences
+
+
+class SyncDataPage(QWizardPage, Ui_SyncDataPage):
+    """
+    Class implementing the synchronization data wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncDataPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.bookmarksCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncBookmarks"))
+        self.historyCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncHistory"))
+        self.passwordsCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncPasswords"))
+        self.userAgentsCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncUserAgents"))
+        self.speedDialCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncSpeedDial"))
+        
+        self.activeCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncEnabled"))
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser(
+            "SyncEnabled", self.activeCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "SyncBookmarks", self.bookmarksCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncHistory", self.historyCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncPasswords", self.passwordsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncUserAgents", self.userAgentsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncSpeedDial", self.speedDialCheckBox.isChecked())
+        
+        from . import SyncGlobals
+        if self.activeCheckBox.isChecked():
+            return SyncGlobals.PageEncryption
+        else:
+            return SyncGlobals.PageCheck
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDataPage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncDataPage</class>
+ <widget class="QWizardPage" name="SyncDataPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Basic synchronization settings</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select, if synchronization should be enabled and which data should be synchronized.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QCheckBox" name="activeCheckBox">
+     <property name="toolTip">
+      <string>Select to activate data synchronization</string>
+     </property>
+     <property name="text">
+      <string>Activate synchronization</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="syncDataBox">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="title">
+      <string>Data to be synchronized</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QCheckBox" name="bookmarksCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize bookmarks</string>
+        </property>
+        <property name="text">
+         <string>Bookmarks</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="historyCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize history</string>
+        </property>
+        <property name="text">
+         <string>History</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="passwordsCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize passwords</string>
+        </property>
+        <property name="text">
+         <string>Passwords</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="userAgentsCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize user agent settings</string>
+        </property>
+        <property name="text">
+         <string>User Agent Settings</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="speedDialCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize the speed dial data</string>
+        </property>
+        <property name="text">
+         <string>Speed Dial Settings</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>150</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>activeCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>syncDataBox</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>63</x>
+     <y>15</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>63</x>
+     <y>42</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDirectorySettingsPage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization shared directory settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_SyncDirectorySettingsPage import Ui_SyncDirectorySettingsPage
+
+import Preferences
+
+
+class SyncDirectorySettingsPage(QWizardPage, Ui_SyncDirectorySettingsPage):
+    """
+    Class implementing the shared directory host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncDirectorySettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.directoryPicker.setMode(E5PathPickerModes.DirectoryMode)
+        self.directoryPicker.setText(
+            Preferences.getWebBrowser("SyncDirectoryPath"))
+        
+        self.directoryPicker.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncDirectoryPath", self.directoryPicker.text())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.directoryPicker.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDirectorySettingsPage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncDirectorySettingsPage</class>
+ <widget class="QWizardPage" name="SyncDirectorySettingsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string/>
+  </property>
+  <property name="title">
+   <string>Synchronize to a shared directory</string>
+  </property>
+  <property name="subTitle">
+   <string>Please enter the data for synchronization via a shared directory. All fields must be filled.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Shared Directory Settings</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Directory Name:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="E5PathPicker" name="directoryPicker" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="focusPolicy">
+         <enum>Qt::StrongFocus</enum>
+        </property>
+        <property name="toolTip">
+         <string>Enter the full path of the shared directory</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>317</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncEncryptionPage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing encryption settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncEncryptionPage import Ui_SyncEncryptionPage
+
+import Preferences
+
+
+class SyncEncryptionPage(QWizardPage, Ui_SyncEncryptionPage):
+    """
+    Class implementing encryption settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncEncryptionPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.keySizeComboBox.addItem(self.tr("128 Bits"), 16)
+        self.keySizeComboBox.addItem(self.tr("192 Bits"), 24)
+        self.keySizeComboBox.addItem(self.tr("256 Bits"), 32)
+        
+        self.registerField("ReencryptData", self.reencryptCheckBox)
+        
+        self.encryptionGroupBox.setChecked(
+            Preferences.getWebBrowser("SyncEncryptData"))
+        self.encryptionKeyEdit.setText(
+            Preferences.getWebBrowser("SyncEncryptionKey"))
+        self.encryptionKeyAgainEdit.setEnabled(False)
+        self.keySizeComboBox.setCurrentIndex(self.keySizeComboBox.findData(
+            Preferences.getWebBrowser("SyncEncryptionKeyLength")))
+        self.loginsOnlyCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncEncryptPasswordsOnly"))
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        Preferences.setWebBrowser(
+            "SyncEncryptData", self.encryptionGroupBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncEncryptionKey", self.encryptionKeyEdit.text())
+        Preferences.setWebBrowser(
+            "SyncEncryptionKeyLength", self.keySizeComboBox.itemData(
+                self.keySizeComboBox.currentIndex()))
+        Preferences.setWebBrowser(
+            "SyncEncryptPasswordsOnly", self.loginsOnlyCheckBox.isChecked())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageType
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        if self.encryptionGroupBox.isChecked():
+            if self.encryptionKeyEdit.text() == "":
+                complete = False
+            else:
+                if self.reencryptCheckBox.isChecked():
+                    complete = (self.encryptionKeyEdit.text() ==
+                                self.encryptionKeyAgainEdit.text())
+                else:
+                    complete = True
+        else:
+            complete = True
+        
+        return complete
+    
+    def __updateUI(self):
+        """
+        Private slot to update the variable parts of the UI.
+        """
+        error = ""
+        
+        if self.encryptionGroupBox.isChecked():
+            self.encryptionKeyAgainEdit.setEnabled(
+                self.reencryptCheckBox.isChecked())
+            
+            if self.encryptionKeyEdit.text() == "":
+                error = error or self.tr(
+                    "Encryption key must not be empty.")
+            
+            if self.encryptionKeyEdit.text() != "" and \
+                    self.reencryptCheckBox.isChecked() and \
+                    (self.encryptionKeyEdit.text() !=
+                     self.encryptionKeyAgainEdit.text()):
+                error = error or self.tr(
+                    "Repeated encryption key is wrong.")
+        
+        self.errorLabel.setText(error)
+        self.completeChanged.emit()
+    
+    @pyqtSlot(str)
+    def on_encryptionKeyEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the encryption key.
+        
+        @param txt content of the edit widget (string)
+        """
+        self.passwordMeter.checkPasswordStrength(txt)
+        self.__updateUI()
+    
+    @pyqtSlot(str)
+    def on_encryptionKeyAgainEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the encryption key repetition.
+        
+        @param txt content of the edit widget (string)
+        """
+        self.__updateUI()
+    
+    @pyqtSlot(bool)
+    def on_encryptionGroupBox_toggled(self, on):
+        """
+        Private slot to handle changes of the encryption selection.
+        
+        @param on state of the group box (boolean)
+        """
+        self.__updateUI()
+    
+    @pyqtSlot(bool)
+    def on_reencryptCheckBox_toggled(self, on):
+        """
+        Private slot to handle changes of the re-encryption selection.
+        
+        @param on state of the check box (boolean)
+        """
+        self.__updateUI()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncEncryptionPage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncEncryptionPage</class>
+ <widget class="QWizardPage" name="SyncEncryptionPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Encryption Settings</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select, if the synchronized data should be encrypted and enter the encryption key</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="encryptionGroupBox">
+     <property name="toolTip">
+      <string>Select to encrypt the synchronzed data</string>
+     </property>
+     <property name="title">
+      <string>Encrypt Data</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0" colspan="2">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>&lt;p&gt;The encryption key will be used to encrypt and decrypt the synchronizde data. If the data should be re-encrypted, the respective selection should be done. The key must only be repeated, if a re-encryption is requested.&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Note: If you forget the encryption key, the encrypted data cannot be recovered!&lt;/b&gt;&lt;br/&gt;&lt;/p&gt;</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="2">
+       <widget class="QCheckBox" name="reencryptCheckBox">
+        <property name="toolTip">
+         <string>Select to re-encrypt the synchronized data</string>
+        </property>
+        <property name="text">
+         <string>Re-encrypt synchronized data</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Encryption Key:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLineEdit" name="encryptionKeyEdit">
+        <property name="toolTip">
+         <string>Enter the encryption key</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Encryption Key (again):</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QLineEdit" name="encryptionKeyAgainEdit">
+        <property name="toolTip">
+         <string>Repeat the encryption key</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0" colspan="2">
+       <widget class="E5PasswordMeter" name="passwordMeter">
+        <property name="toolTip">
+         <string>Shows an indication for the encryption key strength</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" colspan="2">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label_4">
+          <property name="text">
+           <string>Size of generated encryption key:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="keySizeComboBox">
+          <property name="toolTip">
+           <string>Select the size of the generated encryption key</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item row="6" column="0" colspan="2">
+       <widget class="QLabel" name="errorLabel">
+        <property name="styleSheet">
+         <string notr="true">color : red;</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="0" colspan="2">
+       <widget class="QCheckBox" name="loginsOnlyCheckBox">
+        <property name="toolTip">
+         <string>Select to encrypt only the passwords</string>
+        </property>
+        <property name="text">
+         <string>Encrypt Passwords Only</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>191</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PasswordMeter</class>
+   <extends>QProgressBar</extends>
+   <header>E5Gui/E5PasswordMeter</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>encryptionGroupBox</tabstop>
+  <tabstop>reencryptCheckBox</tabstop>
+  <tabstop>encryptionKeyEdit</tabstop>
+  <tabstop>encryptionKeyAgainEdit</tabstop>
+  <tabstop>keySizeComboBox</tabstop>
+  <tabstop>loginsOnlyCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncFtpSettingsPage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization FTP host settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncFtpSettingsPage import Ui_SyncFtpSettingsPage
+
+import Preferences
+
+
+class SyncFtpSettingsPage(QWizardPage, Ui_SyncFtpSettingsPage):
+    """
+    Class implementing the synchronization FTP host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncFtpSettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.serverEdit.setText(Preferences.getWebBrowser("SyncFtpServer"))
+        self.userNameEdit.setText(Preferences.getWebBrowser("SyncFtpUser"))
+        self.passwordEdit.setText(Preferences.getWebBrowser("SyncFtpPassword"))
+        self.pathEdit.setText(Preferences.getWebBrowser("SyncFtpPath"))
+        self.portSpinBox.setValue(Preferences.getWebBrowser("SyncFtpPort"))
+        self.idleSpinBox.setValue(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout"))
+        
+        self.serverEdit.textChanged.connect(self.completeChanged)
+        self.userNameEdit.textChanged.connect(self.completeChanged)
+        self.passwordEdit.textChanged.connect(self.completeChanged)
+        self.pathEdit.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncFtpServer", self.serverEdit.text())
+        Preferences.setWebBrowser("SyncFtpUser", self.userNameEdit.text())
+        Preferences.setWebBrowser("SyncFtpPassword", self.passwordEdit.text())
+        Preferences.setWebBrowser("SyncFtpPath", self.pathEdit.text())
+        Preferences.setWebBrowser("SyncFtpPort", self.portSpinBox.value())
+        Preferences.setWebBrowser("SyncFtpIdleTimeout", self.idleSpinBox.value())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.serverEdit.text() != "" and \
+            self.userNameEdit.text() != "" and \
+            self.passwordEdit.text() != "" and \
+            self.pathEdit.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncFtpSettingsPage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncFtpSettingsPage</class>
+ <widget class="QWizardPage" name="SyncFtpSettingsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Synchronize to an FTP host</string>
+  </property>
+  <property name="subTitle">
+   <string>Please enter the data for synchronization via FTP. All fields must be filled.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Remote FTP Host Settings</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Server:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" colspan="2">
+       <widget class="QLineEdit" name="serverEdit">
+        <property name="toolTip">
+         <string>Enter the FTP server name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>User Name:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="2">
+       <widget class="QLineEdit" name="userNameEdit">
+        <property name="toolTip">
+         <string>Enter the user name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Password:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1" colspan="2">
+       <widget class="QLineEdit" name="passwordEdit">
+        <property name="toolTip">
+         <string>Enter the password</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Path:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1" colspan="2">
+       <widget class="QLineEdit" name="pathEdit">
+        <property name="toolTip">
+         <string>Enter the remote path</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Port:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QSpinBox" name="portSpinBox">
+        <property name="toolTip">
+         <string>Enter the remote port</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>65635</number>
+        </property>
+        <property name="value">
+         <number>21</number>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>218</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="5" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Idle Timeout:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1">
+       <widget class="QSpinBox" name="idleSpinBox">
+        <property name="toolTip">
+         <string>Enter the idle timeout interval to prevent a server disconnect</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="suffix">
+         <string> s</string>
+        </property>
+        <property name="minimum">
+         <number>10</number>
+        </property>
+        <property name="maximum">
+         <number>3600</number>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>419</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>101</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncGlobals.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some global definitions.
+"""
+
+# Page IDs for the sync wizard
+PageData = 0
+PageEncryption = 1
+PageType = 2
+PageFTPSettings = 3
+PageDirectorySettings = 4
+PageCheck = 5
+
+# Sync types
+SyncTypeNone = -1
+SyncTypeFtp = 0
+SyncTypeDirectory = 1
+
+#
+# eflag: noqa = M702
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHandler.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing a base class for synchronization handlers.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QObject, pyqtSignal, QByteArray
+
+import Preferences
+
+from Utilities.crypto import dataEncrypt, dataDecrypt
+
+
+class SyncHandler(QObject):
+    """
+    Base class for synchronization handlers.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SyncHandler, self).__init__(parent)
+        
+        self._firstTimeSynced = False
+        
+        self._remoteFiles = {
+            "bookmarks": "Bookmarks",
+            "history": "History",
+            "passwords": "Logins",
+            "useragents": "UserAgentSettings",
+            "speeddial": "SpeedDial",
+        }
+        
+        self._messages = {
+            "bookmarks": {
+                "RemoteExists": self.tr(
+                    "Remote bookmarks file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote bookmarks file does NOT exists. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local bookmarks file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local bookmarks file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local bookmarks file..."),
+            },
+            "history": {
+                "RemoteExists": self.tr(
+                    "Remote history file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote history file does NOT exists. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local history file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local history file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local history file..."),
+            },
+            "passwords": {
+                "RemoteExists": self.tr(
+                    "Remote logins file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote logins file does NOT exists. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local logins file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local logins file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local logins file..."),
+            },
+            "useragents": {
+                "RemoteExists": self.tr(
+                    "Remote user agent settings file exists! Syncing local"
+                    " copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote user agent settings file does NOT exists."
+                    " Exporting local copy..."),
+                "LocalNewer": self.tr(
+                    "Local user agent settings file is NEWER. Exporting"
+                    " local copy..."),
+                "LocalMissing": self.tr(
+                    "Local user agent settings file does NOT exist."
+                    " Skipping synchronization!"),
+                "Uploading": self.tr(
+                    "Uploading local user agent settings file..."),
+            },
+            "speeddial": {
+                "RemoteExists": self.tr(
+                    "Remote speed dial settings file exists! Syncing local"
+                    " copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote speed dial settings file does NOT exists."
+                    " Exporting local copy..."),
+                "LocalNewer": self.tr(
+                    "Local speed dial settings file is NEWER. Exporting"
+                    " local copy..."),
+                "LocalMissing": self.tr(
+                    "Local speed dial settings file does NOT exist."
+                    " Skipping synchronization!"),
+                "Uploading": self.tr(
+                    "Uploading local speed dial settings file..."),
+            },
+        }
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def readFile(self, fileName, type_):
+        """
+        Public method to read a file.
+        
+        If encrypted synchronization is enabled, the data will be encrypted
+        using the relevant encryption key.
+        
+        @param fileName name of the file to be read (string)
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @return data of the file, optionally encrypted (QByteArray)
+        """
+        if os.path.exists(fileName):
+            try:
+                inputFile = open(fileName, "rb")
+                data = inputFile.read()
+                inputFile.close()
+            except IOError:
+                return QByteArray()
+            
+            if Preferences.getWebBrowser("SyncEncryptData") and \
+               (not Preferences.getWebBrowser("SyncEncryptPasswordsOnly") or
+                (Preferences.getWebBrowser("SyncEncryptPasswordsOnly") and
+                 type_ == "passwords")):
+                key = Preferences.getWebBrowser("SyncEncryptionKey")
+                if not key:
+                    return QByteArray()
+                
+                data, ok = dataEncrypt(
+                    data, key,
+                    keyLength=Preferences.getWebBrowser(
+                        "SyncEncryptionKeyLength"),
+                    hashIterations=100)
+                if not ok:
+                    return QByteArray()
+            
+            return QByteArray(data)
+        
+        return QByteArray()
+    
+    def writeFile(self, data, fileName, type_, timestamp=0):
+        """
+        Public method to write the data to a file.
+        
+        If encrypted synchronization is enabled, the data will be decrypted
+        using the relevant encryption key.
+        
+        @param data data to be written and optionally decrypted (QByteArray)
+        @param fileName name of the file the data is to be written to (string)
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param timestamp timestamp to be given to the file (int)
+        @return tuple giving a success flag and an error string (boolean,
+            string)
+        """
+        data = bytes(data)
+        
+        if Preferences.getWebBrowser("SyncEncryptData") and \
+                (not Preferences.getWebBrowser("SyncEncryptPasswordsOnly") or
+                 (Preferences.getWebBrowser("SyncEncryptPasswordsOnly") and
+                  type_ == "passwords")):
+            key = Preferences.getWebBrowser("SyncEncryptionKey")
+            if not key:
+                return False, self.tr("Invalid encryption key given.")
+            
+            data, ok = dataDecrypt(
+                data, key,
+                keyLength=Preferences.getWebBrowser("SyncEncryptionKeyLength"))
+            if not ok:
+                return False, self.tr("Data cannot be decrypted.")
+        
+        try:
+            outputFile = open(fileName, "wb")
+            outputFile.write(data)
+            outputFile.close()
+            if timestamp > 0:
+                os.utime(fileName, (timestamp, timestamp))
+            return True, ""
+        except IOError as error:
+            return False, str(error)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHostTypePage.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization host type wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from . import SyncGlobals
+
+from .Ui_SyncHostTypePage import Ui_SyncHostTypePage
+
+import Preferences
+
+
+class SyncHostTypePage(QWizardPage, Ui_SyncHostTypePage):
+    """
+    Class implementing the synchronization host type wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncHostTypePage, self).__init__(parent)
+        self.setupUi(self)
+        
+        if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+            self.ftpRadioButton.setChecked(True)
+        elif Preferences.getWebBrowser("SyncType") == \
+                SyncGlobals.SyncTypeDirectory:
+            self.directoryRadioButton.setChecked(True)
+        else:
+            self.noneRadioButton.setChecked(True)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        if self.ftpRadioButton.isChecked():
+            Preferences.setWebBrowser("SyncType", SyncGlobals.SyncTypeFtp)
+            return SyncGlobals.PageFTPSettings
+        elif self.directoryRadioButton.isChecked():
+            Preferences.setWebBrowser(
+                "SyncType", SyncGlobals.SyncTypeDirectory)
+            return SyncGlobals.PageDirectorySettings
+        else:
+            Preferences.setWebBrowser("SyncType", SyncGlobals.SyncTypeNone)
+            return SyncGlobals.PageCheck
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHostTypePage.ui	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncHostTypePage</class>
+ <widget class="QWizardPage" name="SyncHostTypePage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Host Type Selection</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select the type of the host to be used for synchronization.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Synchronization Host Type</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QRadioButton" name="ftpRadioButton">
+        <property name="toolTip">
+         <string>Select to use a FTP host</string>
+        </property>
+        <property name="text">
+         <string>FTP</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="directoryRadioButton">
+        <property name="toolTip">
+         <string>Select to use a shared directory</string>
+        </property>
+        <property name="text">
+         <string>Shared Directory</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="noneRadioButton">
+        <property name="toolTip">
+         <string>Select to use no particular host type</string>
+        </property>
+        <property name="text">
+         <string>None</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>191</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>ftpRadioButton</tabstop>
+  <tabstop>directoryRadioButton</tabstop>
+  <tabstop>noneRadioButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncManager.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject, pyqtSignal
+
+import Preferences
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+
+class SyncManager(QObject):
+    """
+    Class implementing the synchronization manager.
+    
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to give status info about the sync
+        process (string)
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncStatus = pyqtSignal(str, str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SyncManager, self).__init__(parent)
+        
+        self.__handler = None
+    
+    def handler(self):
+        """
+        Public method to get a reference to the sync handler object.
+        
+        @return reference to the sync handler object (SyncHandler)
+        """
+        return self.__handler
+    
+    def showSyncDialog(self):
+        """
+        Public method to show the synchronization dialog.
+        """
+        from .SyncAssistantDialog import SyncAssistantDialog
+        dlg = SyncAssistantDialog()
+        dlg.exec_()
+    
+    def loadSettings(self, forceUpload=False):
+        """
+        Public method to load the settings.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if self.__handler is not None:
+            self.__handler.syncError.disconnect(self.__syncError)
+            self.__handler.syncFinished.disconnect(self.__syncFinished)
+            self.__handler.syncStatus.disconnect(self.__syncStatus)
+            self.__handler.syncMessage.disconnect(self.syncMessage)
+            self.__handler.shutdown()
+        
+        if self.syncEnabled():
+            from . import SyncGlobals
+            if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+                from .FtpSyncHandler import FtpSyncHandler
+                self.__handler = FtpSyncHandler(self)
+            elif Preferences.getWebBrowser("SyncType") == \
+                    SyncGlobals.SyncTypeDirectory:
+                from .DirectorySyncHandler import DirectorySyncHandler
+                self.__handler = DirectorySyncHandler(self)
+            self.__handler.syncError.connect(self.__syncError)
+            self.__handler.syncFinished.connect(self.__syncFinished)
+            self.__handler.syncStatus.connect(self.__syncStatus)
+            self.__handler.syncMessage.connect(self.syncMessage)
+            
+            self.__handler.initialLoadAndCheck(forceUpload=forceUpload)
+            
+            # connect sync manager to bookmarks manager
+            if Preferences.getWebBrowser("SyncBookmarks"):
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.connect(self.__syncBookmarks)
+            else:
+                try:
+                    WebBrowserWindow.bookmarksManager()\
+                        .bookmarksSaved.disconnect(self.__syncBookmarks)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to history manager
+            if Preferences.getWebBrowser("SyncHistory"):
+                WebBrowserWindow.historyManager().historySaved\
+                    .connect(self.__syncHistory)
+            else:
+                try:
+                    WebBrowserWindow.historyManager()\
+                        .historySaved.disconnect(self.__syncHistory)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to passwords manager
+            if Preferences.getWebBrowser("SyncPasswords"):
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.connect(self.__syncPasswords)
+            else:
+                try:
+                    WebBrowserWindow.passwordManager()\
+                        .passwordsSaved.disconnect(self.__syncPasswords)
+                except TypeError:
+                    pass
+            
+            # TODO: UserAgents
+            # connect sync manager to user agent manager
+##            if Preferences.getWebBrowser("SyncUserAgents"):
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.connect(self.__syncUserAgents)
+##            else:
+##                try:
+##                    WebBrowserWindow.userAgentsManager()\
+##                        .userAgentSettingsSaved.disconnect(
+##                            self.__syncUserAgents)
+##                except TypeError:
+##                    pass
+            
+            # TODO: SpeedDial
+            # connect sync manager to speed dial
+##            if Preferences.getWebBrowser("SyncSpeedDial"):
+##                WebBrowserWindow.speedDial()\
+##                    .speedDialSaved.connect(self.__syncSpeedDial)
+##            else:
+##                try:
+##                    WebBrowserWindow.speedDial()\
+##                        .speedDialSaved.disconnect(self.__syncSpeedDial)
+##                except TypeError:
+##                    pass
+        else:
+            self.__handler = None
+            
+            try:
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.disconnect(self.__syncBookmarks)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.historyManager().historySaved\
+                    .disconnect(self.__syncHistory)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.disconnect(self.__syncPasswords)
+            except TypeError:
+                pass
+            # TODO: UserAgents
+##            try:
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.disconnect(self.__syncUserAgents)
+##            except TypeError:
+##                pass
+            # TODO: SpeedDial
+##            try:
+##                WebBrowserWindow.speedDial()\
+##                    .speedDialSaved.disconnect(self.__syncSpeedDial)
+##            except TypeError:
+##                pass
+    
+    def syncEnabled(self):
+        """
+        Public method to check, if synchronization is enabled.
+        
+        @return flag indicating enabled synchronization
+        """
+        from . import SyncGlobals
+        return Preferences.getWebBrowser("SyncEnabled") and \
+            Preferences.getWebBrowser("SyncType") != SyncGlobals.SyncTypeNone
+    
+    def __syncBookmarks(self):
+        """
+        Private slot to synchronize the bookmarks.
+        """
+        if self.__handler is not None:
+            self.__handler.syncBookmarks()
+    
+    def __syncHistory(self):
+        """
+        Private slot to synchronize the history.
+        """
+        if self.__handler is not None:
+            self.__handler.syncHistory()
+    
+    def __syncPasswords(self):
+        """
+        Private slot to synchronize the passwords.
+        """
+        if self.__handler is not None:
+            self.__handler.syncPasswords()
+    
+    def __syncUserAgents(self):
+        """
+        Private slot to synchronize the user agent settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncUserAgents()
+    
+    def __syncSpeedDial(self):
+        """
+        Private slot to synchronize the speed dial settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncSpeedDial()
+    
+    def __syncError(self, message):
+        """
+        Private slot to handle general synchronization issues.
+        
+        @param message error message (string)
+        """
+        self.syncError.emit(message)
+    
+    def __syncFinished(self, type_, status, download):
+        """
+        Private slot to handle a finished synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param status flag indicating success (boolean)
+        @param download flag indicating a download of a file (boolean)
+        """
+        if status and download:
+            if type_ == "bookmarks":
+                WebBrowserWindow.bookmarksManager().reload()
+            elif type_ == "history":
+                WebBrowserWindow.historyManager().reload()
+            elif type_ == "passwords":
+                WebBrowserWindow.passwordManager().reload()
+            # TODO: UserAgents
+##            elif type_ == "useragents":
+##                WebBrowserWindow.userAgentsManager().reload()
+            # TODO: SpeeedDial
+##            elif type_ == "speeddial":
+##                WebBrowserWindow.speedDial().reload()
+        self.syncFinished.emit(type_, status, download)
+    
+    def __syncStatus(self, type_, message):
+        """
+        Private slot to handle a status update of a synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param message status message for the event (string)
+        """
+        self.syncMessage.emit(message)
+        self.syncStatus.emit(type_, message)
+    
+    def close(self):
+        """
+        Public slot to shut down the synchronization manager.
+        """
+        if not self.syncEnabled():
+            return
+        
+        if self.__handler is not None:
+            self.__handler.shutdown()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/__init__.py	Fri Feb 26 20:16:59 2016 +0100
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing capabilities to sync some configuration data.
+"""
--- a/WebBrowser/WebBrowserWindow.py	Thu Feb 25 20:00:37 2016 +0100
+++ b/WebBrowser/WebBrowserWindow.py	Fri Feb 26 20:16:59 2016 +0100
@@ -57,7 +57,7 @@
 from .data import javascript_rc     # __IGNORE_WARNING__
 
 
-from .Tools import Scripts, WebBrowserTools, WebIconProvider, WebBrowserPaths
+from .Tools import Scripts, WebBrowserTools, WebIconProvider
 
 from .ZoomManager import ZoomManager
 
@@ -92,7 +92,7 @@
     _downloadManager = None
     _feedsManager = None
 ##    _userAgentsManager = None
-##    _syncManager = None
+    _syncManager = None
 ##    _speedDial = None
     _personalInformationManager = None
     _greaseMonkeyManager = None
@@ -1692,23 +1692,22 @@
 ##                self.__showUserAgentsDialog)
 ##        self.__actions.append(self.userAgentManagerAct)
         
-        # TODO: Synchronisation
-##        self.synchronizationAct = E5Action(
-##            self.tr('Synchronize data'),
-##            UI.PixmapCache.getIcon("sync.png"),
-##            self.tr('&Synchronize Data...'),
-##            0, 0, self, 'webbrowser_synchronize_data')
-##        self.synchronizationAct.setStatusTip(self.tr(
-##            'Shows a dialog to synchronize data via the network'))
-##        self.synchronizationAct.setWhatsThis(self.tr(
-##            """<b>Synchronize Data...</b>"""
-##            """<p>This shows a dialog to synchronize data via the"""
-##            """ network.</p>"""
-##        ))
-##        if not self.__initShortcutsOnly:
-##            self.synchronizationAct.triggered.connect(
-##                self.__showSyncDialog)
-##        self.__actions.append(self.synchronizationAct)
+        self.synchronizationAct = E5Action(
+            self.tr('Synchronize data'),
+            UI.PixmapCache.getIcon("sync.png"),
+            self.tr('&Synchronize Data...'),
+            0, 0, self, 'webbrowser_synchronize_data')
+        self.synchronizationAct.setStatusTip(self.tr(
+            'Shows a dialog to synchronize data via the network'))
+        self.synchronizationAct.setWhatsThis(self.tr(
+            """<b>Synchronize Data...</b>"""
+            """<p>This shows a dialog to synchronize data via the"""
+            """ network.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.synchronizationAct.triggered.connect(
+                self.__showSyncDialog)
+        self.__actions.append(self.synchronizationAct)
         
         self.zoomValuesAct = E5Action(
             self.tr('Manage Saved Zoom Values'),
@@ -1885,9 +1884,10 @@
         menu = mb.addMenu(self.tr("&Tools"))
         menu.setTearOffEnabled(True)
         menu.addAction(self.feedsManagerAct)
+        # TODO: Site Info
 ##        menu.addAction(self.siteInfoAct)
-##        menu.addSeparator()
-##        menu.addAction(self.synchronizationAct)
+        menu.addSeparator()
+        menu.addAction(self.synchronizationAct)
 ##        menu.addSeparator()
 ##        menu.addAction(self.toolsMonitorAct)
         
@@ -1996,9 +1996,10 @@
         toolstb.setObjectName("ToolsToolBar")
         toolstb.setIconSize(UI.Config.ToolBarIconSize)
         toolstb.addAction(self.feedsManagerAct)
+        # TODO: SiteInfo
 ##        toolstb.addAction(self.siteInfoAct)
-##        toolstb.addSeparator()
-##        toolstb.addAction(self.synchronizationAct)
+        toolstb.addSeparator()
+        toolstb.addAction(self.synchronizationAct)
         
         helptb = self.addToolBar(self.tr("Help"))
         helptb.setObjectName("HelpToolBar")
@@ -2603,7 +2604,6 @@
         Private slot called to toggle fullscreen mode.
         """
         if self.__isFullScreen():
-            # TODO: Full Screen - web pages need to be toggled separately (Qt 5.6)
             # switch back to normal
             self.setWindowState(self.windowState() & ~Qt.WindowFullScreen)
             self.menuBar().show()
@@ -2617,7 +2617,6 @@
             self.fullScreenAct.setIcon(
                 UI.PixmapCache.getIcon("windowRestore.png"))
             self.fullScreenAct.setIconText(self.tr('Restore Window'))
-            # TODO: Full Screen - web pages need to be toggled separately (Qt 5.6)
     
     def __isFullScreen(self):
         """
@@ -3826,26 +3825,25 @@
 ##        dlg = UserAgentsDialog(self)
 ##        dlg.exec_()
 ##    
-    # TODO: Sync
-##    @classmethod
-##    def syncManager(cls):
-##        """
-##        Class method to get a reference to the data synchronization manager.
-##        
-##        @return reference to the data synchronization manager (SyncManager)
-##        """
-##        if cls._syncManager is None:
-##            from .Sync.SyncManager import SyncManager
-##            cls._syncManager = SyncManager()
-##        
-##        return cls._syncManager
-##    
-##    def __showSyncDialog(self):
-##        """
-##        Private slot to show the synchronization dialog.
-##        """
-##        self.syncManager().showSyncDialog()
-##    
+    @classmethod
+    def syncManager(cls):
+        """
+        Class method to get a reference to the data synchronization manager.
+        
+        @return reference to the data synchronization manager (SyncManager)
+        """
+        if cls._syncManager is None:
+            from .Sync.SyncManager import SyncManager
+            cls._syncManager = SyncManager()
+        
+        return cls._syncManager
+    
+    def __showSyncDialog(self):
+        """
+        Private slot to show the synchronization dialog.
+        """
+        self.syncManager().showSyncDialog()
+    
     # TODO: SpeedDial
 ##    @classmethod
 ##    def speedDial(cls):
--- a/eric6.e4p	Thu Feb 25 20:00:37 2016 +0100
+++ b/eric6.e4p	Fri Feb 26 20:16:59 2016 +0100
@@ -1360,6 +1360,19 @@
     <Source>WebBrowser/PersonalInformationManager/PersonalInformationManager.py</Source>
     <Source>WebBrowser/PersonalInformationManager/__init__.py</Source>
     <Source>WebBrowser/SearchWidget.py</Source>
+    <Source>WebBrowser/Sync/DirectorySyncHandler.py</Source>
+    <Source>WebBrowser/Sync/FtpSyncHandler.py</Source>
+    <Source>WebBrowser/Sync/SyncAssistantDialog.py</Source>
+    <Source>WebBrowser/Sync/SyncCheckPage.py</Source>
+    <Source>WebBrowser/Sync/SyncDataPage.py</Source>
+    <Source>WebBrowser/Sync/SyncDirectorySettingsPage.py</Source>
+    <Source>WebBrowser/Sync/SyncEncryptionPage.py</Source>
+    <Source>WebBrowser/Sync/SyncFtpSettingsPage.py</Source>
+    <Source>WebBrowser/Sync/SyncGlobals.py</Source>
+    <Source>WebBrowser/Sync/SyncHandler.py</Source>
+    <Source>WebBrowser/Sync/SyncHostTypePage.py</Source>
+    <Source>WebBrowser/Sync/SyncManager.py</Source>
+    <Source>WebBrowser/Sync/__init__.py</Source>
     <Source>WebBrowser/Tools/DelayedFileWatcher.py</Source>
     <Source>WebBrowser/Tools/Scripts.py</Source>
     <Source>WebBrowser/Tools/WebBrowserTools.py</Source>
@@ -1810,6 +1823,12 @@
     <Form>WebBrowser/Passwords/PasswordsDialog.ui</Form>
     <Form>WebBrowser/PersonalInformationManager/PersonalDataDialog.ui</Form>
     <Form>WebBrowser/SearchWidget.ui</Form>
+    <Form>WebBrowser/Sync/SyncCheckPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncDataPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncDirectorySettingsPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncEncryptionPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncFtpSettingsPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncHostTypePage.ui</Form>
     <Form>WebBrowser/UrlBar/BookmarkActionSelectionDialog.ui</Form>
     <Form>WebBrowser/UrlBar/BookmarkInfoDialog.ui</Form>
     <Form>WebBrowser/VirusTotal/VirusTotalDomainReportDialog.ui</Form>

eric ide

mercurial