Started adding code for a Flash cookie manager.

Sun, 09 Aug 2015 17:18:21 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 09 Aug 2015 17:18:21 +0200
changeset 4359
ac1dda9f3f19
parent 4357
815d1f3116ff
child 4360
b8bdb7cd4a92

Started adding code for a Flash cookie manager.

Globals/compatibility_fixes.py file | annotate | diff | comparison | revisions
Helpviewer/FlashCookieManager/FlashCookie.py file | annotate | diff | comparison | revisions
Helpviewer/FlashCookieManager/FlashCookieManager.py file | annotate | diff | comparison | revisions
Helpviewer/FlashCookieManager/FlashCookieNotification.py file | annotate | diff | comparison | revisions
Helpviewer/FlashCookieManager/FlashCookieUtilities.py file | annotate | diff | comparison | revisions
Helpviewer/FlashCookieManager/__init__.py file | annotate | diff | comparison | revisions
Helpviewer/HelpWindow.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationDialog.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/HelpFlashCookieManagerPage.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/HelpFlashCookieManagerPage.ui file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
icons/default/flashCookie.png file | annotate | diff | comparison | revisions
icons/default/flashCookie128.png file | annotate | diff | comparison | revisions
icons/default/flashCookie16.png file | annotate | diff | comparison | revisions
icons/default/flashCookie48.png file | annotate | diff | comparison | revisions
--- a/Globals/compatibility_fixes.py	Sun Aug 09 17:01:36 2015 +0200
+++ b/Globals/compatibility_fixes.py	Sun Aug 09 17:18:21 2015 +0200
@@ -10,7 +10,7 @@
 should throw a NotImplementedError exception.
 """
 
-import __builtin__
+import __builtin__      # __IGNORE_EXCEPTION__
 import codecs
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/FlashCookieManager/FlashCookie.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash cookie class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QDateTime
+
+
+class FlashCookie(object):
+    """
+    Class implementing the Flash cookie.
+    """
+    def __init(self):
+        """
+        Constructor
+        """
+        self.name = ""
+        self.origin = ""
+        self.size = 0
+        self.path = ""
+        self.contents = ""
+        self.lastModified = QDateTime()
+    
+    def __eq__(self, other):
+        """
+        Special method to compare to another Flash cookie.
+        
+        @param other reference to the other Flash cookie
+        @type FlashCookie
+        @return flag indicating equality of the two cookies
+        @rtype bool
+        """
+        return (self.name == other.name and
+                self.path == other.path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/FlashCookieManager/FlashCookieManager.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,348 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash cookie manager.
+"""
+
+from __future__ import unicode_literals
+
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import shutil
+
+from PyQt5.QtCore import QObject, QTimer, QDir, QFileInfo, QFile
+
+from .FlashCookie import FlashCookie
+
+import Helpviewer.HelpWindow
+
+import Preferences
+
+
+class FlashCookieManager(QObject):
+    """
+    Class implementing the Flash cookie manager object.
+    """
+    RefreshInterval = 60 * 1000
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(FlashCookieManager, self).__init__(parent)
+        
+        self.__flashCookieManagerDialog = None
+        self.__flashCookies = []    # list of FlashCookie
+        self.__newCookiesList = []  # list of str
+        self.__whitelist = []       # list of str
+        self.__blacklist = []       # list of str
+        
+        self.__timer = QTimer(self)
+        self.__timer.setInterval(FlashCookieManager.RefreshInterval)
+        self.__timer.timeout.connect(self.__autoRefresh)
+        
+        # start the timer if needed
+        self.__startStopTimer()
+        
+        if Preferences.getHelp("FlashCookiesDeleteOnStartExit"):
+            self.__loadFlashCookies()
+            self.__removeAllButWhitelisted()
+    
+    def shutdown(self):
+        """
+        Public method to perform shutdown actions.
+        """
+        if self.__flashCookieManagerDialog is not None:
+            self.__flashCookieManagerDialog.close()
+        
+        if Preferences.getHelp("FlashCookiesDeleteOnStartExit"):
+            self.__removeAllButWhitelisted()
+    
+    def setFlashCookies(self, cookies):
+        """
+        Public method to set the list of cached Flash cookies.
+        
+        @param cookies list of Flash cookies to store
+        @type list of FlashCookie
+        """
+        self.__flashCookies = cookies[:]
+    
+    def flashCookies(self):
+        """
+        Public method to get the list of cached Flash cookies.
+        
+        @return list of Flash cookies
+        @rtype list of FlashCookie
+        """
+        if not self.__flashCookies:
+            self.__loadFlashCookies()
+        
+        return self.__flashCookies[:]
+    
+    def newCookiesList(self):
+        """
+        Public method to get the list of newly detected Flash cookies.
+        
+        @return list of newly detected Flash cookies
+        @rtype list of str
+        """
+        return self.__newCookiesList[:]
+    
+    def clearNewOrigins(self):
+        """
+        Public method to clear the list of newly detected Flash cookies.
+        """
+        self.__newCookiesList = []
+    
+    def clearCache(self):
+        """
+        Public method to clear the list of cached Flash cookies.
+        """
+        self.__flashCookies = []
+    
+    def __isBlacklisted(self, cookie):
+        """
+        Private method to check for a blacklisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        """
+        return cookie.origin in Preferences.getHelp("FlashCookiesBlacklist")
+    
+    def __isWhitelisted(self, cookie):
+        """
+        Private method to check for a whitelisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        """
+        return cookie.origin in Preferences.getHelp("FlashCookiesWhitelist")
+    
+    def __removeAllButWhitelisted(self):
+        """
+        Private method to remove all non-whitelisted cookies.
+        """
+        for cookie in self.__flashCookies[:]:
+            if not self.__isWhitelisted(cookie):
+                self.removeCookie(cookie)
+    
+    def __sharedObjectDirName(self):
+        """
+        Private slot to determine the path of the shared data objects.
+        
+        @return path of the shared data objects
+        @rtype str
+        """
+        if "macromedia" in self.flashPlayerDataPath().lower() or \
+                "/.gnash" not in self.flashPlayerDataPath().lower():
+            return "/#SharedObjects/"
+        else:
+            return "/SharedObjects/"
+    
+    def flashPlayerDataPath(self):
+        """
+        Public method to get the Flash Player data path.
+        
+        @return Flash Player data path
+        @rtype str
+        """
+        return Preferences.getHelp("FlashCookiesDataPath")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__startStopTimer()
+    
+    def removeCookie(self, cookie):
+        """
+        Public method to remove a cookie of the list of cached cookies.
+        
+        @param cookie Flash cookie to be removed
+        @type FlashCookie
+        """
+        if cookie in self.__flashCookies:
+            self.__flashCookies.remove(cookie)
+            shutil.rmtree(cookie.path, True)
+    
+    def __autoRefresh(self):
+        """
+        Private slot to refresh the list of cookies.
+        """
+        if self.__flashCookieManagerDialog and \
+                self.__flashCookieManagerDialog.isVisible():
+            return
+        
+        oldFlashCookies = self.__flashCookies[:]
+        self.__loadFlashCookies()
+        newCookieList = []
+        
+        for cookie in self.__flashCookies[:]:
+            if self.__isBlacklisted(cookie):
+                self.removeCookie(cookie)
+                continue
+            
+            if self.__isWhitelisted(cookie):
+                continue
+            
+            newCookie = True
+            for oldCookie in oldFlashCookies:
+                if (oldCookie.path + oldCookie.name ==
+                    cookie.path + cookie.name):
+                    newCookie = False
+                    break
+            
+            if newCookie:
+                newCookieList.append(cookie.path + "/" + cookie.name)
+        
+        if newCookieList and Preferences.getHelp("FlashCookieNotify"):
+            self.__newCookiesList.extend(newCookieList)
+            win = Helpviewer.HelpWindow.HelpWindow.mainWindow()
+            if win is None:
+                return
+            
+            view = win.currentBrowser()
+            if view is None:
+                return
+            
+            from .FlashCookieNotification import FlashCookieNotification
+            notification = FlashCookieNotification(
+                view, self, len(newCookieList))
+            notification.show()
+    
+    def showFlashCookieManagerDialog(self):
+        """
+        Public method to show the Flash cookies management dialog.
+        """
+        if self.__flashCookieManagerDialog is None:
+            from .FlashCookieManagerDialog import FlashCookieManagerDialog
+            self.__flashCookieManagerDialog = FlashCookieManagerDialog(self)
+        
+        self.__flashCookieManagerDialog.refreshView()
+        self.__flashCookieManagerDialog.showPage(0)
+        self.__flashCookieManagerDialog.show()
+        self.__flashCookieManagerDialog.raise_()
+    
+    def __startStopTimer(self):
+        """
+        Private slot to start or stop the auto refresh timer.
+        """
+        if Preferences.getHelp("FlashCookieAutoRefresh"):
+            if not self.__timer.isActive():
+                if not bool(self.__flashCookies):
+                    self.__loadFlashCookies()
+                
+                self.__timer.start()
+        else:
+            self.__timer.stop()
+    
+    def __loadFlashCookies(self):
+        """
+        Private slot to load the Flash cookies to be cached.
+        """
+        self.__flashCookies = []
+        self.__loadFlashCookiesFromPath(self.flashPlayerDataPath())
+    
+    def __loadFlashCookiesFromPath(self, path):
+        """
+        Private slot to load the Flash cookies from a path.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        if path.endswith(("#SharedObjects", "#AppContainer")):
+            # specific to IE and Windows
+            return
+        
+        path = path.replace("\\", "/")
+        solDir = QDir(path)
+        entryList = solDir.entryList()
+        for entry in entryList:
+            if entry == "." or entry == "..":
+                continue
+            entryInfo = QFileInfo(path + "/" + entry)
+            if entryInfo.isDir():
+                self.__loadFlashCookiesFromPath(entryInfo.filePath())
+            else:
+                self.__insertFlashCookie(entryInfo.filePath())
+    
+    def __insertFlashCookie(self, path):
+        """
+        Private method to insert a Flash cookie into the cache.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        solFile = QFile(path)
+        if not solFile.open(QFile.ReadOnly):
+            return
+        
+        # TODO: dissect the flash cookie (see gnash s2x.py for example)
+        data = bytearray(solFile.readAll())
+        for i in range(len(data)):
+            if not ((data[i] >= ord("a") and data[i] <= ord("z")) or
+                    (data[i] >= ord("A") and data[i] <= ord("Z")) or
+                    (data[i] >= ord("0") and data[i] <= ord("9"))):
+                data[i] = 32
+        dataStr = str(data, "utf-8", "replace")
+        dataStr = "\n".join([s for s in dataStr.split(".") if s])
+        
+        solFileInfo = QFileInfo(solFile)
+        
+        cookie = FlashCookie()
+        cookie.contents = dataStr
+        cookie.name = solFileInfo.fileName()
+        cookie.path = solFileInfo.canonicalPath()
+        cookie.size = int(solFile.size())
+        cookie.lastModified = solFileInfo.lastModified()
+        cookie.origin = self.__extractOriginFrom(path)
+        
+        self.__flashCookies.append(cookie)
+    
+    def __extractOriginFrom(self, path):
+        """
+        Private method to extract the cookie origin given its file name.
+        
+        @param path file name of the cookie file
+        @type str
+        @return cookie origin
+        @rtype str
+        """
+        origin = path
+        if path.startswith(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName()):
+            origin = origin.replace(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName(), "")
+            if "/" in origin:
+                origin = origin.split("/", 1)[1]
+        elif path.startswith(
+            self.flashPlayerDataPath() + 
+                "/macromedia.com/support/flashplayer/sys/"):
+            origin = origin.replace(
+                self.flashPlayerDataPath() + 
+                "/macromedia.com/support/flashplayer/sys/", "")
+            if origin == "settings.sol":
+                return self.tr("!default")
+            elif origin.startswith("#"):
+                origin = origin[1:]
+        else:
+            origin = ""
+        
+        index = origin.find("/")
+        if index == -1:
+            return self.tr("!other")
+        
+        origin = origin[:index]
+        if origin in ["localhost", "local"]:
+            origin = "!localhost"
+        
+        return origin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/FlashCookieManager/FlashCookieNotification.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission bar widget.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton
+
+from E5Gui.E5AnimatedWidget import E5AnimatedWidget
+
+import UI.PixmapCache
+
+
+class FlashCookieNotification(E5AnimatedWidget):
+    """
+    Class implementing the feature permission bar widget.
+    """
+    DefaultHeight = 30
+    
+    def __init__(self, view, manager, noCookies):
+        """
+        Constructor
+        
+        @param view reference to the web view
+        @type QWebView
+        @param reference to the Flash cookie manager object
+        @type FlashCookieManager
+        @param noCookies number of newly detected Flash cookies
+        @type int
+        """
+        super(FlashCookieNotification, self).__init__(parent=view)
+        
+        self.__manager = manager
+        
+        if noCookies == 1:
+            msg = self.tr("A new flash cookie was detected.")
+        else:
+            msg = self.tr("{0} new flash cookies were detected.")\
+                .format(noCookies)
+        self.setAutoFillBackground(True)
+        self.__layout = QHBoxLayout()
+        self.setLayout(self.__layout)
+        self.__layout.setContentsMargins(9, 0, 0, 0)
+        self.__iconLabel = QLabel(self)
+        self.__iconLabel.setPixmap(UI.PixmapCache.getPixmap("flashCookie.png"))
+        self.__layout.addWidget(self.__iconLabel)
+        self.__messageLabel = QLabel(msg, self)
+        self.__layout.addWidget(self.__messageLabel)
+        self.__viewButton = QPushButton(self.tr("View"), self)
+        self.__layout.addWidget(self.__viewButton)
+        self.__layout.addStretch()
+        self.__discardButton = QPushButton(UI.PixmapCache.getIcon("close.png"),
+                                           "", self)
+        self.__layout.addWidget(self.__discardButton)
+        
+        self.__viewButton.clicked.connect(manager.showFlashCookieManagerDialog)
+        self.__viewButton.clicked.connect(self.hide)
+        self.__discardButton.clicked.connect(self.hide)
+        
+        self.resize(view.width(), self.height())
+        self.startAnimation()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/FlashCookieManager/FlashCookieUtilities.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some utility functions.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QProcessEnvironment
+
+import Globals
+
+
+def flashDataPathForOS():
+    """
+    Function to determine the OS dependent path where Flash cookies
+    are stored.
+    
+    @return Flash data path
+    @rtype str
+    """
+    # On Microsoft Windows NT 5.x and 6.x, they are stored in:
+    #  %APPDATA%\Macromedia\Flash Player\#SharedObjects\
+    # %APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys\
+    # On Mac OS X, they are stored in:
+    # ~/Library/Preferences/Macromedia/Flash Player/#SharedObjects/
+    # ~/Library/Preferences/Macromedia/Flash Player/macromedia.com/support/⏎
+    #   flashplayer/sys/
+    # On Linux or Unix, they are stored in:
+    # ~/.macromedia/Flash_Player/#SharedObjects/
+    # ~/.macromedia/Flash_Player/macromedia.com/support/flashplayer/sys/
+    # For Linux and Unix systems, if the open-source Gnash plugin is being used
+    #  instead of the official Adobe Flash, they will instead be found at:
+    # ~/.gnash/SharedObjects/
+    
+    flashPath = ""
+    
+    if Globals.isWindowsPlatform():
+        appData = QProcessEnvironment.systemEnvironment().value("APPDATA")
+        appData = appData.replace("\\", "/")
+        flashPath = appData + "/Macromedia/Flash Player"
+    elif Globals.isMacPlatform():
+        flashPath = os.path.expanduser(
+            "~/Library/Preferences/Macromedia/Flash Player")
+    else:
+        if os.path.exists(os.path.expanduser("~/.macromedia")):
+            flashPath = os.path.expanduser("~/.macromedia/Flash_Player")
+        elif os.path.exists(os.path.expanduser("~/.gnash")):
+            flashPath = os.path.expanduser("~/.gnash")
+    
+    return flashPath
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/FlashCookieManager/__init__.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the Flash cookie manager and associated objects.
+"""
--- a/Helpviewer/HelpWindow.py	Sun Aug 09 17:01:36 2015 +0200
+++ b/Helpviewer/HelpWindow.py	Sun Aug 09 17:18:21 2015 +0200
@@ -89,6 +89,7 @@
     _greaseMonkeyManager = None
     _notification = None
     _featurePermissionManager = None
+    _flashCookieManager = None
     
     def __init__(self, home, path, parent, name, fromEric=False,
                  initShortcutsOnly=False, searchWord=None):
@@ -287,11 +288,17 @@
             self.__previewer = None
             self.__shutdownCalled = False
             
+            self.flashCookieManager()
+            
             if self.useQtHelp:
                 QTimer.singleShot(0, self.__lookForNewDocumentation)
                 if self.__searchWord is not None:
                     QTimer.singleShot(0, self.__searchForWord)
             
+            self.__lastActiveWindow = None
+            e5App().focusChanged[QWidget, QWidget].connect(
+                self.__appFocusChanged)
+            
             QTimer.singleShot(0, syncMgr.loadSettings)
     
     def __del__(self):
@@ -2230,6 +2237,8 @@
         
         self.__virusTotal.close()
         
+        self.flashCookieManager().shutdown()
+        
         self.searchEdit.openSearchManager().close()
         
         if self.useQtHelp:
@@ -3301,6 +3310,21 @@
         return cls._featurePermissionManager
         
     @classmethod
+    def flashCookieManager(cls):
+        """
+        Class method to get a reference to the flash cookies manager.
+        
+        @return reference to the feature permission manager
+        @rtype FlashCookieManager
+        """
+        if cls._flashCookieManager is None:
+            from .FlashCookieManager.FlashCookieManager import \
+                FlashCookieManager
+            cls._flashCookieManager = FlashCookieManager()
+        
+        return cls._flashCookieManager
+        
+    @classmethod
     def mainWindow(cls):
         """
         Class method to get a reference to the main window.
@@ -3321,6 +3345,28 @@
         """
         return cls.helpwindows
         
+    def __appFocusChanged(self, old, now):
+        """
+        Private slot to handle a change of the focus.
+        
+        @param old reference to the widget, that lost focus (QWidget or None)
+        @param now reference to the widget having the focus (QWidget or None)
+        """
+        if isinstance(now, HelpWindow):
+            self.__lastActiveWindow = now
+        
+    def getWindow(self):
+        """
+        Public method to get a reference to the most recent active help window.
+        
+        @return reference to most recent help window
+        @rtype HelpWindow
+        """
+        if self.__lastActiveWindow:
+            return self.__lastActiveWindow
+        
+        return self.mainWindow()
+        
     def openSearchManager(self):
         """
         Public method to get a reference to the opensearch manager object.
--- a/Preferences/ConfigurationDialog.py	Sun Aug 09 17:01:36 2015 +0200
+++ b/Preferences/ConfigurationDialog.py	Sun Aug 09 17:18:21 2015 +0200
@@ -59,7 +59,7 @@
         """
         return self.__pageName
 
-
+# TODO: add page for Flash Cookie Manager
 class ConfigurationWidget(QWidget):
     """
     Class implementing a dialog for the configuration of eric6.
@@ -287,6 +287,10 @@
                 [self.tr("Help Documentation"),
                  "preferences-helpdocumentation.png",
                  "HelpDocumentationPage", "0helpPage", None],
+                "helpFlashCookieManagerPage":
+                [self.tr("Flash Cookie Manager"),
+                 "flashCookie16.png",
+                 "HelpFlashCookieManagerPage", "0helpPage", None],
                 "helpViewersPage":
                 [self.tr("Help Viewers"),
                  "preferences-helpviewers.png",
@@ -357,6 +361,10 @@
                 [self.tr("Help Documentation"),
                  "preferences-helpdocumentation.png",
                  "HelpDocumentationPage", "0helpPage", None],
+                "helpFlashCookieManagerPage":
+                [self.tr("Flash Cookie Manager"),
+                 "flashCookie16.png",
+                 "HelpFlashCookieManagerPage", "0helpPage", None],
                 "helpVirusTotalPage":
                 [self.tr("VirusTotal Interface"), "virustotal.png",
                  "HelpVirusTotalPage", "0helpPage", None],
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/HelpFlashCookieManagerPage.py	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash Cookies Manager configuration page.
+"""
+
+from PyQt5.QtCore import pyqtSlot
+
+from E5Gui.E5Completers import E5DirCompleter
+from E5Gui import E5FileDialog
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_HelpFlashCookieManagerPage import Ui_HelpFlashCookieManagerPage
+
+import Preferences
+import Utilities
+import UI.PixmapCache
+
+
+class HelpFlashCookieManagerPage(ConfigurationPageBase,
+                                 Ui_HelpFlashCookieManagerPage):
+    """
+    Class implementing the Flash Cookies Manager configuration page.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(HelpFlashCookieManagerPage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("HelpFlashCookieManagerPage")
+        
+        self.flashDataPathButton.setIcon(UI.PixmapCache.getIcon("open.png"))
+        
+        self.flashDataPathCompleter = E5DirCompleter(self.flashDataPathEdit)
+        
+        # set initial values
+        self.flashDataPathEdit.setText(
+            Preferences.getHelp("FlashCookiesDataPath"))
+        self.autoModeGroup.setChecked(
+            Preferences.getHelp("FlashCookieAutoRefresh"))
+        self.notificationGroup.setChecked(
+            Preferences.getHelp("FlashCookieNotify"))
+        self.deleteGroup.setChecked(
+            Preferences.getHelp("FlashCookiesDeleteOnStartExit"))
+    
+    def save(self):
+        """
+        Public slot to save the Flash Cookies Manager configuration.
+        """
+        Preferences.setHelp("FlashCookiesDataPath",
+                            self.flashDataPathEdit.text())
+        Preferences.setHelp("FlashCookieAutoRefresh",
+                            self.autoModeGroup.isChecked())
+        Preferences.setHelp("FlashCookieNotify",
+                            self.notificationGroup.isChecked())
+        Preferences.setHelp("FlashCookiesDeleteOnStartExit",
+                            self.deleteGroup.isChecked())
+    
+    @pyqtSlot()
+    def on_flashDataPathButton_clicked(self):
+        """
+        Private slot to handle the flash data path selection.
+        """
+        path = E5FileDialog.getExistingDirectory(
+            self,
+            self.tr("Select Flash Cookies Data Path"),
+            self.flashDataPathEdit.text(),
+            E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
+        
+        if path:
+            self.flashDataPathEdit.setText(Utilities.toNativeSeparators(path))
+    
+
+def create(dlg):
+    """
+    Module function to create the configuration page.
+    
+    @param dlg reference to the configuration dialog
+    @return reference to the instantiated page (ConfigurationPageBase)
+    """
+    page = HelpFlashCookieManagerPage()
+    return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/HelpFlashCookieManagerPage.ui	Sun Aug 09 17:18:21 2015 +0200
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HelpFlashCookieManagerPage</class>
+ <widget class="QWidget" name="HelpFlashCookieManagerPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>588</width>
+    <height>419</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure Flash Cookies Manager&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line9">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Flash Data Path</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLineEdit" name="flashDataPathEdit"/>
+      </item>
+      <item row="0" column="1">
+       <widget class="QToolButton" name="flashDataPathButton">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="2">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>&lt;b&gt;Note&lt;/b&gt;: You should select the directory that contains #SharedObject as a subfolder.</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="autoModeGroup">
+     <property name="toolTip">
+      <string>Select to activate auto mode</string>
+     </property>
+     <property name="title">
+      <string>Auto Mode Enabled</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="autoModeLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>The flash data directory will be checked regularly. and flash cookies in blacklist will be deleted automatically.</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="notificationGroup">
+     <property name="toolTip">
+      <string>Select to show a notification upon detection of new flash cookies</string>
+     </property>
+     <property name="title">
+      <string>Notifications enabled</string>
+     </property>
+     <property name="flat">
+      <bool>false</bool>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <widget class="QLabel" name="notificationLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>User will be notified for every new flash cookie that is not in blacklist and whitelist.</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="deleteGroup">
+     <property name="toolTip">
+      <string>Select to delete all flash cookies on startup and shutdown</string>
+     </property>
+     <property name="title">
+      <string>Delete</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_3">
+      <item>
+       <widget class="QLabel" name="deleteLabel">
+        <property name="text">
+         <string>Delete all flash cookies on exit/start (except those are in whitelist)</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_19">
+     <property name="text">
+      <string>&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; These settings are just applied to flash cookies.&lt;/p&gt;</string>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+     <property name="openExternalLinks">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>39</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>flashDataPathEdit</tabstop>
+  <tabstop>flashDataPathButton</tabstop>
+  <tabstop>autoModeGroup</tabstop>
+  <tabstop>notificationGroup</tabstop>
+  <tabstop>deleteGroup</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/Preferences/__init__.py	Sun Aug 09 17:01:36 2015 +0200
+++ b/Preferences/__init__.py	Sun Aug 09 17:18:21 2015 +0200
@@ -48,6 +48,9 @@
     ResourcesBrowserFlag, TranslationsBrowserFlag, InterfacesBrowserFlag, \
     OthersBrowserFlag, AllBrowsersFlag
 
+from Helpviewer.FlashCookieManager.FlashCookieUtilities import \
+    flashDataPathForOS
+
 
 class Prefs(object):
     """
@@ -889,6 +892,13 @@
         "PimSpecial3": "",
         "PimSpecial4": "",
         "GreaseMonkeyDisabledScripts": [],
+        # Flash Cookie Manager
+        "FlashCookiesDeleteOnStartExit": False,
+        "FlashCookieAutoRefresh": False,
+        "FlashCookieNotify": False,
+        "FlashCookiesWhitelist": [],
+        "FlashCookiesBlacklist": [],
+        "FlashCookiesDataPath": flashDataPathForOS(),
     }
     
     @classmethod
@@ -2411,13 +2421,17 @@
                  "SyncHistory", "SyncPasswords", "SyncUserAgents",
                  "SyncSpeedDial", "SyncEncryptData",
                  "SyncEncryptPasswordsOnly",
-                 "WarnOnMultipleClose", "ClickToFlashEnabled"
+                 "WarnOnMultipleClose", "ClickToFlashEnabled",
+                 "FlashCookiesDeleteOnStartExit", "FlashCookieAutoRefresh",
+                 "FlashCookieNotify",
                  ]:
         return toBool(prefClass.settings.value(
             "Help/" + key, prefClass.helpDefaults[key]))
     elif key in ["AdBlockSubscriptions", "AdBlockExceptions",
                  "ClickToFlashWhitelist", "SendRefererWhitelist",
-                 "GreaseMonkeyDisabledScripts", "NoCacheHosts"]:
+                 "GreaseMonkeyDisabledScripts", "NoCacheHosts",
+                 "FlashCookiesWhitelist", "FlashCookiesBlacklist",
+                 ]:
         return toList(prefClass.settings.value(
             "Help/" + key, prefClass.helpDefaults[key]))
     else:
--- a/eric6.e4p	Sun Aug 09 17:01:36 2015 +0200
+++ b/eric6.e4p	Sun Aug 09 17:18:21 2015 +0200
@@ -26,6 +26,46 @@
     <Source>DataViews/PyCoverageDialog.py</Source>
     <Source>DataViews/PyProfileDialog.py</Source>
     <Source>DataViews/__init__.py</Source>
+    <Source>DebugClients/Python/AsyncFile.py</Source>
+    <Source>DebugClients/Python/AsyncIO.py</Source>
+    <Source>DebugClients/Python/DCTestResult.py</Source>
+    <Source>DebugClients/Python/DebugBase.py</Source>
+    <Source>DebugClients/Python/DebugClient.py</Source>
+    <Source>DebugClients/Python/DebugClientBase.py</Source>
+    <Source>DebugClients/Python/DebugClientCapabilities.py</Source>
+    <Source>DebugClients/Python/DebugClientThreads.py</Source>
+    <Source>DebugClients/Python/DebugConfig.py</Source>
+    <Source>DebugClients/Python/DebugProtocol.py</Source>
+    <Source>DebugClients/Python/DebugThread.py</Source>
+    <Source>DebugClients/Python/FlexCompleter.py</Source>
+    <Source>DebugClients/Python/PyProfile.py</Source>
+    <Source>DebugClients/Python/__init__.py</Source>
+    <Source>DebugClients/Python/coverage/__init__.py</Source>
+    <Source>DebugClients/Python/coverage/__main__.py</Source>
+    <Source>DebugClients/Python/coverage/annotate.py</Source>
+    <Source>DebugClients/Python/coverage/backward.py</Source>
+    <Source>DebugClients/Python/coverage/bytecode.py</Source>
+    <Source>DebugClients/Python/coverage/cmdline.py</Source>
+    <Source>DebugClients/Python/coverage/codeunit.py</Source>
+    <Source>DebugClients/Python/coverage/collector.py</Source>
+    <Source>DebugClients/Python/coverage/config.py</Source>
+    <Source>DebugClients/Python/coverage/control.py</Source>
+    <Source>DebugClients/Python/coverage/data.py</Source>
+    <Source>DebugClients/Python/coverage/debug.py</Source>
+    <Source>DebugClients/Python/coverage/execfile.py</Source>
+    <Source>DebugClients/Python/coverage/files.py</Source>
+    <Source>DebugClients/Python/coverage/html.py</Source>
+    <Source>DebugClients/Python/coverage/misc.py</Source>
+    <Source>DebugClients/Python/coverage/parser.py</Source>
+    <Source>DebugClients/Python/coverage/phystokens.py</Source>
+    <Source>DebugClients/Python/coverage/report.py</Source>
+    <Source>DebugClients/Python/coverage/results.py</Source>
+    <Source>DebugClients/Python/coverage/summary.py</Source>
+    <Source>DebugClients/Python/coverage/templite.py</Source>
+    <Source>DebugClients/Python/coverage/version.py</Source>
+    <Source>DebugClients/Python/coverage/xmlreport.py</Source>
+    <Source>DebugClients/Python/eric6dbgstub.py</Source>
+    <Source>DebugClients/Python/getpass.py</Source>
     <Source>DebugClients/Python3/AsyncFile.py</Source>
     <Source>DebugClients/Python3/AsyncIO.py</Source>
     <Source>DebugClients/Python3/DCTestResult.py</Source>
@@ -66,46 +106,6 @@
     <Source>DebugClients/Python3/coverage/xmlreport.py</Source>
     <Source>DebugClients/Python3/eric6dbgstub.py</Source>
     <Source>DebugClients/Python3/getpass.py</Source>
-    <Source>DebugClients/Python/AsyncFile.py</Source>
-    <Source>DebugClients/Python/AsyncIO.py</Source>
-    <Source>DebugClients/Python/DCTestResult.py</Source>
-    <Source>DebugClients/Python/DebugBase.py</Source>
-    <Source>DebugClients/Python/DebugClient.py</Source>
-    <Source>DebugClients/Python/DebugClientBase.py</Source>
-    <Source>DebugClients/Python/DebugClientCapabilities.py</Source>
-    <Source>DebugClients/Python/DebugClientThreads.py</Source>
-    <Source>DebugClients/Python/DebugConfig.py</Source>
-    <Source>DebugClients/Python/DebugProtocol.py</Source>
-    <Source>DebugClients/Python/DebugThread.py</Source>
-    <Source>DebugClients/Python/FlexCompleter.py</Source>
-    <Source>DebugClients/Python/PyProfile.py</Source>
-    <Source>DebugClients/Python/__init__.py</Source>
-    <Source>DebugClients/Python/coverage/__init__.py</Source>
-    <Source>DebugClients/Python/coverage/__main__.py</Source>
-    <Source>DebugClients/Python/coverage/annotate.py</Source>
-    <Source>DebugClients/Python/coverage/backward.py</Source>
-    <Source>DebugClients/Python/coverage/bytecode.py</Source>
-    <Source>DebugClients/Python/coverage/cmdline.py</Source>
-    <Source>DebugClients/Python/coverage/codeunit.py</Source>
-    <Source>DebugClients/Python/coverage/collector.py</Source>
-    <Source>DebugClients/Python/coverage/config.py</Source>
-    <Source>DebugClients/Python/coverage/control.py</Source>
-    <Source>DebugClients/Python/coverage/data.py</Source>
-    <Source>DebugClients/Python/coverage/debug.py</Source>
-    <Source>DebugClients/Python/coverage/execfile.py</Source>
-    <Source>DebugClients/Python/coverage/files.py</Source>
-    <Source>DebugClients/Python/coverage/html.py</Source>
-    <Source>DebugClients/Python/coverage/misc.py</Source>
-    <Source>DebugClients/Python/coverage/parser.py</Source>
-    <Source>DebugClients/Python/coverage/phystokens.py</Source>
-    <Source>DebugClients/Python/coverage/report.py</Source>
-    <Source>DebugClients/Python/coverage/results.py</Source>
-    <Source>DebugClients/Python/coverage/summary.py</Source>
-    <Source>DebugClients/Python/coverage/templite.py</Source>
-    <Source>DebugClients/Python/coverage/version.py</Source>
-    <Source>DebugClients/Python/coverage/xmlreport.py</Source>
-    <Source>DebugClients/Python/eric6dbgstub.py</Source>
-    <Source>DebugClients/Python/getpass.py</Source>
     <Source>DebugClients/Ruby/AsyncFile.rb</Source>
     <Source>DebugClients/Ruby/AsyncIO.rb</Source>
     <Source>DebugClients/Ruby/Completer.rb</Source>
@@ -312,6 +312,11 @@
     <Source>Helpviewer/Feeds/FeedsDialog.py</Source>
     <Source>Helpviewer/Feeds/FeedsManager.py</Source>
     <Source>Helpviewer/Feeds/__init__.py</Source>
+    <Source>Helpviewer/FlashCookieManager/FlashCookie.py</Source>
+    <Source>Helpviewer/FlashCookieManager/FlashCookieManager.py</Source>
+    <Source>Helpviewer/FlashCookieManager/FlashCookieNotification.py</Source>
+    <Source>Helpviewer/FlashCookieManager/FlashCookieUtilities.py</Source>
+    <Source>Helpviewer/FlashCookieManager/__init__.py</Source>
     <Source>Helpviewer/GreaseMonkey/GreaseMonkeyAddScriptDialog.py</Source>
     <Source>Helpviewer/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py</Source>
     <Source>Helpviewer/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py</Source>
@@ -766,6 +771,7 @@
     <Source>Preferences/ConfigurationPages/GraphicsPage.py</Source>
     <Source>Preferences/ConfigurationPages/HelpAppearancePage.py</Source>
     <Source>Preferences/ConfigurationPages/HelpDocumentationPage.py</Source>
+    <Source>Preferences/ConfigurationPages/HelpFlashCookieManagerPage.py</Source>
     <Source>Preferences/ConfigurationPages/HelpInterfacePage.py</Source>
     <Source>Preferences/ConfigurationPages/HelpViewersPage.py</Source>
     <Source>Preferences/ConfigurationPages/HelpVirusTotalPage.py</Source>
@@ -1285,7 +1291,6 @@
     <Form>E5Network/E5SslCertificatesInfoDialog.ui</Form>
     <Form>E5Network/E5SslCertificatesInfoWidget.ui</Form>
     <Form>Graphics/UMLSceneSizeDialog.ui</Form>
-    <Form>Helpviewer/FeaturePermissions/FeaturePermissionsDialog.ui</Form>
     <Form>Helpviewer/AdBlock/AdBlockDialog.ui</Form>
     <Form>Helpviewer/AdBlock/AdBlockExceptionsDialog.ui</Form>
     <Form>Helpviewer/Bookmarks/AddBookmarkDialog.ui</Form>
@@ -1299,6 +1304,7 @@
     <Form>Helpviewer/Download/DownloadAskActionDialog.ui</Form>
     <Form>Helpviewer/Download/DownloadItem.ui</Form>
     <Form>Helpviewer/Download/DownloadManager.ui</Form>
+    <Form>Helpviewer/FeaturePermissions/FeaturePermissionsDialog.ui</Form>
     <Form>Helpviewer/Feeds/FeedEditDialog.ui</Form>
     <Form>Helpviewer/Feeds/FeedsDialog.ui</Form>
     <Form>Helpviewer/Feeds/FeedsManager.ui</Form>
@@ -1514,6 +1520,7 @@
     <Form>Preferences/ConfigurationPages/GraphicsPage.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpAppearancePage.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpDocumentationPage.ui</Form>
+    <Form>Preferences/ConfigurationPages/HelpFlashCookieManagerPage.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpInterfacePage.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpViewersPage.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpVirusTotalPage.ui</Form>
@@ -1632,14 +1639,14 @@
   <Interfaces/>
   <Others>
     <Other>.hgignore</Other>
+    <Other>APIs/Python/zope-2.10.7.api</Other>
+    <Other>APIs/Python/zope-2.11.2.api</Other>
+    <Other>APIs/Python/zope-3.3.1.api</Other>
     <Other>APIs/Python3/PyQt4.bas</Other>
     <Other>APIs/Python3/PyQt5.bas</Other>
     <Other>APIs/Python3/QScintilla2.bas</Other>
     <Other>APIs/Python3/eric6.api</Other>
     <Other>APIs/Python3/eric6.bas</Other>
-    <Other>APIs/Python/zope-2.10.7.api</Other>
-    <Other>APIs/Python/zope-2.11.2.api</Other>
-    <Other>APIs/Python/zope-3.3.1.api</Other>
     <Other>APIs/QSS/qss.api</Other>
     <Other>APIs/Ruby/Ruby-1.8.7.api</Other>
     <Other>APIs/Ruby/Ruby-1.8.7.bas</Other>
@@ -1650,10 +1657,10 @@
     <Other>CSSs</Other>
     <Other>CodeTemplates</Other>
     <Other>DTDs</Other>
+    <Other>DebugClients/Python/coverage/doc</Other>
+    <Other>DebugClients/Python/coverage/htmlfiles</Other>
     <Other>DebugClients/Python3/coverage/doc</Other>
     <Other>DebugClients/Python3/coverage/htmlfiles</Other>
-    <Other>DebugClients/Python/coverage/doc</Other>
-    <Other>DebugClients/Python/coverage/htmlfiles</Other>
     <Other>DesignerTemplates</Other>
     <Other>Dictionaries</Other>
     <Other>Documentation/Help</Other>
Binary file icons/default/flashCookie.png has changed
Binary file icons/default/flashCookie128.png has changed
Binary file icons/default/flashCookie16.png has changed
Binary file icons/default/flashCookie48.png has changed

eric ide

mercurial