Fri, 26 Feb 2016 20:16:59 +0100
Continued porting the web browser.
- started adding the Sync stuff
--- 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><p>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.<br/><br/><b>Note: If you forget the encryption key, the encrypted data cannot be recovered!</b><br/></p></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>