class NetworkManager(QNetworkAccessManager): """ Class implementing a network manager. @signal changed() emitted to indicate a change """ changed = pyqtSignal() def __init__(self, engine, parent=None): """ Constructor @param engine reference to the help engine (QHelpEngine) @param parent reference to the parent object (QObject) """ super(NetworkManager, self).__init__(parent) from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory self.__proxyFactory = E5NetworkProxyFactory() if Preferences.getUI("UseSystemProxy"): QNetworkProxyFactory.setUseSystemConfiguration(True) else: QNetworkProxyFactory.setApplicationProxyFactory( self.__proxyFactory) QNetworkProxyFactory.setUseSystemConfiguration(False) self.languagesChanged() if SSL_AVAILABLE: self.__sslErrorHandler = E5SslErrorHandler(self) self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot) self.__temporarilyIgnoredSslErrors = {} self.__permanentlyIgnoredSslErrors = {} # dictionaries of permanently and temporarily ignored SSL errors self.__loaded = False self.__saveTimer = AutoSaver(self, self.__save) self.changed.connect(self.__saveTimer.changeOccurred) self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired) self.authenticationRequired.connect( lambda reply, auth: self.authentication(reply.url(), auth)) from .EricSchemeHandler import EricSchemeHandler self.__ericSchemeHandler = EricSchemeHandler() WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"eric"), self.__ericSchemeHandler) if engine: from .QtHelpSchemeHandler import QtHelpSchemeHandler self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine) WebBrowserWindow.webProfile().installUrlSchemeHandler( QByteArray(b"qthelp"), self.__qtHelpSchemeHandler) self.__interceptor = NetworkUrlInterceptor(self) try: WebBrowserWindow.webProfile().setUrlRequestInterceptor( self.__interceptor) except AttributeError: # Qt < 5.13 WebBrowserWindow.webProfile().setRequestInterceptor( self.__interceptor) WebBrowserWindow.cookieJar() def __save(self): """ Private slot to save the permanent SSL error exceptions. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): dbString = json.dumps(self.__permanentlyIgnoredSslErrors) Preferences.setWebBrowser("SslExceptionsDB", dbString) def __load(self): """ Private method to load the permanent SSL error exceptions. """ if self.__loaded: return dbString = Preferences.getWebBrowser("SslExceptionsDB") if dbString: try: db = json.loads(dbString) self.__permanentlyIgnoredSslErrors = db except ValueError: # ignore silently pass self.__loaded = True def shutdown(self): """ Public method to shut down the network manager. """ self.__saveTimer.saveIfNeccessary() self.__loaded = False self.__temporarilyIgnoredSslErrors = {} self.__permanentlyIgnoredSslErrors = {} # set proxy factory to None to avoid crashes QNetworkProxyFactory.setApplicationProxyFactory(None) def showSslErrorExceptionsDialog(self): """ Public method to show the SSL error exceptions dialog. """ self.__load() from .SslErrorExceptionsDialog import SslErrorExceptionsDialog dlg = SslErrorExceptionsDialog(self.__permanentlyIgnoredSslErrors) if dlg.exec_() == QDialog.Accepted: self.__permanentlyIgnoredSslErrors = dlg.getSslErrorExceptions() self.changed.emit() def clearSslExceptions(self): """ Public method to clear the permanent SSL certificate error exceptions. """ self.__load() self.__permanentlyIgnoredSslErrors = {} self.changed.emit() self.__saveTimer.saveIfNeccessary() def certificateError(self, error, view): """ Public method to handle SSL certificate errors. @param error object containing the certificate error information @type QWebEngineCertificateError @param view reference to a view to be used as parent for the dialog @type QWidget @return flag indicating to ignore this error @rtype bool """ self.__load() host = error.url().host() if ( host in self.__temporarilyIgnoredSslErrors and error.error() in self.__temporarilyIgnoredSslErrors[host] ): return True if ( host in self.__permanentlyIgnoredSslErrors and error.error() in self.__permanentlyIgnoredSslErrors[host] ): return True title = self.tr("SSL Certificate Error") msgBox = E5MessageBox.E5MessageBox( E5MessageBox.Warning, title, self.tr("""<b>{0}</b>""" """<p>The page you are trying to access has errors""" """ in the SSL certificate.</p>""" """<ul><li>{1}</li></ul>""" """<p>Would you like to make an exception?</p>""") .format(title, error.errorDescription()), modal=True, parent=view) permButton = msgBox.addButton(self.tr("&Permanent accept"), E5MessageBox.AcceptRole) tempButton = msgBox.addButton(self.tr("&Temporary accept"), E5MessageBox.AcceptRole) msgBox.addButton(self.tr("&Reject"), E5MessageBox.RejectRole) msgBox.exec_() if msgBox.clickedButton() == permButton: if host not in self.__permanentlyIgnoredSslErrors: self.__permanentlyIgnoredSslErrors[host] = [] self.__permanentlyIgnoredSslErrors[host].append(error.error()) self.changed.emit() return True elif msgBox.clickedButton() == tempButton: if host not in self.__temporarilyIgnoredSslErrors: self.__temporarilyIgnoredSslErrors[host] = [] self.__temporarilyIgnoredSslErrors[host].append(error.error()) return True else: return False def authentication(self, url, auth, page=None): """ Public slot to handle an authentication request. @param url URL requesting authentication @type QUrl @param auth reference to the authenticator object @type QAuthenticator @param page reference to the web page @type QWebEnginePage or None """ urlRoot = ( "{0}://{1}".format(url.scheme(), url.authority()) ) realm = auth.realm() if not realm and 'realm' in auth.options(): realm = auth.option("realm") if realm: info = self.tr( "<b>Enter username and password for '{0}', realm '{1}'</b>" ).format(urlRoot, realm) else: info = self.tr( "<b>Enter username and password for '{0}'</b>" ).format(urlRoot) from UI.AuthenticationDialog import AuthenticationDialog import WebBrowser.WebBrowserWindow dlg = AuthenticationDialog(info, auth.user(), Preferences.getUser("SavePasswords"), Preferences.getUser("SavePasswords")) if Preferences.getUser("SavePasswords"): username, password = ( WebBrowser.WebBrowserWindow.WebBrowserWindow .passwordManager().getLogin(url, realm) ) if username: dlg.setData(username, password) if dlg.exec_() == QDialog.Accepted: username, password = dlg.getData() auth.setUser(username) auth.setPassword(password) if Preferences.getUser("SavePasswords") and dlg.shallSave(): ( WebBrowser.WebBrowserWindow.WebBrowserWindow .passwordManager().setLogin( url, realm, username, password) ) else: if page is not None: self.__showAuthenticationErrorPage(page, url) def __showAuthenticationErrorPage(self, page, url): """ Private method to show an authentication error page. @param page reference to the page @type QWebEnginePage @param url reference to the URL requesting authentication @type QUrl """ html = readAllFileContents(":/html/authenticationErrorPage.html") html = html.replace("@IMAGE@", pixmapToDataUrl( qApp.style().standardIcon(QStyle.SP_MessageBoxCritical).pixmap( 48, 48)).toString()) html = html.replace("@FAVICON@", pixmapToDataUrl( qApp.style() .standardIcon(QStyle.SP_MessageBoxCritical).pixmap( 16, 16)).toString()) html = html.replace("@TITLE@", self.tr("Authentication required")) html = html.replace("@H1@", self.tr("Authentication required")) html = html.replace( "@LI-1@", self.tr("Authentication is required to access:")) html = html.replace( "@LI-2@", '<a href="{0}">{0}</a>'.format(url.toString())) page.setHtml(html, url) def proxyAuthentication(self, requestUrl, auth, proxyHost): """ Public slot to handle a proxy authentication request. @param requestUrl requested URL @type QUrl @param auth reference to the authenticator object @type QAuthenticator @param proxyHost name of the proxy host @type str """ proxy = QNetworkProxy.applicationProxy() if proxy.user() and proxy.password(): auth.setUser(proxy.user()) auth.setPassword(proxy.password()) return proxyAuthenticationRequired(proxy, auth) def languagesChanged(self): """ Public slot to (re-)load the list of accepted languages. """ from WebBrowser.WebBrowserLanguagesDialog import ( WebBrowserLanguagesDialog ) languages = Preferences.toList( Preferences.Prefs.settings.value( "WebBrowser/AcceptLanguages", WebBrowserLanguagesDialog.defaultAcceptLanguages())) self.__acceptLanguage = WebBrowserLanguagesDialog.httpString(languages) WebBrowserWindow.webProfile().setHttpAcceptLanguage( self.__acceptLanguage) def installUrlInterceptor(self, interceptor): """ Public method to install an URL interceptor. @param interceptor URL interceptor to be installed @type UrlInterceptor """ self.__interceptor.installUrlInterceptor(interceptor) def removeUrlInterceptor(self, interceptor): """ Public method to remove an URL interceptor. @param interceptor URL interceptor to be removed @type UrlInterceptor """ self.__interceptor.removeUrlInterceptor(interceptor) def preferencesChanged(self): """ Public slot to handle a change of preferences. """ self.__interceptor.preferencesChanged() if Preferences.getUI("UseSystemProxy"): QNetworkProxyFactory.setUseSystemConfiguration(True) else: QNetworkProxyFactory.setApplicationProxyFactory( self.__proxyFactory) QNetworkProxyFactory.setUseSystemConfiguration(False) def createRequest(self, op, request, data): """ Public method to launch a network action. @param op operation to be performed @type QNetworkAccessManager.Operation @param request request to be operated on @type QNetworkRequest @param data reference to the data to be sent @type QIODevice @return reference to the network reply @rtype QNetworkReply """ req = QNetworkRequest(request) req.setAttribute(QNetworkRequest.SpdyAllowedAttribute, True) req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True) return super(NetworkManager, self).createRequest(op, req, data)
class UserAgentManager(QObject): """ Class implementing a user agent manager. @signal changed() emitted to indicate a change @signal userAgentSettingsSaved() emitted after the user agent settings were saved """ changed = pyqtSignal() userAgentSettingsSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(UserAgentManager, self).__init__(parent) self.__agents = {} # dictionary with agent strings indexed by host name self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "userAgentSettings.xml") def save(self): """ Public slot to save the user agent entries to disk. """ if not self.__loaded: return from .UserAgentWriter import UserAgentWriter agentFile = self.getFileName() writer = UserAgentWriter() if not writer.write(agentFile, self.__agents): E5MessageBox.critical( None, self.tr("Saving user agent data"), self.tr("""<p>User agent data could not be saved to""" """ <b>{0}</b></p>""").format(agentFile)) else: self.userAgentSettingsSaved.emit() def __load(self): """ Private method to load the saved user agent settings. """ agentFile = self.getFileName() from .UserAgentReader import UserAgentReader reader = UserAgentReader() self.__agents = reader.read(agentFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading user agent data"), self.tr("""Error when loading user agent data on""" """ line {0}, column {1}:\n{2}""").format( reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def reload(self): """ Public method to reload the user agent settings. """ if not self.__loaded: return self.__agents = {} self.__load() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def removeUserAgent(self, host): """ Public method to remove a user agent entry. @param host host name (string) """ if host in self.__agents: del self.__agents[host] self.changed.emit() def allHostNames(self): """ Public method to get a list of all host names we a user agent setting for. @return sorted list of all host names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__agents.keys()) def hostsCount(self): """ Public method to get the number of available user agent settings. @return number of user agent settings (integer) """ if not self.__loaded: self.__load() return len(self.__agents) def userAgent(self, host): """ Public method to get the user agent setting for a host. @param host host name (string) @return user agent string (string) """ if not self.__loaded: self.__load() for agentHost in self.__agents: if host.endswith(agentHost): return self.__agents[agentHost] return "" def setUserAgent(self, host, agent): """ Public method to set the user agent string for a host. @param host host name (string) @param agent user agent string (string) """ if host != "" and agent != "": self.__agents[host] = agent self.changed.emit() def userAgentForUrl(self, url): """ Public method to determine the user agent for the given URL. @param url URL to determine user agent for (QUrl) @return user agent string (string) """ if url.isValid(): host = url.host() return self.userAgent(host) return "" def setUserAgentForUrl(self, url, agent): """ Public method to set the user agent string for an URL. @param url URL to register user agent setting for (QUrl) @param agent new current user agent string (string) """ if url.isValid(): host = url.host() self.setUserAgent(host, agent)
class IrcNetworkManager(QObject): """ Class implementing the IRC identity object. @signal dataChanged() emitted after some data has changed @signal networksChanged() emitted after a network object has changed @signal identitiesChanged() emitted after an identity object has changed """ dataChanged = pyqtSignal() networksChanged = pyqtSignal() identitiesChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(IrcNetworkManager, self).__init__(parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__settings = Preferences.Prefs.settings self.__networks = {} self.__identities = {} self.dataChanged.connect(self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def save(self): """ Public slot to save the IRC data. """ if not self.__loaded: return # save IRC data self.__settings.beginGroup("IRC") # identities self.__settings.remove("Identities") self.__settings.beginGroup("Identities") for key in self.__identities: self.__settings.beginGroup(key) self.__identities[key].save(self.__settings) self.__settings.endGroup() self.__settings.endGroup() # networks self.__settings.remove("Networks") self.__settings.beginGroup("Networks") for key in self.__networks: self.__settings.beginGroup(key) self.__networks[key].save(self.__settings) self.__settings.endGroup() self.__settings.endGroup() self.__settings.endGroup() def __load(self): """ Private slot to load the IRC data. """ if self.__loaded: return # load IRC data self.__settings.beginGroup("IRC") # identities self.__settings.beginGroup("Identities") for key in self.__settings.childGroups(): self.__identities[key] = IrcIdentity(key) self.__settings.beginGroup(key) self.__identities[key].load(self.__settings) self.__settings.endGroup() self.__settings.endGroup() # networks self.__settings.beginGroup("Networks") for key in self.__settings.childGroups(): self.__networks[key] = IrcNetwork(key) self.__settings.beginGroup(key) self.__networks[key].load(self.__settings) self.__settings.endGroup() self.__settings.endGroup() self.__settings.endGroup() if not self.__identities or \ not self.__networks: # data structures got corrupted; load defaults self.__loadDefaults() if IrcIdentity.DefaultIdentityName not in self.__identities: self.__loadDefaults(identityOnly=True) self.__loaded = True def __loadDefaults(self, identityOnly=False): """ Private method to load default values. @param identityOnly flag indicating to just load the default identity (boolean) """ if not identityOnly: self.__networks = {} self.__identities = {} # identity identity = IrcIdentity.createDefaultIdentity() self.__identities[identity.getName()] = identity if not identityOnly: network = IrcNetwork.createDefaultNetwork() self.__networks[network.getName()] = network network = IrcNetwork.createDefaultNetwork(True) self.__networks[network.getName()] = network self.dataChanged.emit() ################################################################## ## Identity related methods below ################################################################## def getIdentity(self, name, create=False): """ Public method to get an identity object. @param name name of the identity to get (string) @param create flag indicating to create a new object, if none exists (boolean) @return reference to the identity (IrcIdentity) """ if not name: return None if not self.__loaded: self.__load() if name in self.__identities: return self.__identities[name] elif create: id = IrcIdentity(name) self.__identities[name] = id self.dataChanged.emit() return id else: return None def getIdentities(self): """ Public method to get a copy of all identities. @return dictionary of all identities (dict of IrcIdentity) """ return copy.deepcopy(self.__identities) def setIdentities(self, identities): """ Public method to set the identities. @param identities dictionary of all identities (dict of IrcIdentity) """ self.__identities = copy.deepcopy(identities) self.identityChanged() # Check all networks, if the identity they use is still available. # If it isn't, change them to use the default identity. for network in self.__networks.values(): if network.getIdentityName() not in self.__identities: network.setIdentityName(IrcIdentity.DefaultIdentityName) def getIdentityNames(self): """ Public method to get the names of all identities. @return names of all identities (list of string) """ return list(self.__identities.keys()) def addIdentity(self, identity): """ Public method to add a new identity. @param identity reference to the identity to add (IrcIdentity) """ name = identity.getName() self.__identities[name] = identity self.identityChanged() def deleteIdentity(self, name): """ Public method to delete the given identity. @param name name of the identity to delete (string) """ if name in self.__identities and \ name != IrcIdentity.DefaultIdentityName: del self.__identities[name] self.identityChanged() def renameIdentity(self, oldName, newName): """ Public method to rename an identity. @param oldName old name of the identity (string) @param newName new name of the identity (string) """ if oldName in self.__identities: self.__identities[newName] = self.__identities[oldName] del self.__identities[oldName] for network in self.__networks: if network.getIdentityName() == oldName: network.setIdentityName(newName) self.identityChanged() def identityChanged(self): """ Public method to indicate a change of an identity object. """ self.dataChanged.emit() self.identitiesChanged.emit() ################################################################## ## Network related methods below ################################################################## def getNetwork(self, name): """ Public method to get a network object. @param name name of the network (string) @return reference to the network object (IrcNetwork) """ if not self.__loaded: self.__load() if name in self.__networks: return self.__networks[name] else: return None def setNetwork(self, network, networkName=""): """ Public method to set a network. @param network network object to set (IrcNetwork) @param networkName name the network was known for (string) """ name = network.getName() if networkName and name != networkName: # the network name has changed self.deleteNetwork(networkName) self.addNetwork(network) elif name in self.__networks: self.__networks[name] = network self.networkChanged() def addNetwork(self, network): """ Public method to add a network. @param network network object to add (IrcNetwork) """ name = network.getName() if name not in self.__networks: self.__networks[name] = network self.networkChanged() def deleteNetwork(self, name): """ Public method to delete the given network. @param name name of the network to delete (string) """ if name in self.__networks: del self.__networks[name] self.networkChanged() def networkChanged(self): """ Public method to indicate a change of a network object. """ self.dataChanged.emit() self.networksChanged.emit() def getNetworkNames(self): """ Public method to get a list of all known network names. @return list of network names (list of string) """ if not self.__loaded: self.__load() return list(sorted(self.__networks.keys()))
class SpeedDial(QObject): """ Class implementing the speed dial. @signal pagesChanged() emitted after the list of pages changed @signal speedDialSaved() emitted after the speed dial data was saved """ pagesChanged = pyqtSignal() speedDialSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__webFrames = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.pagesChanged.connect(self.__pagesChanged) self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred) def addWebFrame(self, frame): """ Public method to add a web frame. @param frame reference to the frame to be added (QWebFrame) """ if frame not in self.__webFrames: self.__webFrames.append(frame) def addPage(self, url, title): """ Public method to add a page for the given data. @param url URL of the page (QUrl) @param title title of the page (string) """ if url.isEmpty(): return from .Page import Page page = Page(url.toString(), title) self.__webPages.append(page) self.pagesChanged.emit() def removePage(self, url): """ Public method to remove a page. @param url URL of the page (QUrl) """ page = self.pageForUrl(url) if not page.url: return self.removeImageForUrl(page.url) self.__webPages.remove(page) self.pagesChanged.emit() def __imageFileName(self, url): """ Private method to generate the image file name for a URL. @param url URL to generate the file name from (string) @return name of the image file (string) """ return os.path.join( self.__thumbnailsDirectory, str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")), QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png") def initialScript(self): """ Public method to get the 'initial' JavaScript script. @return initial JavaScript script (string) """ if self.__regenerateScript: self.__regenerateScript = False self.__initialScript = "" for page in self.__webPages: if page.broken: imgSource = "qrc:icons/brokenPage.png" else: imgSource = self.__imageFileName(page.url) if not os.path.exists(imgSource): self.loadThumbnail(page.url) imgSource = "qrc:icons/loading.gif" if not page.url: imgSource = "" else: imgSource = QUrl.fromLocalFile(imgSource).toString() self.__initialScript += \ "addBox('{0}', '{1}', '{2}');\n".format( page.url, Utilities.html_uencode(page.title), imgSource) return self.__initialScript def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "speedDial.xml") def __initialize(self): """ Private method to initialize the speed dial. """ self.__thumbnailsDirectory = os.path.join( Utilities.getConfigDir(), "browser", "thumbnails") # Create directory if it does not exist yet if not os.path.exists(self.__thumbnailsDirectory): os.makedirs(self.__thumbnailsDirectory) self.__load() def reload(self): """ Public method to reload the speed dial data. """ self.__load() def __load(self): """ Private method to load the speed dial configuration. """ allPages, pagesPerRow, speedDialSize = [], 0, 0 speedDialFile = self.getFileName() if os.path.exists(speedDialFile): from .SpeedDialReader import SpeedDialReader reader = SpeedDialReader() allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile) self.__pagesPerRow = pagesPerRow if pagesPerRow else 4 self.__speedDialSize = speedDialSize if speedDialSize else 231 if allPages: self.__webPages = allPages self.pagesChanged.emit() else: allPages = \ 'url:"http://eric-ide.python-projects.org/"|'\ 'title:"Eric Web Site";'\ 'url:"http://www.riverbankcomputing.com/"|'\ 'title:"PyQt Web Site";'\ 'url:"http://www.qt.io/"|title:"Qt Web Site";'\ 'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\ 'url:"http://www.python.org"|title:"Python Language Website";'\ 'url:"http://www.google.com"|title:"Google";' self.changed(allPages) def save(self): """ Public method to save the speed dial configuration. """ from .SpeedDialWriter import SpeedDialWriter speedDialFile = self.getFileName() writer = SpeedDialWriter() if not writer.write(speedDialFile, self.__webPages, self.__pagesPerRow, self.__speedDialSize): E5MessageBox.critical( None, self.tr("Saving Speed Dial data"), self.tr( """<p>Speed Dial data could not be saved to""" """ <b>{0}</b></p>""").format(speedDialFile)) else: self.speedDialSaved.emit() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def pageForUrl(self, url): """ Public method to get the page for the given URL. @param url URL to be searched for (QUrl) @return page for the URL (Page) """ urlString = url.toString() for page in self.__webPages: if page.url == urlString: return page from .Page import Page return Page() def urlForShortcut(self, key): """ Public method to get the URL for the given shortcut key. @param key shortcut key (integer) @return URL for the key (QUrl) """ if key < 0 or len(self.__webPages) <= key: return QUrl() return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8")) @pyqtSlot(str) def changed(self, allPages): """ Public slot to react on changed pages. @param allPages string giving all pages (string) """ if not allPages: return entries = allPages.split('";') self.__webPages = [] from .Page import Page for entry in entries: if not entry: continue tmp = entry.split('"|') if len(tmp) == 2: broken = False elif len(tmp) == 3: broken = "brokenPage" in tmp[2][5:] else: continue page = Page(tmp[0][5:], tmp[1][7:], broken) self.__webPages.append(page) self.pagesChanged.emit() @pyqtSlot(str) @pyqtSlot(str, bool) def loadThumbnail(self, url, loadTitle=False): """ Public slot to load a thumbnail of the given URL. @param url URL of the thumbnail (string) @param loadTitle flag indicating to get the title for the thumbnail from the site (boolean) """ if not url: return from .PageThumbnailer import PageThumbnailer thumbnailer = PageThumbnailer(self) thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8"))) thumbnailer.setLoadTitle(loadTitle) thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated) self.__thumbnailers.append(thumbnailer) thumbnailer.start() @pyqtSlot(str) def removeImageForUrl(self, url): """ Public slot to remove the image for a URL. @param url URL to remove the image for (string) """ fileName = self.__imageFileName(url) if os.path.exists(fileName): os.remove(fileName) @pyqtSlot(str, result=str) def urlFromUserInput(self, url): """ Public slot to get the URL from user input. @param url URL entered by the user (string) @return sanitized URL (string) """ return QUrl.fromUserInput(url).toString() @pyqtSlot(str, result=str) def unescapeTitle(self, title): """ Public slot to unescape the titel string. @param title escaped title (string) @return un-escaped title (string) """ return Utilities.html_udecode(title) @pyqtSlot(int) def setPagesInRow(self, count): """ Public slot to set the number of pages per row. @param count number of pages per row (integer) """ self.__pagesPerRow = count self.__saveTimer.changeOccurred() def pagesInRow(self): """ Public method to get the number of dials per row. @return number of dials per row (integer) """ return self.__pagesPerRow @pyqtSlot(int) def setSdSize(self, size): """ Public slot to set the size of the speed dial. @param size size of the speed dial (integer) """ self.__speedDialSize = size self.__saveTimer.changeOccurred() def sdSize(self): """ Public method to get the speed dial size. @return speed dial size (integer) """ return self.__speedDialSize def __thumbnailCreated(self, image): """ Private slot to handle the creation of a thumbnail image. @param image thumbnail image (QPixmap) """ from .PageThumbnailer import PageThumbnailer thumbnailer = self.sender() if not isinstance(thumbnailer, PageThumbnailer) or \ thumbnailer not in self.__thumbnailers: return loadTitle = thumbnailer.loadTitle() title = thumbnailer.title() url = thumbnailer.url().toString() fileName = self.__imageFileName(url) if image.isNull(): fileName = "qrc:icons/brokenPage.png" title = self.tr("Unable to load") loadTitle = True page = self.pageForUrl(thumbnailer.url()) page.broken = True else: if not image.save(fileName): qWarning( "SpeedDial.__thumbnailCreated: Cannot save thumbnail" " to {0}".format(fileName)) fileName = QUrl.fromLocalFile(fileName).toString() self.__regenerateScript = True for frame in self.__cleanFrames(): frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format( url, fileName)) if loadTitle: frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format( url, Utilities.html_uencode(title))) thumbnailer.deleteLater() self.__thumbnailers.remove(thumbnailer) def __cleanFrames(self): """ Private method to clean all frames. @return list of speed dial frames (list of QWebFrame) """ frames = [] for frame in self.__webFrames[:]: if frame.url().toString() == "eric:speeddial": frames.append(frame) else: self.__webFrames.remove(frame) return frames def __pagesChanged(self): """ Private slot to react on a change of the pages configuration. """ # update all speed dial pages self.__regenerateScript = True for frame in self.__cleanFrames(): frame.page().triggerAction(QWebPage.Reload)
class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadCanceled(): menu.addAction( UI.PixmapCache.getIcon("restart.png"), self.tr("Retry"), self.__contextMenuRetry) else: if itm.downloadedSuccessfully(): menu.addAction( UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction( UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction( self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction( self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction( self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if selectedRowsCount > 1 or \ (selectedRowsCount == 1 and not self.__downloads[ self.downloadsView.selectionModel().selectedRows()[0].row()] .downloading()): menu.addSeparator() menu.addAction( self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.close() def activeDownloads(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloads() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr("""There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloads()), icon=E5MessageBox.Warning) if not res: self.show() return False return True def download(self, requestOrUrl, requestFileName=False, mainWindow=None): """ Public method to download a file. @param requestOrUrl reference to a request object (QNetworkRequest) or a URL to be downloaded (QUrl) @keyparam requestFileName flag indicating to ask for the download file name (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ request = QNetworkRequest(requestOrUrl) if request.url().isEmpty(): return self.handleUnsupportedContent( self.__manager.get(request), requestFileName=requestFileName, download=True, mainWindow=mainWindow) def handleUnsupportedContent(self, reply, requestFileName=False, webPage=None, download=False, mainWindow=None): """ Public method to handle unsupported content by downloading the referenced resource. @param reply reference to the reply object (QNetworkReply) @keyparam requestFileName indicating to ask for a filename (boolean) @keyparam webPage reference to the web page (HelpWebPage) @keyparam download flag indicating a download request (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ if reply is None or reply.url().isEmpty(): return size = reply.header(QNetworkRequest.ContentLengthHeader) if size == 0: return from .DownloadItem import DownloadItem itm = DownloadItem( reply=reply, requestFilename=requestFileName, webPage=webPage, download=download, parent=self, mainWindow=mainWindow) self.__addItem(itm) if itm.canceledFileSelect(): return if not self.isVisible(): self.show() self.activateWindow() self.raise_() def __addItem(self, itm): """ Private method to add a download to the list of downloads. @param itm reference to the download item (DownloadItem) """ itm.statusChanged.connect(self.__updateRow) itm.downloadFinished.connect(self.__finished) row = len(self.__downloads) self.__model.beginInsertRows(QModelIndex(), row, row) self.__downloads.append(itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.__updateActiveItemCount() def __updateRow(self, itm=None): """ Private slot to update a download item. @param itm reference to the download item (DownloadItem) """ if itm is None: itm = self.sender() if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) oldHeight = self.downloadsView.rowHeight(row) self.downloadsView.setRowHeight( row, max(oldHeight, itm.minimumSizeHint().height() * 1.5)) remove = False globalSettings = QWebSettings.globalSettings() if not itm.downloading() and \ globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): remove = True if itm.downloadedSuccessfully() and \ self.removePolicy() == DownloadManager.RemoveSuccessFullDownload: remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getHelp("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setHelp("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setHelp("DownloadManagerSize", self.size()) Preferences.setHelp("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setHelp("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getHelp("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getHelp("DownloadManagerPosition") self.move(pos) downloads = Preferences.getHelp("DownloadManagerDownloads") for download in downloads: if not download[0].isEmpty() and \ download[1] != "": from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) self.__loaded = True self.__updateActiveItemCount() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot cleanup the downloads. """ if len(self.__downloads) == 0: return self.__model.removeRows(0, len(self.__downloads)) if len(self.__downloads) == 0 and \ self.__iconProvider is not None: self.__iconProvider = None self.changeOccurred() self.__updateActiveItemCount() def __updateItemCount(self): """ Private method to update the count label. """ count = len(self.__downloads) self.countLabel.setText(self.tr("%n Download(s)", "", count)) def __updateActiveItemCount(self): """ Private method to update the window title. """ count = self.activeDownloads() if count > 0: self.setWindowTitle( self.tr("Downloading %n file(s)", "", count)) else: self.setWindowTitle(self.tr("Downloads")) def __finished(self): """ Private slot to handle a finished download. """ self.__updateActiveItemCount() if self.isVisible(): QApplication.alert(self) def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def count(self): """ Public method to get the number of downloads. @return number of downloads (integer) """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() self.__updateItemCount() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuRetry(self): """ Private method to retry of the download. """ itm = self.__currentItem() if itm is not None: itm.retry() def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toString() QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()
class HistoryManager(QWebHistoryInterface): """ Class implementing the history manager. @signal historyCleared() emitted after the history has been cleared @signal historyReset() emitted after the history has been reset @signal entryAdded(HistoryEntry) emitted after a history entry has been added @signal entryRemoved(HistoryEntry) emitted after a history entry has been removed @signal entryUpdated(int) emitted after a history entry has been updated @signal historySaved() emitted after the history was saved """ historyCleared = pyqtSignal() historyReset = pyqtSignal() entryAdded = pyqtSignal(HistoryEntry) entryRemoved = pyqtSignal(HistoryEntry) entryUpdated = pyqtSignal(int) historySaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(HistoryManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = "" self.__expiredTimer = QTimer(self) self.__expiredTimer.setSingleShot(True) self.__expiredTimer.timeout.connect(self.__checkForExpired) self.__frequencyTimer = QTimer(self) self.__frequencyTimer.setSingleShot(True) self.__frequencyTimer.timeout.connect(self.__refreshFrequencies) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.__load() from .HistoryModel import HistoryModel from .HistoryFilterModel import HistoryFilterModel from .HistoryTreeModel import HistoryTreeModel self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self) super(HistoryManager, self).setDefaultInterface(self) self.__startFrequencyTimer() def close(self): """ Public method to close the history manager. """ # remove history items on application exit if self.__daysToExpire == -2: self.clear() self.__saveTimer.saveIfNeccessary() def history(self): """ Public method to return the history. @return reference to the list of history entries (list of HistoryEntry) """ return self.__history[:] def setHistory(self, history, loadedAndSorted=False): """ Public method to set a new history. @param history reference to the list of history entries to be set (list of HistoryEntry) @param loadedAndSorted flag indicating that the list is sorted (boolean) """ self.__history = history[:] if not loadedAndSorted: self.__history.sort() self.__checkForExpired() if loadedAndSorted: try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" else: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.historyReset.emit() def historyContains(self, url): """ Public method to check the history for an entry. @param url URL to check for (string) @return flag indicating success (boolean) """ return self.__historyFilterModel.historyContains(url) def _addHistoryEntry(self, itm): """ Protected method to add a history item. @param itm reference to the history item to add (HistoryEntry) """ globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return self.__history.insert(0, itm) self.entryAdded.emit(itm) if len(self.__history) == 1: self.__checkForExpired() def _removeHistoryEntry(self, itm): """ Protected method to remove a history item. @param itm reference to the history item to remove (HistoryEntry) """ self.__lastSavedUrl = "" self.__history.remove(itm) self.entryRemoved.emit(itm) def addHistoryEntry(self, url): """ Public method to add a history entry. @param url URL to be added (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: if cleanurl.password(): # don't save the password in the history cleanurl.setPassword("") if cleanurl.host(): cleanurl.setHost(cleanurl.host().lower()) itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime()) self._addHistoryEntry(itm) def updateHistoryEntry(self, url, title): """ Public method to update a history entry. @param url URL of the entry to update (string) @param title title of the entry to update (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: for index in range(len(self.__history)): if url == self.__history[index].url: self.__history[index].title = title self.__saveTimer.changeOccurred() if not self.__lastSavedUrl: self.__lastSavedUrl = self.__history[index].url self.entryUpdated.emit(index) break def removeHistoryEntry(self, url, title=""): """ Public method to remove a history entry. @param url URL of the entry to remove (QUrl) @param title title of the entry to remove (string) """ for index in range(len(self.__history)): if url == QUrl(self.__history[index].url) and (not title or title == self.__history[index].title): self._removeHistoryEntry(self.__history[index]) break def historyModel(self): """ Public method to get a reference to the history model. @return reference to the history model (HistoryModel) """ return self.__historyModel def historyFilterModel(self): """ Public method to get a reference to the history filter model. @return reference to the history filter model (HistoryFilterModel) """ return self.__historyFilterModel def historyTreeModel(self): """ Public method to get a reference to the history tree model. @return reference to the history tree model (HistoryTreeModel) """ return self.__historyTreeModel def __checkForExpired(self): """ Private slot to check entries for expiration. """ if self.__daysToExpire < 0 or len(self.__history) == 0: return now = QDateTime.currentDateTime() nextTimeout = 0 while self.__history: checkForExpired = QDateTime(self.__history[-1].dateTime) checkForExpired.setDate(checkForExpired.date().addDays(self.__daysToExpire)) if now.daysTo(checkForExpired) > 7: nextTimeout = 7 * 86400 else: nextTimeout = now.secsTo(checkForExpired) if nextTimeout > 0: break itm = self.__history.pop(-1) self.__lastSavedUrl = "" self.entryRemoved.emit(itm) self.__saveTimer.saveIfNeccessary() if nextTimeout > 0: self.__expiredTimer.start(nextTimeout * 1000) def daysToExpire(self): """ Public method to get the days for entry expiration. @return days for entry expiration (integer) """ return self.__daysToExpire def setDaysToExpire(self, limit): """ Public method to set the days for entry expiration. @param limit days for entry expiration (integer) """ if self.__daysToExpire == limit: return self.__daysToExpire = limit self.__checkForExpired() self.__saveTimer.changeOccurred() def preferencesChanged(self): """ Public method to indicate a change of preferences. """ self.setDaysToExpire(Preferences.getHelp("HistoryLimit")) @pyqtSlot() def clear(self, period=0): """ Public slot to clear the complete history. @param period history period in milliseconds to be cleared (integer) """ if period == 0: self.__history = [] self.historyReset.emit() else: breakMS = QDateTime.currentMSecsSinceEpoch() - period while self.__history and (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() > breakMS): itm = self.__history.pop(0) self.entryRemoved.emit(itm) self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.historyCleared.emit() def getFileName(self): """ Public method to get the file name of the history file. @return name of the history file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "history") def reload(self): """ Public method to reload the history. """ self.__load() def __load(self): """ Private method to load the saved history entries from disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): return if not historyFile.open(QIODevice.ReadOnly): E5MessageBox.warning( None, self.tr("Loading History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( historyFile.fileName, historyFile.errorString() ), ) return history = [] # double check, that the history file is sorted as it is read needToSort = False lastInsertedItem = HistoryEntry() data = QByteArray(historyFile.readAll()) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) while not stream.atEnd(): ver = stream.readUInt32() if ver != HISTORY_VERSION: continue itm = HistoryEntry() itm.url = Utilities.readStringFromStream(stream) stream >> itm.dateTime itm.title = Utilities.readStringFromStream(stream) if not itm.dateTime.isValid(): continue if itm == lastInsertedItem: if not lastInsertedItem.title and len(history) > 0: history[0].title = itm.title continue if not needToSort and history and lastInsertedItem < itm: needToSort = True history.insert(0, itm) lastInsertedItem = itm historyFile.close() if needToSort: history.sort() self.setHistory(history, True) # if the history had to be sorted, rewrite the history sorted if needToSort: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() def save(self): """ Public slot to save the history entries to disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): self.__lastSavedUrl = "" saveAll = self.__lastSavedUrl == "" first = len(self.__history) - 1 if not saveAll: # find the first one to save for index in range(len(self.__history)): if self.__history[index].url == self.__lastSavedUrl: first = index - 1 break if first == len(self.__history) - 1: saveAll = True if saveAll: # use a temporary file when saving everything f = QTemporaryFile() f.setAutoRemove(False) opened = f.open() else: f = historyFile opened = f.open(QIODevice.Append) if not opened: E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( f.fileName(), f.errorString() ), ) return for index in range(first, -1, -1): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) itm = self.__history[index] stream.writeUInt32(HISTORY_VERSION) stream.writeString(itm.url.encode("utf-8")) stream << itm.dateTime stream.writeString(itm.title.encode("utf-8")) f.write(data) f.close() if saveAll: if historyFile.exists() and not historyFile.remove(): E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Error removing old history file <b>{0}</b>.""" """<br/>Reason: {1}</p>""").format( historyFile.fileName(), historyFile.errorString() ), ) if not f.copy(historyFile.fileName()): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error moving new history file over old one """ """(<b>{0}</b>).<br/>Reason: {1}</p>""" ).format(historyFile.fileName(), f.errorString()), ) self.historySaved.emit() try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" def __refreshFrequencies(self): """ Private slot to recalculate the refresh frequencies. """ self.__historyFilterModel.recalculateFrequencies() self.__startFrequencyTimer() def __startFrequencyTimer(self): """ Private method to start the timer to recalculate the frequencies. """ tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0)) self.__frequencyTimer.start(QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged() emitted to indicate a change of the current search engine """ changed = pyqtSignal() currentEngineChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (string) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string) """ if name not in self.__engines: return self.__current = name self.currentEngineChanged.emit() self.changed.emit() def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if not self.__current or self.__current not in self.__engines: return None return self.__engines[self.__current] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string) @return reference to the engine (OpenSearchEngine) """ if name not in self.__engines: return None return self.__engines[name] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string) @return flag indicating an existing engine (boolean) """ return name in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (list of strings) """ return sorted(self.__engines.keys()) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ from .OpenSearchEngine import OpenSearchEngine if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return from Helpviewer.HelpWindow import HelpWindow reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url)) reply.finished.connect(self.__engineFromUrlAvailable) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if engine.name() in self.__engines: return False engine.setParent(self) self.__engines[engine.name()] = engine self.changed.emit() return True def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string) """ if len(self.__engines) <= 1: return if name not in self.__engines: return engine = self.__engines[name] for keyword in [ k for k in self.__keywords if self.__keywords[k] == engine ]: del self.__keywords[keyword] del self.__engines[name] file_ = QDir(self.enginesDirectory()).filePath( self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(list(self.__engines.keys())[0]) self.changed.emit() def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string) @return valid engine file name (string) """ fileName = "" # strip special characters for c in engineName: if c.isspace(): fileName += '_' continue if c.isalnum(): fileName += c fileName += ".xml" return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string) """ dir = QDir() if not dir.mkpath(dirName): return dir.setPath(dirName) from .OpenSearchWriter import OpenSearchWriter writer = OpenSearchWriter() for engine in list(self.__engines.values()): name = self.generateEngineFileName(engine.name()) fileName = dir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setHelp("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setHelp("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False dir = QDir(dirName) for name in dir.entryList(["*.xml"]): fileName = dir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getHelp("WebSearchEngine") keywords = Preferences.getHelp("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[keyword] = self.engine(engineName) if self.__current not in self.__engines and \ len(self.__engines) > 0: self.__current = list(self.__engines.keys())[0] self.__loading = False self.currentEngineChanged.emit() def restoreDefaults(self): """ Public method to restore the default search engines. """ from .OpenSearchReader import OpenSearchReader from .DefaultSearchEngines import DefaultSearchEngines_rc # __IGNORE_WARNING__ defaultEngineFiles = [ "YouTube.xml", "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml", "Facebook.xml", "Google_Im_Feeling_Lucky.xml", "Google.xml", "LEO_DeuEng.xml", "LinuxMagazin.xml", "Reddit.xml", "Wikia_en.xml", "Wikia.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml" ] # Keep this list in sync with the contents of the resource file. reader = OpenSearchReader() for engineFileName in defaultEngineFiles: engineFile = QFile(":/" + engineFileName) if not engineFile.open(QIODevice.ReadOnly): continue engine = reader.read(engineFile) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "searchengines") def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = E5MessageBox.yesNo( None, "", self.tr("""<p>Do you want to add the following engine to your""" """ list of search engines?<br/><br/>Name: {0}<br/>""" """Searches on: {1}</p>""").format(engine.name(), host)) return res def __engineFromUrlAvailable(self): """ Private slot to add a search engine from the net. """ reply = self.sender() if reply is None: return if reply.error() != QNetworkReply.NoError: reply.close() if reply in self.__replies: self.__replies.remove(reply) return from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(reply) reply.close() if reply in self.__replies: self.__replies.remove(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string) @return search URL (QUrl) """ try: keyword, term = keywordSearch.split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string) @return reference to the search engine object (OpenSearchEngine) """ if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.changed.emit() def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (list of strings) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if not keyword: continue self.__keywords[keyword] = engine self.changed.emit() def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.changed.emit()
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded emitted after a bookmark node has been added @signal entryRemoved emitted after a bookmark node has been removed @signal entryChanged emitted after a bookmark node has been changed """ def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() self.connect(self, SIGNAL("entryAdded"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryRemoved"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryChanged"), self.__saveTimer.changeOccurred) def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return self.__loaded = True bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") if not QFile.exists(bookmarkFile): ba = QByteArray(DefaultBookmarks) bookmarkFile = QBuffer(ba) bookmarkFile.open(QIODevice.ReadOnly) reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: KQMessageBox.warning(None, self.trUtf8("Loading Bookmarks"), self.trUtf8("""Error when loading bookmarks on line %1, column %2:\n""" """%3""")\ .arg(reader.lineNumber())\ .arg(reader.columnNumber())\ .arg(reader.errorString())) others = [] for index in range(len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if (node.title == self.trUtf8("Toolbar Bookmarks") or \ node.title == BOOKMARKBAR) and \ self.__toolbar is None: node.title = self.trUtf8(BOOKMARKBAR) self.__toolbar = node if (node.title == self.trUtf8("Menu") or \ node.title == BOOKMARKMENU) and \ self.__menu is None: node.title = self.trUtf8(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.trUtf8(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.trUtf8(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) self.__convertFromOldBookmarks() def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return writer = XbelWriter() bookmarkFile = os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): KQMessageBox.warning(None, self.trUtf8("Saving Bookmarks"), self.trUtf8("""Error saving bookmarks to <b>%1</b>.""").arg(bookmarkFile)) # restore localized titles self.__menu.title = self.trUtf8(BOOKMARKMENU) self.__toolbar.title = self.trUtf8(BOOKMARKBAR) def addBookmark(self, parent, node, row = -1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (QString) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (QString) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ supportedFormats = QStringList() \ << self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)") \ << self.trUtf8("HTML Netscape bookmarks").append(" (*.html *.htm)") fileName = KQFileDialog.getOpenFileName(\ None, self.trUtf8("Import Bookmarks"), QString(), supportedFormats.join(";;"), None) if fileName.isEmpty(): return reader = XbelReader() importRootNode = None if fileName.endsWith(".html"): inFile = QFile(fileName) inFile.open(QIODevice.ReadOnly) if inFile.openMode == QIODevice.NotOpen: KQMessageBox.warning(None, self.trUtf8("Import Bookmarks"), self.trUtf8("""Error opening bookmarks file <b>%1</b>.""")\ .arg(fileName)) return webpage = QWebPage() webpage.mainFrame().setHtml(QString(inFile.readAll())) result = webpage.mainFrame().evaluateJavaScript(extract_js).toByteArray() buffer_ = QBuffer(result) buffer_.open(QIODevice.ReadOnly) importRootNode = reader.read(buffer_) else: importRootNode = reader.read(fileName) if reader.error() != QXmlStreamReader.NoError: KQMessageBox.warning(None, self.trUtf8("Import Bookmarks"), self.trUtf8("""Error when importing bookmarks on line %1, column %2:\n""" """%3""")\ .arg(reader.lineNumber())\ .arg(reader.columnNumber())\ .arg(reader.errorString())) return importRootNode.setType(BookmarkNode.Folder) importRootNode.title = self.trUtf8("Imported %1")\ .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)) self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName = KQFileDialog.getSaveFileName(\ None, self.trUtf8("Export Bookmarks"), "eric4_bookmarks.xbel", self.trUtf8("XBEL bookmarks").append(" (*.xbel *.xml)")) if fileName.isEmpty(): return writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): KQMessageBox.critical(None, self.trUtf8("Exporting Bookmarks"), self.trUtf8("""Error exporting bookmarks to <b>%1</b>.""")\ .arg(fileName)) def __convertFromOldBookmarks(self): """ Private method to convert the old bookmarks into the new ones. """ bmNames = Preferences.Prefs.settings.value('Bookmarks/Names') bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files') if bmNames.isValid() and bmFiles.isValid(): bmNames = bmNames.toStringList() bmFiles = bmFiles.toStringList() if len(bmNames) == len(bmFiles): convertedRootNode = BookmarkNode(BookmarkNode.Folder) convertedRootNode.title = self.trUtf8("Converted %1")\ .arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)) for i in range(len(bmNames)): node = BookmarkNode(BookmarkNode.Bookmark, convertedRootNode) node.title = bmNames[i] url = QUrl(bmFiles[i]) if url.scheme().isEmpty(): url.setScheme("file") node.url = url.toString() self.addBookmark(self.menu(), convertedRootNode) Preferences.Prefs.settings.remove('Bookmarks')
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged() emitted to indicate a change of the current search engine """ changed = pyqtSignal() currentEngineChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (string) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string) """ if name not in self.__engines: return self.__current = name self.currentEngineChanged.emit() self.changed.emit() def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if not self.__current or self.__current not in self.__engines: return None return self.__engines[self.__current] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string) @return reference to the engine (OpenSearchEngine) """ if name not in self.__engines: return None return self.__engines[name] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string) @return flag indicating an existing engine (boolean) """ return name in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (list of strings) """ return sorted(self.__engines.keys()) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ from .OpenSearchEngine import OpenSearchEngine if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return False from WebBrowser.WebBrowserWindow import WebBrowserWindow reply = WebBrowserWindow.networkManager().get(QNetworkRequest(url)) reply.finished.connect(lambda: self.__engineFromUrlAvailable(reply)) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if engine.name() in self.__engines: return False engine.setParent(self) self.__engines[engine.name()] = engine self.changed.emit() return True def addEngineFromForm(self, res, view): """ Public method to add a new search engine from a form. @param res result of the JavaScript run on by WebBrowserView.__addSearchEngine() @type dict or None @param view reference to the web browser view @type WebBrowserView """ if not res: return method = res["method"] actionUrl = QUrl(res["action"]) inputName = res["inputName"] if method != "get": E5MessageBox.warning( self, self.tr("Method not supported"), self.tr("""{0} method is not supported.""").format( method.upper())) return if actionUrl.isRelative(): actionUrl = view.url().resolved(actionUrl) searchUrlQuery = QUrlQuery(actionUrl) searchUrlQuery.addQueryItem(inputName, "{searchTerms}") inputFields = res["inputs"] for inputField in inputFields: name = inputField[0] value = inputField[1] if not name or name == inputName or not value: continue searchUrlQuery.addQueryItem(name, value) engineName, ok = QInputDialog.getText( view, self.tr("Engine name"), self.tr("Enter a name for the engine"), QLineEdit.Normal) if not ok: return actionUrl.setQuery(searchUrlQuery) from .OpenSearchEngine import OpenSearchEngine engine = OpenSearchEngine() engine.setName(engineName) engine.setDescription(engineName) engine.setSearchUrlTemplate( actionUrl.toDisplayString(QUrl.FullyDecoded)) engine.setImage(view.icon().pixmap(16, 16).toImage()) self.__addEngineByEngine(engine) def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string) """ if len(self.__engines) <= 1: return if name not in self.__engines: return engine = self.__engines[name] for keyword in [ k for k in self.__keywords if self.__keywords[k] == engine ]: del self.__keywords[keyword] del self.__engines[name] file_ = QDir(self.enginesDirectory()).filePath( self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(list(self.__engines.keys())[0]) self.changed.emit() def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string) @return valid engine file name (string) """ fileName = "" # strip special characters for c in engineName: if c.isspace(): fileName += '_' continue if c.isalnum(): fileName += c fileName += ".xml" return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string) """ qdir = QDir() if not qdir.mkpath(dirName): return qdir.setPath(dirName) from .OpenSearchWriter import OpenSearchWriter writer = OpenSearchWriter() for engine in list(self.__engines.values()): name = self.generateEngineFileName(engine.name()) fileName = qdir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setWebBrowser("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setWebBrowser("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False qdir = QDir(dirName) for name in qdir.entryList(["*.xml"]): fileName = qdir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getWebBrowser("WebSearchEngine") keywords = Preferences.getWebBrowser("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[keyword] = self.engine(engineName) if (self.__current not in self.__engines and len(self.__engines) > 0): self.__current = list(self.__engines.keys())[0] self.__loading = False self.currentEngineChanged.emit() def restoreDefaults(self): """ Public method to restore the default search engines. """ from .OpenSearchReader import OpenSearchReader from .DefaultSearchEngines import DefaultSearchEngines_rc # __IGNORE_WARNING__ defaultEngineFiles = [ "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml", "DuckDuckGo.xml", "Facebook.xml", "Google.xml", "Google_Im_Feeling_Lucky.xml", "LEO_DeuEng.xml", "LinuxMagazin.xml", "MetaGer_MetaGer2.xml", "PyPI.xml", "Qwant.xml", "Reddit.xml", "StartPage.xml", "StartPage_en.xml", "Wikia.xml", "Wikia_en.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml", "YouTube.xml", "searxme.xml", ] # Keep this list in sync with the contents of the resource file. reader = OpenSearchReader() for engineFileName in defaultEngineFiles: engineFile = QFile(":/" + engineFileName) if not engineFile.open(QIODevice.ReadOnly): continue engine = reader.read(engineFile) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "searchengines") def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = E5MessageBox.yesNo( None, "", self.tr("""<p>Do you want to add the following engine to your""" """ list of search engines?<br/><br/>Name: {0}<br/>""" """Searches on: {1}</p>""").format(engine.name(), host)) return res def __engineFromUrlAvailable(self, reply): """ Private slot to add a search engine from the net. @param reply reference to the network reply @type QNetworkReply """ reply.close() if reply in self.__replies: self.__replies.remove(reply) if reply.error() == QNetworkReply.NoError: from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return else: # some error happened from WebBrowser.WebBrowserWindow import WebBrowserWindow WebBrowserWindow.getWindow().statusBar().showMessage( reply.errorString(), 10000) def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string) @return search URL (QUrl) """ try: keyword, term = keywordSearch.split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string) @return reference to the search engine object (OpenSearchEngine) """ if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.changed.emit() def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (list of strings) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if not keyword: continue self.__keywords[keyword] = engine self.changed.emit() def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.changed.emit()
class AdBlockManager(QObject): """ Class implementing the AdBlock manager. @signal rulesChanged() emitted after some rule has changed @signal requiredSubscriptionLoaded(subscription) emitted to indicate loading of a required subscription is finished (AdBlockSubscription) @signal enabledChanged(enabled) emitted to indicate a change of the enabled state """ rulesChanged = pyqtSignal() requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription) enabledChanged = pyqtSignal(bool) def __init__(self, parent=None): """ Constructor @param parent reference to the parent object @type QObject """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__limitedEasyList = Preferences.getWebBrowser( "AdBlockUseLimitedEasyList") self.__defaultSubscriptionUrlString = ( "abp:subscribe?location=" "https://easylist-downloads.adblockplus.org/easylist.txt&" "title=EasyList") self.__additionalDefaultSubscriptionUrlStrings = ( "abp:subscribe?location=https://raw.githubusercontent.com/" "hoshsadiq/adblock-nocoin-list/master/nocoin.txt&" "title=NoCoin", ) self.__customSubscriptionUrlString = (bytes( self.__customSubscriptionUrl().toEncoded()).decode()) self.__mutex = QMutex() self.__matcher = AdBlockMatcher(self) self.rulesChanged.connect(self.__saveTimer.changeOccurred) self.rulesChanged.connect(self.__rulesChanged) self.__interceptor = AdBlockUrlInterceptor(self) from WebBrowser.WebBrowserWindow import WebBrowserWindow WebBrowserWindow.networkManager().installUrlInterceptor( self.__interceptor) def __rulesChanged(self): """ Private slot handling a change of the AdBlock rules. """ from WebBrowser.WebBrowserWindow import WebBrowserWindow WebBrowserWindow.mainWindow().reloadUserStyleSheet() self.__updateMatcher() def close(self): """ Public method to close the open search engines manager. """ self.__adBlockDialog and self.__adBlockDialog.close() (self.__adBlockExceptionsDialog and self.__adBlockExceptionsDialog.close()) self.__saveTimer.saveIfNeccessary() def isEnabled(self): """ Public method to check, if blocking ads is enabled. @return flag indicating the enabled state @rtype bool """ if not self.__loaded: self.load() return self.__enabled def setEnabled(self, enabled): """ Public slot to set the enabled state. @param enabled flag indicating the enabled state @type bool """ if self.isEnabled() == enabled: return from WebBrowser.WebBrowserWindow import WebBrowserWindow self.__enabled = enabled for mainWindow in WebBrowserWindow.mainWindows(): mainWindow.adBlockIcon().setEnabled(enabled) if enabled: self.__loadSubscriptions() self.rulesChanged.emit() self.enabledChanged.emit(enabled) def block(self, info): """ Public method to check, if a request should be blocked. @param info request info object @type QWebEngineUrlRequestInfo @return flag indicating to block the request @rtype bool """ locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ if not self.isEnabled(): return False urlString = bytes(info.requestUrl().toEncoded()).decode().lower() urlDomain = info.requestUrl().host().lower() urlScheme = info.requestUrl().scheme().lower() if (not self.canRunOnScheme(urlScheme) or not self.__canBeBlocked(info.firstPartyUrl())): return False res = False blockedRule = self.__matcher.match(info, urlDomain, urlString) if blockedRule: res = True if (info.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeMainFrame): url = QUrl("eric:adblock") query = QUrlQuery() query.addQueryItem("rule", blockedRule.filter()) query.addQueryItem("subscription", blockedRule.subscription().title()) url.setQuery(query) info.redirect(url) else: info.block(True) return res def canRunOnScheme(self, scheme): """ Public method to check, if AdBlock can be performed on the scheme. @param scheme scheme to check @type str @return flag indicating, that AdBlock can be performed @rtype bool """ return scheme not in ["data", "eric", "qthelp", "qrc", "file", "abp"] def page(self): """ Public method to get a reference to the page block object. @return reference to the page block object @rtype AdBlockPage """ if self.__adBlockPage is None: from .AdBlockPage import AdBlockPage self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. @return URL for custom subscriptions @rtype QUrl """ dataDir = os.path.join(Utilities.getConfigDir(), "web_browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. @return URL for custom subscriptions @rtype QUrl """ location = self.__customSubscriptionLocation() encodedUrl = bytes(location.toEncoded()).decode() url = QUrl("abp:subscribe?location={0}&title={1}".format( encodedUrl, self.tr("Custom Rules"))) return url def customRules(self): """ Public method to get a subscription for custom rules. @return subscription object for custom rules @rtype AdBlockSubscription """ location = self.__customSubscriptionLocation() for subscription in self.__subscriptions: if subscription.location() == location: return subscription url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, True, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription def subscriptions(self): """ Public method to get all subscriptions. @return list of subscriptions @rtype list of AdBlockSubscription """ if not self.__loaded: self.load() return self.__subscriptions[:] def subscription(self, location): """ Public method to get a subscription based on its location. @param location location of the subscription to search for @type str @return subscription or None @rtype AdBlockSubscription """ if location != "": for subscription in self.__subscriptions: if subscription.location().toString() == location: return subscription return None def updateAllSubscriptions(self): """ Public method to update all subscriptions. """ for subscription in self.__subscriptions: subscription.updateNow() def removeSubscription(self, subscription, emitSignal=True): """ Public method to remove an AdBlock subscription. @param subscription AdBlock subscription to be removed @type AdBlockSubscription @param emitSignal flag indicating to send a signal @type bool """ if subscription is None: return if subscription.url().toString().startswith( (self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString)): return try: self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() QFile.remove(rulesFileName) requiresSubscriptions = self.getRequiresSubscriptions(subscription) for requiresSubscription in requiresSubscriptions: self.removeSubscription(requiresSubscription, False) if emitSignal: self.rulesChanged.emit() except ValueError: pass def addSubscriptionFromUrl(self, url): """ Public method to ad an AdBlock subscription given the abp URL. @param url URL to subscribe an AdBlock subscription @type QUrl @return flag indicating success @rtype bool """ if url.path() != "subscribe": return False title = QUrl.fromPercentEncoding( QByteArray(QUrlQuery(url).queryItemValue("title").encode())) if not title: return False res = E5MessageBox.yesNo( None, self.tr("Subscribe?"), self.tr("""<p>Subscribe to this AdBlock subscription?</p>""" """<p>{0}</p>""").format(title)) if res: from .AdBlockSubscription import AdBlockSubscription from WebBrowser.WebBrowserWindow import WebBrowserWindow dlg = WebBrowserWindow.adBlockManager().showDialog() subscription = AdBlockSubscription( url, False, WebBrowserWindow.adBlockManager()) WebBrowserWindow.adBlockManager().addSubscription(subscription) dlg.addSubscription(subscription, False) dlg.setFocus() dlg.raise_() return res def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. @param subscription AdBlock subscription to be added @type AdBlockSubscription """ if subscription is None: return self.__subscriptions.insert(-1, subscription) subscription.rulesChanged.connect(self.rulesChanged) subscription.changed.connect(self.rulesChanged) subscription.enabledChanged.connect(self.rulesChanged) self.rulesChanged.emit() def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return Preferences.setWebBrowser("AdBlockEnabled", self.__enabled) if self.__subscriptionsLoaded: subscriptions = [] requiresSubscriptions = [] # intermediate store for subscription requiring others for subscription in self.__subscriptions: if subscription is None: continue urlString = bytes(subscription.url().toEncoded()).decode() if "requiresLocation" in urlString: requiresSubscriptions.append(urlString) else: subscriptions.append(urlString) subscription.saveRules() for subscription in requiresSubscriptions: subscriptions.insert(-1, subscription) # custom should be last Preferences.setWebBrowser("AdBlockSubscriptions", subscriptions) def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return self.__loaded = True self.__enabled = Preferences.getWebBrowser("AdBlockEnabled") if self.__enabled: self.__loadSubscriptions() def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return subscriptions = Preferences.getWebBrowser("AdBlockSubscriptions") if subscriptions: for subscription in subscriptions: if subscription.startswith(self.__customSubscriptionUrlString): break else: subscriptions.append(self.__customSubscriptionUrlString) else: subscriptions = ([self.__defaultSubscriptionUrlString] + self.__additionalDefaultSubscriptionUrlStrings + [self.__customSubscriptionUrlString]) for subscription in subscriptions: url = QUrl.fromEncoded(subscription.encode("utf-8")) adBlockSubscription = AdBlockSubscription( url, subscription.startswith(self.__customSubscriptionUrlString), self, subscription.startswith(self.__defaultSubscriptionUrlString)) adBlockSubscription.rulesChanged.connect(self.rulesChanged) adBlockSubscription.changed.connect(self.rulesChanged) adBlockSubscription.enabledChanged.connect(self.rulesChanged) adBlockSubscription.rulesEnabledChanged.connect( self.__updateMatcher) adBlockSubscription.rulesEnabledChanged.connect( self.__saveTimer.changeOccurred) self.__subscriptions.append(adBlockSubscription) self.__subscriptionsLoaded = True self.__updateMatcher() def loadRequiredSubscription(self, location, title): """ Public method to load a subscription required by another one. @param location location of the required subscription @type str @param title title of the required subscription @type str """ # Step 1: check, if the subscription is in the list of subscriptions urlString = "abp:subscribe?location={0}&title={1}".format( location, title) for subscription in self.__subscriptions: if subscription.url().toString().startswith(urlString): # We found it! return # Step 2: if it is not, get it url = QUrl.fromEncoded(urlString.encode("utf-8")) adBlockSubscription = AdBlockSubscription(url, False, self) self.addSubscription(adBlockSubscription) self.requiredSubscriptionLoaded.emit(adBlockSubscription) def getRequiresSubscriptions(self, subscription): """ Public method to get a list of subscriptions, that require the given one. @param subscription subscription to check for @type AdBlockSubscription @return list of subscription requiring the given one @rtype list of AdBlockSubscription """ subscriptions = [] location = subscription.location().toString() for subscription in self.__subscriptions: if subscription.requiresLocation() == location: subscriptions.append(subscription) return subscriptions def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. @return reference to the dialog @rtype AdBlockDialog """ if self.__adBlockDialog is None: from .AdBlockDialog import AdBlockDialog self.__adBlockDialog = AdBlockDialog(self) self.__adBlockDialog.show() return self.__adBlockDialog def elementHidingRules(self, url): """ Public method to get the element hiding rules. @param url URL to get hiding rules for @type QUrl @return element hiding rules @rtype str """ if (not self.isEnabled() or not self.canRunOnScheme(url.scheme()) or not self.__canBeBlocked(url)): return "" return self.__matcher.elementHidingRules() def elementHidingRulesForDomain(self, url): """ Public method to get the element hiding rules for a domain. @param url URL to get hiding rules for @type QUrl @return element hiding rules @rtype str """ if (not self.isEnabled() or not self.canRunOnScheme(url.scheme()) or not self.__canBeBlocked(url)): return "" return self.__matcher.elementHidingRulesForDomain(url.host()) def exceptions(self): """ Public method to get a list of excepted hosts. @return list of excepted hosts @rtype list of str """ return self.__exceptedHosts def setExceptions(self, hosts): """ Public method to set the list of excepted hosts. @param hosts list of excepted hosts @type list of str """ self.__exceptedHosts = [host.lower() for host in hosts] Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) def addException(self, host): """ Public method to add an exception. @param host to be excepted @type str """ host = host.lower() if host and host not in self.__exceptedHosts: self.__exceptedHosts.append(host) Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) def removeException(self, host): """ Public method to remove an exception. @param host to be removed from the list of exceptions @type str """ host = host.lower() if host in self.__exceptedHosts: self.__exceptedHosts.remove(host) Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts) def isHostExcepted(self, host): """ Public slot to check, if a host is excepted. @param host host to check @type str @return flag indicating an exception @rtype bool """ host = host.lower() return host in self.__exceptedHosts def showExceptionsDialog(self): """ Public method to show the AdBlock Exceptions dialog. @return reference to the exceptions dialog @rtype AdBlockExceptionsDialog """ if self.__adBlockExceptionsDialog is None: from .AdBlockExceptionsDialog import AdBlockExceptionsDialog self.__adBlockExceptionsDialog = AdBlockExceptionsDialog() self.__adBlockExceptionsDialog.load(self.__exceptedHosts) self.__adBlockExceptionsDialog.show() return self.__adBlockExceptionsDialog def useLimitedEasyList(self): """ Public method to test, if limited EasyList rules shall be used. @return flag indicating limited EasyList rules @rtype bool """ return self.__limitedEasyList def setUseLimitedEasyList(self, limited): """ Public method to set the limited EasyList flag. @param limited flag indicating to use limited EasyList @type bool """ self.__limitedEasyList = limited for subscription in self.__subscriptions: if subscription.url().toString().startswith( self.__defaultSubscriptionUrlString): subscription.updateNow() Preferences.setWebBrowser("AdBlockUseLimitedEasyList", limited) def getDefaultSubscriptionUrl(self): """ Public method to get the default subscription URL. @return default subscription URL @rtype str """ return self.__defaultSubscriptionUrlString def __updateMatcher(self): """ Private slot to update the adblock matcher. """ from WebBrowser.WebBrowserWindow import WebBrowserWindow WebBrowserWindow.networkManager().removeUrlInterceptor( self.__interceptor) if self.__enabled: self.__matcher.update() else: self.__matcher.clear() WebBrowserWindow.networkManager().installUrlInterceptor( self.__interceptor) def __canBeBlocked(self, url): """ Private method to check, if the given URL could be blocked (i.e. is not whitelisted). @param url URL to be checked @type QUrl @return flag indicating that the given URL can be blocked @rtype bool """ return not self.__matcher.adBlockDisabledForUrl(url)
class HistoryManager(QWebHistoryInterface): """ Class implementing the history manager. @signal historyCleared() emitted after the history has been cleared @signal historyReset() emitted after the history has been reset @signal entryAdded emitted after a history entry has been added @signal entryRemoved emitted after a history entry has been removed @signal entryUpdated(int) emitted after a history entry has been updated """ def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QWebHistoryInterface.__init__(self, parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = QString() self.__expiredTimer = QTimer() self.__expiredTimer.setSingleShot(True) self.connect(self.__expiredTimer, SIGNAL("timeout()"), self.__checkForExpired) self.__frequencyTimer = QTimer() self.__frequencyTimer.setSingleShot(True) self.connect(self.__frequencyTimer, SIGNAL("timeout()"), self.__refreshFrequencies) self.connect(self, SIGNAL("entryAdded"), self.__saveTimer.changeOccurred) self.connect(self, SIGNAL("entryRemoved"), self.__saveTimer.changeOccurred) self.__load() self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = HistoryTreeModel(self.__historyFilterModel, self) QWebHistoryInterface.setDefaultInterface(self) self.__startFrequencyTimer() def close(self): """ Public method to close the history manager. """ # remove history items on application exit if self.__daysToExpire == -2: self.clear() self.__saveTimer.saveIfNeccessary() def history(self): """ Public method to return the history. @return reference to the list of history entries (list of HistoryEntry) """ return self.__history[:] def setHistory(self, history, loadedAndSorted = False): """ Public method to set a new history. @param history reference to the list of history entries to be set (list of HistoryEntry) @param loadedAndSorted flag indicating that the list is sorted (boolean) """ self.__history = history[:] if not loadedAndSorted: self.__history.sort() self.__checkForExpired() if loadedAndSorted: try: self.__lastSavedUrl = QString(self.__history[0].url) except IndexError: self.__lastSavedUrl = QString() else: self.__lastSavedUrl.clear() self.__saveTimer.changeOccurred() self.emit(SIGNAL("historyReset()")) def historyContains(self, url): """ Public method to check the history for an entry. @param url URL to check for (QString) @return flag indicating success (boolean) """ return self.__historyFilterModel.historyContains(url) def _addHistoryEntry(self, itm): """ Protected method to add a history item. @param itm reference to the history item to add (HistoryEntry) """ globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return self.__history.insert(0, itm) self.emit(SIGNAL("entryAdded"), itm) if len(self.__history) == 1: self.__checkForExpired() def _removeHistoryEntry(self, itm): """ Protected method to remove a history item. @param itm reference to the history item to remove (HistoryEntry) """ self.__lastSavedUrl.clear() self.__history.remove(itm) self.emit(SIGNAL("entryRemoved"), itm) def addHistoryEntry(self, url): """ Public method to add a history entry. @param url URL to be added (QString) """ cleanurl = QUrl(url) cleanurl.setPassword("") cleanurl.setHost(cleanurl.host().toLower()) itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime()) self._addHistoryEntry(itm) def updateHistoryEntry(self, url, title): """ Public method to update a history entry. @param url URL of the entry to update (QString) @param title title of the entry to update (QString) """ for index in range(len(self.__history)): if url == self.__history[index].url: self.__history[index].title = title self.__saveTimer.changeOccurred() if self.__lastSavedUrl.isEmpty(): self.__lastSavedUrl = QString(self.__history[index].url) self.emit(SIGNAL("entryUpdated(int)"), index) break def removeHistoryEntry(self, url, title = QString()): """ Public method to remove a history entry. @param url URL of the entry to remove (QUrl) @param title title of the entry to remove (QString) """ for index in range(len(self.__history)): if url == QUrl(self.__history[index].url) and \ (title.isEmpty() or title == self.__history[index].title): self._removeHistoryEntry(self.__history[index]) break def historyModel(self): """ Public method to get a reference to the history model. @return reference to the history model (HistoryModel) """ return self.__historyModel def historyFilterModel(self): """ Public method to get a reference to the history filter model. @return reference to the history filter model (HistoryFilterModel) """ return self.__historyFilterModel def historyTreeModel(self): """ Public method to get a reference to the history tree model. @return reference to the history tree model (HistoryTreeModel) """ return self.__historyTreeModel def __checkForExpired(self): """ Private slot to check entries for expiration. """ if self.__daysToExpire < 0 or len(self.__history) == 0: return now = QDateTime.currentDateTime() nextTimeout = 0 while self.__history: checkForExpired = QDateTime(self.__history[-1].dateTime) checkForExpired.setDate(checkForExpired.date().addDays(self.__daysToExpire)) if now.daysTo(checkForExpired) > 7: nextTimeout = 7 * 86400 else: nextTimeout = now.secsTo(checkForExpired) if nextTimeout > 0: break itm = self.__history.pop(-1) self.__lastSavedUrl.clear() self.emit(SIGNAL("entryRemoved"), itm) self.__saveTimer.saveIfNeccessary() if nextTimeout > 0: self.__expiredTimer.start(nextTimeout * 1000) def daysToExpire(self): """ Public method to get the days for entry expiration. @return days for entry expiration (integer) """ return self.__daysToExpire def setDaysToExpire(self, limit): """ Public method to set the days for entry expiration. @param limit days for entry expiration (integer) """ if self.__daysToExpire == limit: return self.__daysToExpire = limit self.__checkForExpired() self.__saveTimer.changeOccurred() def preferencesChanged(self): """ Public method to indicate a change of preferences. """ self.setDaysToExpire(Preferences.getHelp("HistoryLimit")) def clear(self): """ Public slot to clear the complete history. """ self.__history = [] self.__lastSavedUrl.clear() self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.emit(SIGNAL("historyReset()")) self.emit(SIGNAL("historyCleared()")) def __load(self): """ Private method to load the saved history entries from disk. """ historyFile = QFile(Utilities.getConfigDir() + "/browser/history") if not historyFile.exists(): return if not historyFile.open(QIODevice.ReadOnly): KQMessageBox.warning(None, self.trUtf8("Loading History"), self.trUtf8("""<p>Unable to open history file <b>%1</b>.""" """<br/>Reason: %2</p>""")\ .arg(historyFile.fileName).arg(historyFile.errorString())) return history = [] historyStream = QDataStream(historyFile) # double check, that the history file is sorted as it is read needToSort = False lastInsertedItem = HistoryEntry() data = QByteArray() stream = QDataStream() buffer = QBuffer() stream.setDevice(buffer) while not historyFile.atEnd(): historyStream >> data buffer.close() buffer.setBuffer(data) buffer.open(QIODevice.ReadOnly) ver = stream.readUInt32() if ver != HISTORY_VERSION: continue itm = HistoryEntry() stream >> itm.url stream >> itm.dateTime stream >> itm.title if not itm.dateTime.isValid(): continue if itm == lastInsertedItem: if lastInsertedItem.title.isEmpty() and len(history) > 0: history[0].title = itm.title continue if not needToSort and history and lastInsertedItem < itm: needToSort = True history.insert(0, itm) lastInsertedItem = itm historyFile.close() if needToSort: history.sort() self.setHistory(history, True) # if the history had to be sorted, rewrite the history sorted if needToSort: self.__lastSavedUrl.clear() self.__saveTimer.changeOccurred() def save(self): """ Public slot to save the history entries to disk. """ historyFile = QFile(Utilities.getConfigDir() + "/browser/history") if not historyFile.exists(): self.__lastSavedUrl.clear() saveAll = self.__lastSavedUrl.isEmpty() first = len(self.__history) - 1 if not saveAll: # find the first one to save for index in range(len(self.__history)): if self.__history[index].url == self.__lastSavedUrl: first = index - 1 break if first == len(self.__history) - 1: saveAll = True # use a temporary file when saving everything tempFile = QTemporaryFile() tempFile.setAutoRemove(False) if saveAll: opened = tempFile.open() else: opened = historyFile.open(QIODevice.Append) if not opened: if saveAll: f = tempFile else: f = historyFile KQMessageBox.warning(None, self.trUtf8("Saving History"), self.trUtf8("""<p>Unable to open history file <b>%1</b>.""" """<br/>Reason: %2</p>""")\ .arg(f.fileName()).arg(f.errorString())) return if saveAll: historyStream = QDataStream(tempFile) else: historyStream = QDataStream(historyFile) for index in range(first, -1, -1): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) itm = self.__history[index] stream.writeUInt32(HISTORY_VERSION) stream << itm.url stream << itm.dateTime stream << itm.title historyStream << data if saveAll: tempFile.close() if historyFile.exists() and not historyFile.remove(): KQMessageBox.warning(None, self.trUtf8("Saving History"), self.trUtf8("""<p>Error removing old history file <b>%1</b>.""" """<br/>Reason: %2</p>""")\ .arg(historyFile.fileName()).arg(historyFile.errorString())) if not tempFile.copy(historyFile.fileName()): KQMessageBox.warning(None, self.trUtf8("Saving History"), self.trUtf8("""<p>Error moving new history file over old one """ """(<b>%1</b>).<br/>Reason: %2</p>""")\ .arg(historyFile.fileName()).arg(tempFile.errorString())) else: historyFile.close() try: self.__lastSavedUrl = QString(self.__history[0].url) except IndexError: self.__lastSavedUrl = QString() def __refreshFrequencies(self): """ Private slot to recalculate the refresh frequencies. """ self.__historyFilterModel.recalculateFrequencies() self.__startFrequencyTimer() def __startFrequencyTimer(self): """ Private method to start the timer to recalculate the frequencies. """ tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0)) self.__frequencyTimer.start(QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
class ZoomManager(QObject): """ Class implementing a manager for site specific zoom level settings. """ changed = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(ZoomManager, self).__init__(parent) self.__zoomDB = {} self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.__loaded = False def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return dbString = Preferences.getHelp("ZoomValuesDB") if dbString: try: db = json.loads(dbString) self.__zoomDB = db except ValueError: # ignore silently pass self.__loaded = True def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return dbString = json.dumps(self.__zoomDB) Preferences.setHelp("ZoomValuesDB", dbString) def __keyFromUrl(self, url): """ Private method to generate a DB key for an URL. @param url URL to generate a key for @type QUrl @return key for the given URL @rtype str """ if url.isEmpty(): key = "" else: scheme = url.scheme() host = url.host() if host: key = host elif scheme == "file": path = url.path() key = path.rsplit("/", 1)[0] else: key = "" return key def setZoomValue(self, url, zoomValue): """ Public method to record the zoom value for the given URL. Note: Only zoom values not equal 100% are recorded. @param url URL of the page to remember the zoom value for @type QUrl @param zoomValue zoom value for the URL @type int """ self.load() key = self.__keyFromUrl(url) if not key: return if ((zoomValue == 100 and key not in self.__zoomDB) or (key in self.__zoomDB and self.__zoomDB[key] == zoomValue)): return if zoomValue == 100: del self.__zoomDB[key] else: self.__zoomDB[key] = zoomValue self.changed.emit() def zoomValue(self, url): """ Public method to get the zoom value for an URL. @param url URL of the page to get the zoom value for @type QUrl @return zoomValue zoom value for the URL @rtype int """ self.load() key = self.__keyFromUrl(url) if not key: zoom = 100 if key in self.__zoomDB: zoom = self.__zoomDB[key] else: # default zoom value (i.e. no zoom) zoom = 100 return zoom def clear(self): """ Public method to clear the saved zoom values. """ self.__zoomDB = {} self.__loaded = True self.changed.emit() def removeZoomValue(self, site): """ Public method to remove a zoom value entry. @param site web site name @type str """ self.load() if site in self.__zoomDB: del self.__zoomDB[site] self.changed.emit() def allSiteNames(self): """ Public method to get a list of all site names. @return sorted list of all site names @rtype list of str """ self.load() return sorted(self.__zoomDB.keys()) def sitesCount(self): """ Public method to get the number of available sites. @return number of sites @rtype int """ self.load() return len(self.__zoomDB) def siteInfo(self, site): """ Public method to get the zoom value for the site. @param site web site name @type str @return zoom value for the site @rtype int """ self.load() if site not in self.__zoomDB: return None return self.__zoomDB[site]
class AdBlockManager(QObject): """ Class implementing the AdBlock manager. @signal rulesChanged() emitted after some rule has changed """ def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("rulesChanged()"), self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def isEnabled(self): """ Public method to check, if blocking ads is enabled. @return flag indicating the enabled state (boolean) """ if not self.__loaded: self.load() return self.__enabled def setEnabled(self, enabled): """ Public slot to set the enabled state. @param enabled flag indicating the enabled state (boolean) """ if self.isEnabled() == enabled: return self.__enabled = enabled if enabled: self.__loadSubscriptions() self.emit(SIGNAL("rulesChanged()")) def network(self): """ Public method to get a reference to the network block object. @return reference to the network block object (AdBlockNetwork) """ if self.__adBlockNetwork is None: self.__adBlockNetwork = AdBlockNetwork(self) return self.__adBlockNetwork def page(self): """ Public method to get a reference to the page block object. @return reference to the page block object (AdBlockPage) """ if self.__adBlockPage is None: self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. @return URL for custom subscriptions (QUrl) """ dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. @return URL for custom subscriptions (QUrl) """ location = self.__customSubscriptionLocation() encodedUrl = QString.fromUtf8(location.toEncoded()) url = QUrl("abp:subscribe?location=%s&title=%s" % \ (encodedUrl, self.trUtf8("Custom Rules"))) return url def customRules(self): """ Public method to get a subscription for custom rules. @return subscription object for custom rules (AdBlockSubscription) """ location = self.__customSubscriptionLocation() for subscription in self.__subscriptions: if subscription.location() == location: return subscription url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription def subscriptions(self): """ Public method to get all subscriptions. @return list of subscriptions (list of AdBlockSubscription) """ if not self.__loaded: self.load() return self.__subscriptions[:] def removeSubscription(self, subscription): """ Public method to remove an AdBlock subscription. @param subscription AdBlock subscription to be removed (AdBlockSubscription) """ if subscription is None: return try: self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() QFile.remove(rulesFileName) self.emit(SIGNAL("rulesChanged()")) except ValueError: pass def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. @param subscription AdBlock subscription to be added (AdBlockSubscription) """ if subscription is None: return self.__subscriptions.append(subscription) self.connect(subscription, SIGNAL("rulesChanged()"), self, SIGNAL("rulesChanged()")) self.connect(subscription, SIGNAL("changed()"), self, SIGNAL("rulesChanged()")) self.emit(SIGNAL("rulesChanged()")) def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return Preferences.setHelp("AdBlockEnabled", int(self.__enabled)) if self.__subscriptionsLoaded: subscriptions = QStringList() for subscription in self.__subscriptions: if subscription is None: continue subscriptions.append(QString.fromUtf8(subscription.url().toEncoded())) subscription.saveRules() Preferences.setHelp("AdBlockSubscriptions", subscriptions) def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return self.__loaded = True self.__enabled = bool(Preferences.getHelp("AdBlockEnabled")) if self.__enabled: self.__loadSubscriptions() def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return defaultSubscriptionUrl = \ "abp:subscribe?location=http://adblockplus.mozdev.org/easylist/easylist.txt&title=EasyList" defaultSubscriptions = QStringList() defaultSubscriptions.append( QString.fromUtf8(self.__customSubscriptionUrl().toEncoded())) defaultSubscriptions.append(defaultSubscriptionUrl) subscriptions = Preferences.getHelp("AdBlockSubscriptions") if len(subscriptions) == 0: subscriptions = defaultSubscriptions for subscription in subscriptions: url = QUrl.fromEncoded(subscription.toUtf8()) adBlockSubscription = AdBlockSubscription(url, self, subscription == defaultSubscriptionUrl) self.connect(adBlockSubscription, SIGNAL("rulesChanged()"), self, SIGNAL("rulesChanged()")) self.connect(adBlockSubscription, SIGNAL("changed()"), self, SIGNAL("rulesChanged()")) self.__subscriptions.append(adBlockSubscription) self.__subscriptionsLoaded = True def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. """ if self.__adBlockDialog is None: self.__adBlockDialog = AdBlockDialog() self.__adBlockDialog.show() return self.__adBlockDialog
class CookieJar(QNetworkCookieJar): """ Class implementing a QNetworkCookieJar subclass with various accept policies. @signal cookiesChanged() emitted after the cookies have been changed """ cookiesChanged = pyqtSignal() AcceptAlways = 0 AcceptNever = 1 AcceptOnlyFromSitesNavigatedTo = 2 AcceptMax = 2 KeepUntilExpire = 0 KeepUntilExit = 1 KeepMax = 1 Allow = 0 Block = 1 AllowForSession = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(CookieJar, self).__init__(parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.__save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "web_browser", "cookies.ini") self.__store = WebBrowserWindow.webProfile().cookieStore() try: self.__store.setCookieFilter(self.__cookieFilter) except AttributeError: # pre Qt 5.11 pass self.__store.cookieAdded.connect(self.__cookieAdded) self.__store.cookieRemoved.connect(self.__cookieRemoved) self.__load() self.__store.loadAllCookies() def close(self): """ Public slot to close the cookie jar. """ if not self.__loaded: self.__load() if self.__keepCookies == self.KeepUntilExit: self.clear() self.__saveTimer.saveIfNeccessary() def clear(self): """ Public method to clear all cookies. """ if not self.__loaded: self.__load() self.setAllCookies([]) self.__store.deleteAllCookies() self.cookiesChanged.emit() def removeCookies(self, cookies): """ Public method to remove a list of cookies. @param cookies list of cookies to be removed @type list of QNetworkCookie """ wasBlocked = self.blockSignals(True) for cookie in cookies: self.__store.deleteCookie(cookie) self.blockSignals(wasBlocked) self.cookiesChanged.emit() def removeCookie(self, cookie): """ Public method to remove a cookie. @param cookie cookie to be removed @type QNetworkCookie """ self.__store.deleteCookie(cookie) self.cookiesChanged.emit() def __load(self): """ Private method to load the cookies settings. """ if self.__loaded: return cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) # load exceptions self.__exceptionsBlock = Preferences.toList( cookieSettings.value("Exceptions/block")) self.__exceptionsAllow = Preferences.toList( cookieSettings.value("Exceptions/allow")) self.__exceptionsAllowForSession = Preferences.toList( cookieSettings.value("Exceptions/allowForSession")) self.__exceptionsBlock.sort() self.__exceptionsAllow.sort() self.__exceptionsAllowForSession.sort() self.__acceptCookies = Preferences.getWebBrowser("AcceptCookies") self.__keepCookies = Preferences.getWebBrowser("KeepCookiesUntil") if self.__keepCookies == self.KeepUntilExit: self.clear() self.__filterTrackingCookies = Preferences.toBool( Preferences.getWebBrowser("FilterTrackingCookies")) self.__loaded = True self.cookiesChanged.emit() def __save(self): """ Private method to save the cookies settings. """ if not self.__loaded: return cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) cookieSettings.setValue("Exceptions/block", self.__exceptionsBlock) cookieSettings.setValue("Exceptions/allow", self.__exceptionsAllow) cookieSettings.setValue("Exceptions/allowForSession", self.__exceptionsAllowForSession) Preferences.setWebBrowser("AcceptCookies", self.__acceptCookies) Preferences.setWebBrowser("KeepCookiesUntil", self.__keepCookies) Preferences.setWebBrowser("FilterTrackingCookies", self.__filterTrackingCookies) @pyqtSlot(QNetworkCookie) def __cookieAdded(self, cookie): """ Private slot handling the addition of a cookie. @param cookie cookie which was added @type QNetworkCookie """ if self.__rejectCookie(cookie, cookie.domain()): self.__store.deleteCookie(cookie) return self.insertCookie(cookie) self.cookiesChanged.emit() @pyqtSlot(QNetworkCookie) def __cookieRemoved(self, cookie): """ Private slot handling the removal of a cookie. @param cookie cookie which was removed @type QNetworkCookie """ if self.deleteCookie(cookie): self.cookiesChanged.emit() def __cookieFilter(self, request): """ Private method to filter cookies. Note: This method is used for Qt 5.11+ only. @param request reference to the cookie filter request object @type QWebEngineCookieStore.FilterRequest @return flag indicating cookie access is allowed @rtype bool """ if not self.__loaded: self.__load() if self.__acceptCookies == self.AcceptNever: res = self.__isOnDomainList(self.__exceptionsAllow, request.origin.host()) if not res: return False if self.__acceptCookies == self.AcceptAlways: res = self.__isOnDomainList(self.__exceptionsBlock, request.origin.host()) if res: return False if (self.__acceptCookies == self.AcceptOnlyFromSitesNavigatedTo and request.thirdParty): return False return True def __rejectCookie(self, cookie, cookieDomain): """ Private method to test, if a cookie shall be rejected. @param cookie cookie to be tested @type QNetworkCookie @param cookieDomain domain of the cookie @type str @return flag indicating the cookie shall be rejected @rtype bool """ if not self.__loaded: self.__load() if self.__acceptCookies == self.AcceptNever: res = self.__isOnDomainList(self.__exceptionsAllow, cookieDomain) if not res: return True if self.__acceptCookies == self.AcceptAlways: res = self.__isOnDomainList(self.__exceptionsBlock, cookieDomain) if res: return True if self.__acceptCookies == self.AcceptOnlyFromSitesNavigatedTo: mainWindow = WebBrowserWindow.mainWindow() if mainWindow is not None: browser = mainWindow.getWindow().currentBrowser() if browser is not None: url = browser.url() if url.isValid(): host = url.host() else: host = "" res = self.__matchDomain(cookieDomain, host) if not res: return True if self.__filterTrackingCookies and cookie.name().startsWith(b"__utm"): return True return False def acceptPolicy(self): """ Public method to get the accept policy. @return current accept policy """ if not self.__loaded: self.__load() return self.__acceptCookies def setAcceptPolicy(self, policy): """ Public method to set the accept policy. @param policy accept policy to be set """ if not self.__loaded: self.__load() if policy > self.AcceptMax: return if policy == self.__acceptCookies: return self.__acceptCookies = policy self.__saveTimer.changeOccurred() def keepPolicy(self): """ Public method to get the keep policy. @return keep policy """ if not self.__loaded: self.__load() return self.__keepCookies def setKeepPolicy(self, policy): """ Public method to set the keep policy. @param policy keep policy to be set """ if not self.__loaded: self.__load() if policy > self.KeepMax: return if policy == self.__keepCookies: return self.__keepCookies = policy self.__saveTimer.changeOccurred() def blockedCookies(self): """ Public method to return the list of blocked domains. @return list of blocked domains (list of strings) """ if not self.__loaded: self.__load() return self.__exceptionsBlock def allowedCookies(self): """ Public method to return the list of allowed domains. @return list of allowed domains (list of strings) """ if not self.__loaded: self.__load() return self.__exceptionsAllow def allowForSessionCookies(self): """ Public method to return the list of allowed session cookie domains. @return list of allowed session cookie domains (list of strings) """ if not self.__loaded: self.__load() return self.__exceptionsAllowForSession def setBlockedCookies(self, list_): """ Public method to set the list of blocked domains. @param list_ list of blocked domains (list of strings) """ if not self.__loaded: self.__load() self.__exceptionsBlock = list_[:] self.__exceptionsBlock.sort() self.__saveTimer.changeOccurred() def setAllowedCookies(self, list_): """ Public method to set the list of allowed domains. @param list_ list of allowed domains (list of strings) """ if not self.__loaded: self.__load() self.__exceptionsAllow = list_[:] self.__exceptionsAllow.sort() self.__saveTimer.changeOccurred() def setAllowForSessionCookies(self, list_): """ Public method to set the list of allowed session cookie domains. @param list_ list of allowed session cookie domains (list of strings) """ if not self.__loaded: self.__load() self.__exceptionsAllowForSession = list_[:] self.__exceptionsAllowForSession.sort() self.__saveTimer.changeOccurred() def filterTrackingCookies(self): """ Public method to get the filter tracking cookies flag. @return filter tracking cookies flag (boolean) """ return self.__filterTrackingCookies def setFilterTrackingCookies(self, filterTrackingCookies): """ Public method to set the filter tracking cookies flag. @param filterTrackingCookies filter tracking cookies flag (boolean) """ if filterTrackingCookies == self.__filterTrackingCookies: return self.__filterTrackingCookies = filterTrackingCookies self.__saveTimer.changeOccurred() def __isOnDomainList(self, rules, domain): """ Private method to check, if either the rule matches the domain exactly or the domain ends with ".rule". @param rules list of rules (list of strings) @param domain domain name to check (string) @return flag indicating a match (boolean) """ for rule in rules: if rule.startswith("."): if domain.endswith(rule): return True withoutDot = rule[1:] if domain == withoutDot: return True else: domainEnding = domain[-(len(rule) + 1):] if (domainEnding and domainEnding[0] == "." and domain.endswith(rule)): return True if rule == domain: return True return False def __matchDomain(self, cookieDomain, siteDomain): """ Private method to check, if a URLs host matches a cookie domain according to RFC 6265. @param cookieDomain domain of the cookie @type str @param siteDomain domain or host of an URL @type str @return flag indicating a match @rtype bool """ if not siteDomain: # empty URLs always match return True if cookieDomain.startswith("."): cookieDomain = cookieDomain[1:] if siteDomain.startswith("."): siteDomain = siteDomain[1:] if cookieDomain == siteDomain: return True if not siteDomain.endswith(cookieDomain): return False index = siteDomain.find(cookieDomain) return index > 0 and siteDomain[index - 1] == "." def cookies(self): """ Public method to get the cookies of the cookie jar. @return list of all cookies (list of QNetworkCookie) """ if not self.__loaded: self.__load() return self.allCookies() def cookieDomains(self): """ Public method to get a list of all domains used by the cookies. @return list of domain names @rtype list of str """ domains = [] for cookie in self.cookies(): domain = cookie.domain() if domain not in domains: domains.append(domain) return domains
class ZoomManager(QObject): """ Class implementing a manager for site specific zoom level settings. @signal changed() emitted to indicate a change of the zoom level """ changed = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(ZoomManager, self).__init__(parent) self.__zoomDB = {} self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.__loaded = False def close(self): """ Public method to close the zoom manager. """ self.__saveTimer.saveIfNeccessary() def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return dbString = Preferences.getWebBrowser("ZoomValuesDB") if dbString: try: db = json.loads(dbString) self.__zoomDB = db except ValueError: # ignore silently pass self.__loaded = True def save(self): """ Public method to save the zoom values. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): dbString = json.dumps(self.__zoomDB) Preferences.setWebBrowser("ZoomValuesDB", dbString) def __keyFromUrl(self, url): """ Private method to generate a DB key for an URL. @param url URL to generate a key for @type QUrl @return key for the given URL @rtype str """ if url.isEmpty(): key = "" else: scheme = url.scheme() host = url.host() if host: key = host elif scheme == "file": path = url.path() key = path.rsplit("/", 1)[0] else: key = "" return key def setZoomValue(self, url, zoomValue): """ Public method to record the zoom value for the given URL. Note: Only zoom values not equal 100% are recorded. @param url URL of the page to remember the zoom value for @type QUrl @param zoomValue zoom value for the URL @type int """ self.load() key = self.__keyFromUrl(url) if not key: return if ((zoomValue == 100 and key not in self.__zoomDB) or (key in self.__zoomDB and self.__zoomDB[key] == zoomValue)): return if zoomValue == 100: del self.__zoomDB[key] else: self.__zoomDB[key] = zoomValue self.changed.emit() def zoomValue(self, url): """ Public method to get the zoom value for an URL. @param url URL of the page to get the zoom value for @type QUrl @return zoomValue zoom value for the URL @rtype int """ self.load() key = self.__keyFromUrl(url) if not key: zoom = 100 if key in self.__zoomDB: zoom = self.__zoomDB[key] else: # default zoom value (i.e. no zoom) zoom = 100 return zoom def clear(self): """ Public method to clear the saved zoom values. """ self.__zoomDB = {} self.__loaded = True self.changed.emit() def removeZoomValue(self, site): """ Public method to remove a zoom value entry. @param site web site name @type str """ self.load() if site in self.__zoomDB: del self.__zoomDB[site] self.changed.emit() def allSiteNames(self): """ Public method to get a list of all site names. @return sorted list of all site names @rtype list of str """ self.load() return sorted(self.__zoomDB.keys()) def sitesCount(self): """ Public method to get the number of available sites. @return number of sites @rtype int """ self.load() return len(self.__zoomDB) def siteInfo(self, site): """ Public method to get the zoom value for the site. @param site web site name @type str @return zoom value for the site @rtype int """ self.load() if site not in self.__zoomDB: return None return self.__zoomDB[site]
class CookieJar(QNetworkCookieJar): """ Class implementing a QNetworkCookieJar subclass with various accept policies. @signal cookiesChanged() emitted after the cookies have been changed """ cookiesChanged = pyqtSignal() JAR_VERSION = 23 AcceptAlways = 0 AcceptNever = 1 AcceptOnlyFromSitesNavigatedTo = 2 KeepUntilExpire = 0 KeepUntilExit = 1 KeepUntilTimeLimit = 2 Allow = 0 Block = 1 AllowForSession = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(CookieJar, self).__init__(parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser", "cookies.ini") def saveCookies(self, cookiesList): """ Public method to save the cookies. @param cookiesList list of cookies to be saved @return saved cookies as a byte array (QByteArray) """ data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) stream.writeUInt16(self.JAR_VERSION) stream.writeUInt32(len(cookiesList)) for cookie in cookiesList: stream << cookie.toRawForm() return data def loadCookies(self, cookies): """ Public method to restore the saved cookies. @param cookies byte array containing the saved cookies (QByteArray) @return list of cookies """ if cookies.isEmpty(): return [] cookiesList = [] data = QByteArray(cookies) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) version = stream.readUInt16() if version != self.JAR_VERSION: return [] stream.readUInt32() # number of cookies rawCookie = QByteArray() while not stream.atEnd(): stream >> rawCookie newCookies = QNetworkCookie.parseCookies(rawCookie) for newCookie in newCookies: cookiesList.append(newCookie) return cookiesList def close(self): """ Public slot to close the cookie jar. """ if self.__loaded and self.__keepCookies == self.KeepUntilExit: self.clear() self.__saveTimer.saveIfNeccessary() def clear(self): """ Public method to clear all cookies. """ if not self.__loaded: self.load() self.setAllCookies([]) self.__saveTimer.changeOccurred() self.cookiesChanged.emit() def load(self): """ Public method to load the cookies. """ if self.__loaded: return cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) # load cookies cookies = cookieSettings.value("Cookies") if cookies: cookiesList = self.loadCookies(cookies) else: cookiesList = [] self.setAllCookies(cookiesList) # load exceptions self.__exceptionsBlock = Preferences.toList( cookieSettings.value("Exceptions/block")) self.__exceptionsAllow = Preferences.toList( cookieSettings.value("Exceptions/allow")) self.__exceptionsAllowForSession = Preferences.toList( cookieSettings.value("Exceptions/allowForSession")) self.__exceptionsBlock.sort() self.__exceptionsAllow.sort() self.__exceptionsAllowForSession.sort() self.__acceptCookies = Preferences.getHelp("AcceptCookies") self.__keepCookies = Preferences.getHelp("KeepCookiesUntil") if self.__keepCookies == self.KeepUntilExit: self.setAllCookies([]) self.__filterTrackingCookies = Preferences.toBool( Preferences.getHelp("FilterTrackingCookies")) self.__loaded = True self.cookiesChanged.emit() def save(self): """ Public method to save the cookies. """ if not self.__loaded: return self.__purgeOldCookies() cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) cookiesList = self.allCookies() for index in range(len(cookiesList) - 1, -1, -1): if cookiesList[index].isSessionCookie(): del cookiesList[index] cookies = self.saveCookies(cookiesList) cookieSettings.setValue("Cookies", cookies) cookieSettings.setValue("Exceptions/block", self.__exceptionsBlock) cookieSettings.setValue("Exceptions/allow", self.__exceptionsAllow) cookieSettings.setValue("Exceptions/allowForSession", self.__exceptionsAllowForSession) Preferences.setHelp("AcceptCookies", self.__acceptCookies) Preferences.setHelp("KeepCookiesUntil", self.__keepCookies) Preferences.setHelp("FilterTrackingCookies", self.__filterTrackingCookies) def __purgeOldCookies(self): """ Private method to purge old cookies. """ cookies = self.allCookies() if len(cookies) == 0: return oldCount = len(cookies) now = QDateTime.currentDateTime() for index in range(len(cookies) - 1, -1, -1): if not cookies[index].isSessionCookie() and \ cookies[index].expirationDate() < now: del cookies[index] if oldCount == len(cookies): return self.setAllCookies(cookies) self.cookiesChanged.emit() def cookiesForUrl(self, url): """ Public method to get the cookies for a URL. @param url URL to get cookies for (QUrl) @return list of cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return [] return QNetworkCookieJar.cookiesForUrl(self, url) def setCookiesFromUrl(self, cookieList, url): """ Public method to set cookies for a URL. @param cookieList list of cookies to set (list of QNetworkCookie) @param url url to set cookies for (QUrl) @return flag indicating cookies were set (boolean) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return False host = url.host() eBlock = self.__isOnDomainList(self.__exceptionsBlock, host) eAllow = not eBlock and \ self.__isOnDomainList(self.__exceptionsAllow, host) eAllowSession = not eBlock and \ not eAllow and \ self.__isOnDomainList( self.__exceptionsAllowForSession, host) addedCookies = False acceptInitially = self.__acceptCookies != self.AcceptNever if (acceptInitially and not eBlock) or \ (not acceptInitially and (eAllow or eAllowSession)): # url domain == cookie domain soon = QDateTime.currentDateTime() soon = soon.addDays(90) for cookie in cookieList: lst = [] if not (self.__filterTrackingCookies and cookie.name().startsWith(b"__utm")): if eAllowSession: cookie.setExpirationDate(QDateTime()) if self.__keepCookies == self.KeepUntilTimeLimit and \ not cookie.isSessionCookie and \ cookie.expirationDate() > soon: cookie.setExpirationDate(soon) lst.append(cookie) if QNetworkCookieJar.setCookiesFromUrl(self, lst, url): addedCookies = True elif self.__acceptCookies == self.AcceptAlways: # force it in if wanted cookies = self.allCookies() for ocookie in cookies[:]: # does the cookie exist already? if cookie.name() == ocookie.name() and \ cookie.domain() == ocookie.domain() and \ cookie.path() == ocookie.path(): # found a match cookies.remove(ocookie) cookies.append(cookie) self.setAllCookies(cookies) addedCookies = True if addedCookies: self.__saveTimer.changeOccurred() self.cookiesChanged.emit() return addedCookies def acceptPolicy(self): """ Public method to get the accept policy. @return current accept policy """ if not self.__loaded: self.load() return self.__acceptCookies def setAcceptPolicy(self, policy): """ Public method to set the accept policy. @param policy accept policy to be set """ if not self.__loaded: self.load() if policy > self.AcceptOnlyFromSitesNavigatedTo: return if policy == self.__acceptCookies: return self.__acceptCookies = policy self.__saveTimer.changeOccurred() def keepPolicy(self): """ Public method to get the keep policy. @return keep policy """ if not self.__loaded: self.load() return self.__keepCookies def setKeepPolicy(self, policy): """ Public method to set the keep policy. @param policy keep policy to be set """ if not self.__loaded: self.load() if policy > self.KeepUntilTimeLimit: return if policy == self.__keepCookies: return self.__keepCookies = policy self.__saveTimer.changeOccurred() def blockedCookies(self): """ Public method to return the blocked cookies. @return list of blocked cookies (list of strings) """ if not self.__loaded: self.load() return self.__exceptionsBlock def allowedCookies(self): """ Public method to return the allowed cookies. @return list of allowed cookies (list of strings) """ if not self.__loaded: self.load() return self.__exceptionsAllow def allowForSessionCookies(self): """ Public method to return the allowed session cookies. @return list of allowed session cookies (list of strings) """ if not self.__loaded: self.load() return self.__exceptionsAllowForSession def setBlockedCookies(self, list_): """ Public method to set the list of blocked cookies. @param list_ list of blocked cookies (list of strings) """ if not self.__loaded: self.load() self.__exceptionsBlock = list_[:] self.__exceptionsBlock.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowedCookies(self, list_): """ Public method to set the list of allowed cookies. @param list_ list of allowed cookies (list of strings) """ if not self.__loaded: self.load() self.__exceptionsAllow = list_[:] self.__exceptionsAllow.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowForSessionCookies(self, list_): """ Public method to set the list of allowed session cookies. @param list_ list of allowed session cookies (list of strings) """ if not self.__loaded: self.load() self.__exceptionsAllowForSession = list_[:] self.__exceptionsAllowForSession.sort() self.__applyRules() self.__saveTimer.changeOccurred() def filterTrackingCookies(self): """ Public method to get the filter tracking cookies flag. @return filter tracking cookies flag (boolean) """ return self.__filterTrackingCookies def setFilterTrackingCookies(self, filterTrackingCookies): """ Public method to set the filter tracking cookies flag. @param filterTrackingCookies filter tracking cookies flag (boolean) """ if filterTrackingCookies == self.__filterTrackingCookies: return self.__filterTrackingCookies = filterTrackingCookies self.__saveTimer.changeOccurred() def __isOnDomainList(self, rules, domain): """ Private method to check, if either the rule matches the domain exactly or the domain ends with ".rule". @param rules list of rules (list of strings) @param domain domain name to check (string) @return flag indicating a match (boolean) """ for rule in rules: if rule.startswith("."): if domain.endswith(rule): return True withoutDot = rule[1:] if domain == withoutDot: return True else: domainEnding = domain[-(len(rule) + 1):] if domainEnding and \ domainEnding[0] == "." and \ domain.endswith(rule): return True if rule == domain: return True return False def __applyRules(self): """ Private method to apply the cookie rules. """ cookiesList = self.allCookies() changed = False for index in range(len(cookiesList) - 1, -1, -1): cookie = cookiesList[index] if self.__isOnDomainList(self.__exceptionsBlock, cookie.domain()): del cookiesList[index] changed = True elif self.__isOnDomainList(self.__exceptionsAllowForSession, cookie.domain()): cookie.setExpirationDate(QDateTime()) changed = True if changed: self.setAllCookies(cookiesList) self.__saveTimer.changeOccurred() self.cookiesChanged.emit() def cookies(self): """ Public method to get the cookies of the cookie jar. @return list of all cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() return self.allCookies() def setCookies(self, cookies): """ Public method to set all cookies. @param cookies list of cookies to be set (list of QNetworkCookie) """ if not self.__loaded: self.load() self.setAllCookies(cookies) self.__saveTimer.changeOccurred() self.cookiesChanged.emit()
class HistoryManager(QWebHistoryInterface): """ Class implementing the history manager. @signal historyCleared() emitted after the history has been cleared @signal historyReset() emitted after the history has been reset @signal entryAdded(HistoryEntry) emitted after a history entry has been added @signal entryRemoved(HistoryEntry) emitted after a history entry has been removed @signal entryUpdated(int) emitted after a history entry has been updated @signal historySaved() emitted after the history was saved """ historyCleared = pyqtSignal() historyReset = pyqtSignal() entryAdded = pyqtSignal(HistoryEntry) entryRemoved = pyqtSignal(HistoryEntry) entryUpdated = pyqtSignal(int) historySaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(HistoryManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.__daysToExpire = Preferences.getHelp("HistoryLimit") self.__history = [] self.__lastSavedUrl = "" self.__expiredTimer = QTimer(self) self.__expiredTimer.setSingleShot(True) self.__expiredTimer.timeout.connect(self.__checkForExpired) self.__frequencyTimer = QTimer(self) self.__frequencyTimer.setSingleShot(True) self.__frequencyTimer.timeout.connect(self.__refreshFrequencies) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.__load() from .HistoryModel import HistoryModel from .HistoryFilterModel import HistoryFilterModel from .HistoryTreeModel import HistoryTreeModel self.__historyModel = HistoryModel(self, self) self.__historyFilterModel = \ HistoryFilterModel(self.__historyModel, self) self.__historyTreeModel = \ HistoryTreeModel(self.__historyFilterModel, self) super(HistoryManager, self).setDefaultInterface(self) self.__startFrequencyTimer() def close(self): """ Public method to close the history manager. """ # remove history items on application exit if self.__daysToExpire == -2: self.clear() self.__saveTimer.saveIfNeccessary() def history(self): """ Public method to return the history. @return reference to the list of history entries (list of HistoryEntry) """ return self.__history[:] def setHistory(self, history, loadedAndSorted=False): """ Public method to set a new history. @param history reference to the list of history entries to be set (list of HistoryEntry) @param loadedAndSorted flag indicating that the list is sorted (boolean) """ self.__history = history[:] if not loadedAndSorted: self.__history.sort() self.__checkForExpired() if loadedAndSorted: try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" else: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.historyReset.emit() def historyContains(self, url): """ Public method to check the history for an entry. @param url URL to check for (string) @return flag indicating success (boolean) """ return self.__historyFilterModel.historyContains(url) def _addHistoryEntry(self, itm): """ Protected method to add a history item. @param itm reference to the history item to add (HistoryEntry) """ globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return self.__history.insert(0, itm) self.entryAdded.emit(itm) if len(self.__history) == 1: self.__checkForExpired() def _removeHistoryEntry(self, itm): """ Protected method to remove a history item. @param itm reference to the history item to remove (HistoryEntry) """ self.__lastSavedUrl = "" self.__history.remove(itm) self.entryRemoved.emit(itm) def addHistoryEntry(self, url): """ Public method to add a history entry. @param url URL to be added (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: if cleanurl.password(): # don't save the password in the history cleanurl.setPassword("") if cleanurl.host(): cleanurl.setHost(cleanurl.host().lower()) itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime()) self._addHistoryEntry(itm) def updateHistoryEntry(self, url, title): """ Public method to update a history entry. @param url URL of the entry to update (string) @param title title of the entry to update (string) """ cleanurl = QUrl(url) if cleanurl.scheme() not in ["eric", "about"]: for index in range(len(self.__history)): if url == self.__history[index].url: self.__history[index].title = title self.__saveTimer.changeOccurred() if not self.__lastSavedUrl: self.__lastSavedUrl = self.__history[index].url self.entryUpdated.emit(index) break def removeHistoryEntry(self, url, title=""): """ Public method to remove a history entry. @param url URL of the entry to remove (QUrl) @param title title of the entry to remove (string) """ for index in range(len(self.__history)): if url == QUrl(self.__history[index].url) and \ (not title or title == self.__history[index].title): self._removeHistoryEntry(self.__history[index]) break def historyModel(self): """ Public method to get a reference to the history model. @return reference to the history model (HistoryModel) """ return self.__historyModel def historyFilterModel(self): """ Public method to get a reference to the history filter model. @return reference to the history filter model (HistoryFilterModel) """ return self.__historyFilterModel def historyTreeModel(self): """ Public method to get a reference to the history tree model. @return reference to the history tree model (HistoryTreeModel) """ return self.__historyTreeModel def __checkForExpired(self): """ Private slot to check entries for expiration. """ if self.__daysToExpire < 0 or len(self.__history) == 0: return now = QDateTime.currentDateTime() nextTimeout = 0 while self.__history: checkForExpired = QDateTime(self.__history[-1].dateTime) checkForExpired.setDate(checkForExpired.date().addDays( self.__daysToExpire)) if now.daysTo(checkForExpired) > 7: nextTimeout = 7 * 86400 else: nextTimeout = now.secsTo(checkForExpired) if nextTimeout > 0: break itm = self.__history.pop(-1) self.__lastSavedUrl = "" self.entryRemoved.emit(itm) self.__saveTimer.saveIfNeccessary() if nextTimeout > 0: self.__expiredTimer.start(nextTimeout * 1000) def daysToExpire(self): """ Public method to get the days for entry expiration. @return days for entry expiration (integer) """ return self.__daysToExpire def setDaysToExpire(self, limit): """ Public method to set the days for entry expiration. @param limit days for entry expiration (integer) """ if self.__daysToExpire == limit: return self.__daysToExpire = limit self.__checkForExpired() self.__saveTimer.changeOccurred() def preferencesChanged(self): """ Public method to indicate a change of preferences. """ self.setDaysToExpire(Preferences.getHelp("HistoryLimit")) @pyqtSlot() def clear(self, period=0): """ Public slot to clear the complete history. @param period history period in milliseconds to be cleared (integer) """ if period == 0: self.__history = [] self.historyReset.emit() else: breakMS = QDateTime.currentMSecsSinceEpoch() - period while self.__history and \ (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() > breakMS): itm = self.__history.pop(0) self.entryRemoved.emit(itm) self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.historyCleared.emit() def getFileName(self): """ Public method to get the file name of the history file. @return name of the history file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "history") def reload(self): """ Public method to reload the history. """ self.__load() def __load(self): """ Private method to load the saved history entries from disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): return if not historyFile.open(QIODevice.ReadOnly): E5MessageBox.warning( None, self.tr("Loading History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format( historyFile.fileName, historyFile.errorString())) return history = [] # double check, that the history file is sorted as it is read needToSort = False lastInsertedItem = HistoryEntry() data = QByteArray(historyFile.readAll()) stream = QDataStream(data, QIODevice.ReadOnly) stream.setVersion(QDataStream.Qt_4_6) while not stream.atEnd(): ver = stream.readUInt32() if ver != HISTORY_VERSION: continue itm = HistoryEntry() itm.url = Utilities.readStringFromStream(stream) stream >> itm.dateTime itm.title = Utilities.readStringFromStream(stream) if not itm.dateTime.isValid(): continue if itm == lastInsertedItem: if not lastInsertedItem.title and len(history) > 0: history[0].title = itm.title continue if not needToSort and history and lastInsertedItem < itm: needToSort = True history.insert(0, itm) lastInsertedItem = itm historyFile.close() if needToSort: history.sort() self.setHistory(history, True) # if the history had to be sorted, rewrite the history sorted if needToSort: self.__lastSavedUrl = "" self.__saveTimer.changeOccurred() def save(self): """ Public slot to save the history entries to disk. """ historyFile = QFile(self.getFileName()) if not historyFile.exists(): self.__lastSavedUrl = "" saveAll = self.__lastSavedUrl == "" first = len(self.__history) - 1 if not saveAll: # find the first one to save for index in range(len(self.__history)): if self.__history[index].url == self.__lastSavedUrl: first = index - 1 break if first == len(self.__history) - 1: saveAll = True if saveAll: # use a temporary file when saving everything f = QTemporaryFile() f.setAutoRemove(False) opened = f.open() else: f = historyFile opened = f.open(QIODevice.Append) if not opened: E5MessageBox.warning( None, self.tr("Saving History"), self.tr("""<p>Unable to open history file <b>{0}</b>.<br/>""" """Reason: {1}</p>""").format(f.fileName(), f.errorString())) return for index in range(first, -1, -1): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_4_6) itm = self.__history[index] stream.writeUInt32(HISTORY_VERSION) stream.writeString(itm.url.encode("utf-8")) stream << itm.dateTime stream.writeString(itm.title.encode('utf-8')) f.write(data) f.close() if saveAll: if historyFile.exists() and not historyFile.remove(): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error removing old history file <b>{0}</b>.""" """<br/>Reason: {1}</p>""").format( historyFile.fileName(), historyFile.errorString())) if not f.copy(historyFile.fileName()): E5MessageBox.warning( None, self.tr("Saving History"), self.tr( """<p>Error moving new history file over old one """ """(<b>{0}</b>).<br/>Reason: {1}</p>""").format( historyFile.fileName(), f.errorString())) self.historySaved.emit() try: self.__lastSavedUrl = self.__history[0].url except IndexError: self.__lastSavedUrl = "" def __refreshFrequencies(self): """ Private slot to recalculate the refresh frequencies. """ self.__historyFilterModel.recalculateFrequencies() self.__startFrequencyTimer() def __startFrequencyTimer(self): """ Private method to start the timer to recalculate the frequencies. """ tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0)) self.__frequencyTimer.start( QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
class AdBlockManager(QObject): """ Class implementing the AdBlock manager. @signal rulesChanged() emitted after some rule has changed """ rulesChanged = pyqtSignal() requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription) def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__defaultSubscriptionUrlString = \ "abp:subscribe?location=" \ "https://easylist-downloads.adblockplus.org/easylist.txt&"\ "title=EasyList" self.__customSubscriptionUrlString = \ bytes(self.__customSubscriptionUrl().toEncoded()).decode() self.rulesChanged.connect(self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__adBlockDialog and self.__adBlockDialog.close() self.__adBlockExceptionsDialog and \ self.__adBlockExceptionsDialog.close() self.__saveTimer.saveIfNeccessary() def isEnabled(self): """ Public method to check, if blocking ads is enabled. @return flag indicating the enabled state (boolean) """ if not self.__loaded: self.load() return self.__enabled def setEnabled(self, enabled): """ Public slot to set the enabled state. @param enabled flag indicating the enabled state (boolean) """ if self.isEnabled() == enabled: return import Helpviewer.HelpWindow self.__enabled = enabled for mainWindow in Helpviewer.HelpWindow.HelpWindow.mainWindows(): mainWindow.adBlockIcon().setEnabled(enabled) if enabled: self.__loadSubscriptions() self.rulesChanged.emit() def network(self): """ Public method to get a reference to the network block object. @return reference to the network block object (AdBlockNetwork) """ if self.__adBlockNetwork is None: from .AdBlockNetwork import AdBlockNetwork self.__adBlockNetwork = AdBlockNetwork(self) return self.__adBlockNetwork def page(self): """ Public method to get a reference to the page block object. @return reference to the page block object (AdBlockPage) """ if self.__adBlockPage is None: from .AdBlockPage import AdBlockPage self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. @return URL for custom subscriptions (QUrl) """ dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. @return URL for custom subscriptions (QUrl) """ location = self.__customSubscriptionLocation() encodedUrl = bytes(location.toEncoded()).decode() url = QUrl("abp:subscribe?location={0}&title={1}".format( encodedUrl, self.tr("Custom Rules"))) return url def customRules(self): """ Public method to get a subscription for custom rules. @return subscription object for custom rules (AdBlockSubscription) """ location = self.__customSubscriptionLocation() for subscription in self.__subscriptions: if subscription.location() == location: return subscription url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, True, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription def subscriptions(self): """ Public method to get all subscriptions. @return list of subscriptions (list of AdBlockSubscription) """ if not self.__loaded: self.load() return self.__subscriptions[:] def subscription(self, location): """ Public method to get a subscription based on its location. @param location location of the subscription to search for (string) @return subscription or None (AdBlockSubscription) """ if location != "": for subscription in self.__subscriptions: if subscription.location().toString() == location: return subscription return None def updateAllSubscriptions(self): """ Public method to update all subscriptions. """ for subscription in self.__subscriptions: subscription.updateNow() def removeSubscription(self, subscription, emitSignal=True): """ Public method to remove an AdBlock subscription. @param subscription AdBlock subscription to be removed (AdBlockSubscription) @param emitSignal flag indicating to send a signal (boolean) """ if subscription is None: return if subscription.url().toString().startswith( (self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString)): return try: self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() QFile.remove(rulesFileName) requiresSubscriptions = self.getRequiresSubscriptions(subscription) for requiresSubscription in requiresSubscriptions: self.removeSubscription(requiresSubscription, False) if emitSignal: self.rulesChanged.emit() except ValueError: pass def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. @param subscription AdBlock subscription to be added (AdBlockSubscription) """ if subscription is None: return self.__subscriptions.insert(-1, subscription) subscription.rulesChanged.connect(self.rulesChanged) subscription.changed.connect(self.rulesChanged) subscription.enabledChanged.connect(self.rulesChanged) self.rulesChanged.emit() def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return Preferences.setHelp("AdBlockEnabled", self.__enabled) if self.__subscriptionsLoaded: subscriptions = [] requiresSubscriptions = [] # intermediate store for subscription requiring others for subscription in self.__subscriptions: if subscription is None: continue urlString = bytes(subscription.url().toEncoded()).decode() if "requiresLocation" in urlString: requiresSubscriptions.append(urlString) else: subscriptions.append(urlString) subscription.saveRules() for subscription in requiresSubscriptions: subscriptions.insert(-1, subscription) # custom should be last Preferences.setHelp("AdBlockSubscriptions", subscriptions) def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return self.__loaded = True self.__enabled = Preferences.getHelp("AdBlockEnabled") if self.__enabled: self.__loadSubscriptions() def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return subscriptions = Preferences.getHelp("AdBlockSubscriptions") if subscriptions: for subscription in subscriptions: if subscription.startswith( self.__defaultSubscriptionUrlString): break else: subscriptions.insert(0, self.__defaultSubscriptionUrlString) for subscription in subscriptions: if subscription.startswith(self.__customSubscriptionUrlString): break else: subscriptions.append(self.__customSubscriptionUrlString) else: subscriptions = [ self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString ] for subscription in subscriptions: url = QUrl.fromEncoded(subscription.encode("utf-8")) adBlockSubscription = AdBlockSubscription( url, subscription.startswith(self.__customSubscriptionUrlString), self, subscription.startswith(self.__defaultSubscriptionUrlString)) adBlockSubscription.rulesChanged.connect(self.rulesChanged) adBlockSubscription.changed.connect(self.rulesChanged) adBlockSubscription.enabledChanged.connect(self.rulesChanged) self.__subscriptions.append(adBlockSubscription) self.__subscriptionsLoaded = True def loadRequiredSubscription(self, location, title): """ Public method to load a subscription required by another one. @param location location of the required subscription (string) @param title title of the required subscription (string) """ # Step 1: check, if the subscription is in the list of subscriptions urlString = "abp:subscribe?location={0}&title={1}".format( location, title) for subscription in self.__subscriptions: if subscription.url().toString().startswith(urlString): # We found it! return # Step 2: if it is not, get it url = QUrl.fromEncoded(urlString.encode("utf-8")) adBlockSubscription = AdBlockSubscription(url, False, self) self.addSubscription(adBlockSubscription) self.requiredSubscriptionLoaded.emit(adBlockSubscription) def getRequiresSubscriptions(self, subscription): """ Public method to get a list of subscriptions, that require the given one. @param subscription subscription to check for (AdBlockSubscription) @return list of subscription requiring the given one (list of AdBlockSubscription) """ subscriptions = [] location = subscription.location().toString() for subscription in self.__subscriptions: if subscription.requiresLocation() == location: subscriptions.append(subscription) return subscriptions def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. @return reference to the dialog (AdBlockDialog) """ if self.__adBlockDialog is None: from .AdBlockDialog import AdBlockDialog self.__adBlockDialog = AdBlockDialog() self.__adBlockDialog.show() return self.__adBlockDialog def showRule(self): """ Public slot to show an AdBlock rule. """ act = self.sender() if act is not None: rule = act.data() if rule: self.showDialog().showRule(rule) def elementHidingRules(self): """ Public method to get the element hiding rules. @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: rules += subscription.elementHidingRules() if rules: # remove last ", rules = rules[:-1] return rules def elementHidingRulesForDomain(self, url): """ Public method to get the element hiding rules for a domain. @param url URL to get hiding rules for (QUrl) @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: if subscription.elemHideDisabledForUrl(url): return "" rules += subscription.elementHidingRulesForDomain(url.host()) if rules: # remove last ", rules = rules[:-1] return rules def exceptions(self): """ Public method to get a list of excepted hosts. @return list of excepted hosts (list of string) """ return self.__exceptedHosts def setExceptions(self, hosts): """ Public method to set the list of excepted hosts. @param hosts list of excepted hosts (list of string) """ self.__exceptedHosts = hosts[:] Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def addException(self, host): """ Public method to add an exception. @param host to be excepted (string) """ if host and host not in self.__exceptedHosts: self.__exceptedHosts.append(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def removeException(self, host): """ Public method to remove an exception. @param host to be removed from the list of exceptions (string) """ if host in self.__exceptedHosts: self.__exceptedHosts.remove(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def isHostExcepted(self, host): """ Public slot to check, if a host is excepted. @param host host to check (string) @return flag indicating an exception (boolean) """ return host in self.__exceptedHosts def showExceptionsDialog(self): """ Public method to show the AdBlock Exceptions dialog. @return reference to the exceptions dialog (AdBlockExceptionsDialog) """ if self.__adBlockExceptionsDialog is None: from .AdBlockExceptionsDialog import AdBlockExceptionsDialog self.__adBlockExceptionsDialog = AdBlockExceptionsDialog() self.__adBlockExceptionsDialog.load(self.__exceptedHosts) self.__adBlockExceptionsDialog.show() return self.__adBlockExceptionsDialog
class DownloadManager(QDialog, Ui_DownloadManager): """ Class implementing the download manager. """ RemoveNever = 0 RemoveExit = 1 RemoveSuccessFullDownload = 2 def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(DownloadManager, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.__saveTimer = AutoSaver(self, self.save) self.__model = DownloadModel(self) self.__manager = Helpviewer.HelpWindow.HelpWindow\ .networkAccessManager() self.__iconProvider = None self.__downloads = [] self.__downloadDirectory = "" self.__loaded = False self.setDownloadDirectory(Preferences.getUI("DownloadPath")) self.downloadsView.setShowGrid(False) self.downloadsView.verticalHeader().hide() self.downloadsView.horizontalHeader().hide() self.downloadsView.setAlternatingRowColors(True) self.downloadsView.horizontalHeader().setStretchLastSection(True) self.downloadsView.setModel(self.__model) self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu) self.downloadsView.customContextMenuRequested.connect( self.__customContextMenuRequested) self.__load() def __customContextMenuRequested(self, pos): """ Private slot to handle the context menu request for the bookmarks tree. @param pos position the context menu was requested (QPoint) """ menu = QMenu() selectedRowsCount = len( self.downloadsView.selectionModel().selectedRows()) if selectedRowsCount == 1: row = self.downloadsView.selectionModel().selectedRows()[0].row() itm = self.__downloads[row] if itm.downloadCanceled(): menu.addAction(UI.PixmapCache.getIcon("restart.png"), self.tr("Retry"), self.__contextMenuRetry) else: if itm.downloadedSuccessfully(): menu.addAction(UI.PixmapCache.getIcon("open.png"), self.tr("Open"), self.__contextMenuOpen) elif itm.downloading(): menu.addAction(UI.PixmapCache.getIcon("stopLoading.png"), self.tr("Cancel"), self.__contextMenuCancel) menu.addSeparator() menu.addAction(self.tr("Open Containing Folder"), self.__contextMenuOpenFolder) menu.addSeparator() menu.addAction(self.tr("Go to Download Page"), self.__contextMenuGotoPage) menu.addAction(self.tr("Copy Download Link"), self.__contextMenuCopyLink) menu.addSeparator() menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) if selectedRowsCount > 1 or \ (selectedRowsCount == 1 and not self.__downloads[ self.downloadsView.selectionModel().selectedRows()[0].row()] .downloading()): menu.addSeparator() menu.addAction(self.tr("Remove From List"), self.__contextMenuRemoveSelected) menu.exec_(QCursor.pos()) def shutdown(self): """ Public method to stop the download manager. """ self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.close() def activeDownloads(self): """ Public method to get the number of active downloads. @return number of active downloads (integer) """ count = 0 for download in self.__downloads: if download.downloading(): count += 1 return count def allowQuit(self): """ Public method to check, if it is ok to quit. @return flag indicating allowance to quit (boolean) """ if self.activeDownloads() > 0: res = E5MessageBox.yesNo( self, self.tr(""), self.tr( """There are %n downloads in progress.\n""" """Do you want to quit anyway?""", "", self.activeDownloads()), icon=E5MessageBox.Warning) if not res: self.show() return False return True def download(self, requestOrUrl, requestFileName=False, mainWindow=None): """ Public method to download a file. @param requestOrUrl reference to a request object (QNetworkRequest) or a URL to be downloaded (QUrl) @keyparam requestFileName flag indicating to ask for the download file name (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ request = QNetworkRequest(requestOrUrl) if request.url().isEmpty(): return self.handleUnsupportedContent(self.__manager.get(request), requestFileName=requestFileName, download=True, mainWindow=mainWindow) def handleUnsupportedContent(self, reply, requestFileName=False, webPage=None, download=False, mainWindow=None): """ Public method to handle unsupported content by downloading the referenced resource. @param reply reference to the reply object (QNetworkReply) @keyparam requestFileName indicating to ask for a filename (boolean) @keyparam webPage reference to the web page (HelpWebPage) @keyparam download flag indicating a download request (boolean) @keyparam mainWindow reference to the main window (HelpWindow) """ if reply is None or reply.url().isEmpty(): return size = reply.header(QNetworkRequest.ContentLengthHeader) if size == 0: return from .DownloadItem import DownloadItem itm = DownloadItem(reply=reply, requestFilename=requestFileName, webPage=webPage, download=download, parent=self, mainWindow=mainWindow) self.__addItem(itm) if itm.canceledFileSelect(): return if not self.isVisible(): self.show() self.activateWindow() self.raise_() def __addItem(self, itm): """ Private method to add a download to the list of downloads. @param itm reference to the download item (DownloadItem) """ itm.statusChanged.connect(self.__updateRow) itm.downloadFinished.connect(self.__finished) row = len(self.__downloads) self.__model.beginInsertRows(QModelIndex(), row, row) self.__downloads.append(itm) self.__model.endInsertRows() self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5) # just in case the download finished before the constructor returned self.__updateRow(itm) self.changeOccurred() self.__updateActiveItemCount() def __updateRow(self, itm=None): """ Private slot to update a download item. @param itm reference to the download item (DownloadItem) """ if itm is None: itm = self.sender() if itm not in self.__downloads: return row = self.__downloads.index(itm) if self.__iconProvider is None: self.__iconProvider = QFileIconProvider() icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) if icon.isNull(): icon = self.style().standardIcon(QStyle.SP_FileIcon) itm.setIcon(icon) oldHeight = self.downloadsView.rowHeight(row) self.downloadsView.setRowHeight( row, max(oldHeight, itm.minimumSizeHint().height() * 1.5)) remove = False globalSettings = QWebSettings.globalSettings() if not itm.downloading() and \ globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): remove = True if itm.downloadedSuccessfully() and \ self.removePolicy() == DownloadManager.RemoveSuccessFullDownload: remove = True if remove: self.__model.removeRow(row) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) # record the change self.changeOccurred() def removePolicy(self): """ Public method to get the remove policy. @return remove policy (integer) """ return Preferences.getHelp("DownloadManagerRemovePolicy") def setRemovePolicy(self, policy): """ Public method to set the remove policy. @param policy policy to be set (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) """ assert policy in (DownloadManager.RemoveExit, DownloadManager.RemoveNever, DownloadManager.RemoveSuccessFullDownload) if policy == self.removePolicy(): return Preferences.setHelp("DownloadManagerRemovePolicy", self.policy) def save(self): """ Public method to save the download settings. """ if not self.__loaded: return Preferences.setHelp("DownloadManagerSize", self.size()) Preferences.setHelp("DownloadManagerPosition", self.pos()) if self.removePolicy() == DownloadManager.RemoveExit: return downloads = [] for download in self.__downloads: downloads.append(download.getData()) Preferences.setHelp("DownloadManagerDownloads", downloads) def __load(self): """ Private method to load the download settings. """ if self.__loaded: return size = Preferences.getHelp("DownloadManagerSize") if size.isValid(): self.resize(size) pos = Preferences.getHelp("DownloadManagerPosition") self.move(pos) downloads = Preferences.getHelp("DownloadManagerDownloads") for download in downloads: if not download[0].isEmpty() and \ download[1] != "": from .DownloadItem import DownloadItem itm = DownloadItem(parent=self) itm.setData(download) self.__addItem(itm) self.cleanupButton.setEnabled( (len(self.__downloads) - self.activeDownloads()) > 0) self.__loaded = True self.__updateActiveItemCount() def cleanup(self): """ Public slot to cleanup the downloads. """ self.on_cleanupButton_clicked() @pyqtSlot() def on_cleanupButton_clicked(self): """ Private slot cleanup the downloads. """ if len(self.__downloads) == 0: return self.__model.removeRows(0, len(self.__downloads)) if len(self.__downloads) == 0 and \ self.__iconProvider is not None: self.__iconProvider = None self.changeOccurred() self.__updateActiveItemCount() def __updateItemCount(self): """ Private method to update the count label. """ count = len(self.__downloads) self.countLabel.setText(self.tr("%n Download(s)", "", count)) def __updateActiveItemCount(self): """ Private method to update the window title. """ count = self.activeDownloads() if count > 0: self.setWindowTitle(self.tr("Downloading %n file(s)", "", count)) else: self.setWindowTitle(self.tr("Downloads")) def __finished(self): """ Private slot to handle a finished download. """ self.__updateActiveItemCount() if self.isVisible(): QApplication.alert(self) def setDownloadDirectory(self, directory): """ Public method to set the current download directory. @param directory current download directory (string) """ self.__downloadDirectory = directory if self.__downloadDirectory != "": self.__downloadDirectory += "/" def downloadDirectory(self): """ Public method to get the current download directory. @return current download directory (string) """ return self.__downloadDirectory def count(self): """ Public method to get the number of downloads. @return number of downloads (integer) """ return len(self.__downloads) def downloads(self): """ Public method to get a reference to the downloads. @return reference to the downloads (list of DownloadItem) """ return self.__downloads def changeOccurred(self): """ Public method to signal a change. """ self.__saveTimer.changeOccurred() self.__updateItemCount() ########################################################################### ## Context menu related methods below ########################################################################### def __currentItem(self): """ Private method to get a reference to the current item. @return reference to the current item (DownloadItem) """ index = self.downloadsView.currentIndex() if index and index.isValid(): row = index.row() return self.__downloads[row] return None def __contextMenuRetry(self): """ Private method to retry of the download. """ itm = self.__currentItem() if itm is not None: itm.retry() def __contextMenuOpen(self): """ Private method to open the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFile() def __contextMenuOpenFolder(self): """ Private method to open the folder containing the downloaded file. """ itm = self.__currentItem() if itm is not None: itm.openFolder() def __contextMenuCancel(self): """ Private method to cancel the current download. """ itm = self.__currentItem() if itm is not None: itm.cancelDownload() def __contextMenuGotoPage(self): """ Private method to open the download page. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl() Helpviewer.HelpWindow.HelpWindow.mainWindow().openUrl(url, "") def __contextMenuCopyLink(self): """ Private method to copy the download link to the clipboard. """ itm = self.__currentItem() if itm is not None: url = itm.getPageUrl().toString() QApplication.clipboard().setText(url) def __contextMenuSelectAll(self): """ Private method to select all downloads. """ self.downloadsView.selectAll() def __contextMenuRemoveSelected(self): """ Private method to remove the selected downloads from the list. """ self.downloadsView.removeSelected()
class PasswordManager(QObject): """ Class implementing the password manager. @signal changed() emitted to indicate a change """ SEPARATOR = "====================" FORMS = "=====FORMS=====" NEVER = "=====NEVER=====" def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred) def clear(self): """ Public slot to clear the saved passwords. """ if not self.__loaded: self.__load() self.__logins = {} self.__loginForms = {} self.__never = [] self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.emit(SIGNAL("changed()")) def getLogin(self, url, realm): """ Public method to get the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string or QString) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) try: return self.__logins[key][0], Utilities.pwDecode(self.__logins[key][1]) except KeyError: return "", "" def setLogin(self, url, realm, username, password): """ Public method to set the login credentials. @param url URL to set the credentials for (QUrl) @param realm realm to set the credentials for (string or QString) @param username username for the login (string or QString) @param password password for the login (string or QString) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) self.__logins[key] = (unicode(username), Utilities.pwEncode(password)) self.emit(SIGNAL("changed()")) def __createKey(self, url, realm): """ Private method to create the key string for the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string or QString) @return key string (string) """ realm = unicode(realm) if realm: key = "%s://%s (%s)" % (url.scheme(), url.authority(), realm) else: key = "%s://%s" % (url.scheme(), url.authority()) return key def save(self): """ Public slot to save the login entries to disk. """ if not self.__loaded: return loginFile = os.path.join(Utilities.getConfigDir(), "browser", "logins") try: f = open(loginFile, "w") for key, login in self.__logins.items(): f.write("%s\n" % key) f.write("%s\n" % login[0]) f.write("%s\n" % login[1]) f.write("%s\n" % self.SEPARATOR) if self.__loginForms: f.write("%s\n" % self.FORMS) for key, form in self.__loginForms.items(): f.write("%s\n" % key) form.save(f) f.write("%s\n" % self.SEPARATOR) if self.__never: f.write("%s\n" % self.NEVER) for key in self.__never: f.write("%s\n") % key f.close() except IOError, err: KQMessageBox.critical(None, self.trUtf8("Saving login data"), self.trUtf8("""<p>Login data could not be saved to <b>%1</b></p>""" """<p>Reason: %2</p>""").arg(loginFile).arg(str(err))) return
class UserAgentManager(QObject): """ Class implementing a user agent manager. @signal changed() emitted to indicate a change @signal userAgentSettingsSaved() emitted after the user agent settings were saved """ changed = pyqtSignal() userAgentSettingsSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(UserAgentManager, self).__init__(parent) self.__agents = {} # dictionary with agent strings indexed by host name self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "userAgentSettings.xml") def save(self): """ Public slot to save the user agent entries to disk. """ if not self.__loaded: return from .UserAgentWriter import UserAgentWriter agentFile = self.getFileName() writer = UserAgentWriter() if not writer.write(agentFile, self.__agents): E5MessageBox.critical( None, self.tr("Saving user agent data"), self.tr( """<p>User agent data could not be saved to""" """ <b>{0}</b></p>""").format(agentFile)) else: self.userAgentSettingsSaved.emit() def __load(self): """ Private method to load the saved user agent settings. """ agentFile = self.getFileName() if not os.path.exists(agentFile): self.__loadNonXml(os.path.splitext(agentFile)[0]) else: from .UserAgentReader import UserAgentReader reader = UserAgentReader() self.__agents = reader.read(agentFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading user agent data"), self.tr("""Error when loading user agent data on""" """ line {0}, column {1}:\n{2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def __loadNonXml(self, agentFile): """ Private method to load non-XML user agent files. This method is to convert from the old, non-XML format to the new XML based format. @param agentFile name of the non-XML user agent file (string) """ if os.path.exists(agentFile): try: f = open(agentFile, "r", encoding="utf-8") lines = f.read() f.close() except IOError as err: E5MessageBox.critical( None, self.tr("Loading user agent data"), self.tr("""<p>User agent data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: {1}</p>""") .format(agentFile, str(err))) return for line in lines.splitlines(): if not line or \ line.startswith("#") or \ "@@" not in line: continue host, agent = line.split("@@", 1) self.__agents[host] = agent os.remove(agentFile) self.__loaded = True # this does the conversion self.save() def reload(self): """ Public method to reload the user agent settings. """ if not self.__loaded: return self.__agents = {} self.__load() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def removeUserAgent(self, host): """ Public method to remove a user agent entry. @param host host name (string) """ if host in self.__agents: del self.__agents[host] self.changed.emit() def allHostNames(self): """ Public method to get a list of all host names we a user agent setting for. @return sorted list of all host names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__agents.keys()) def hostsCount(self): """ Public method to get the number of available user agent settings. @return number of user agent settings (integer) """ if not self.__loaded: self.__load() return len(self.__agents) def userAgent(self, host): """ Public method to get the user agent setting for a host. @param host host name (string) @return user agent string (string) """ if not self.__loaded: self.__load() if host not in self.__agents: return "" return self.__agents[host] def setUserAgent(self, host, agent): """ Public method to set the user agent string for a host. @param host host name (string) @param agent user agent string (string) """ if host != "" and agent != "": self.__agents[host] = agent self.changed.emit() def userAgentForUrl(self, url): """ Public method to determine the user agent for the given URL. @param url URL to determine user agent for (QUrl) @return user agent string (string) """ if url.isValid(): host = url.host() return self.userAgent(host) return "" def setUserAgentForUrl(self, url, agent): """ Public method to set the user agent string for an URL. @param url URL to register user agent setting for (QUrl) @param agent new current user agent string (string) """ if url.isValid(): host = url.host() self.setUserAgent(host, agent)
class PasswordManager(QObject): """ Class implementing the password manager. @signal changed() emitted to indicate a change @signal passwordsSaved() emitted after the passwords were saved """ changed = pyqtSignal() passwordsSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(PasswordManager, self).__init__(parent) # setup userscript to monitor forms script = QWebEngineScript() script.setName("_eric_passwordmonitor") script.setInjectionPoint(QWebEngineScript.DocumentReady) script.setWorldId(WebBrowserPage.SafeJsWorld) script.setRunsOnSubFrames(True) script.setSourceCode(Scripts.setupFormObserver()) profile = WebBrowser.WebBrowserWindow.WebBrowserWindow.webProfile() profile.scripts().insert(script) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def clear(self): """ Public slot to clear the saved passwords. """ if not self.__loaded: self.__load() self.__logins = {} self.__loginForms = {} self.__never = [] self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.changed.emit() def getLogin(self, url, realm): """ Public method to get the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) try: return self.__logins[key][0], Utilities.crypto.pwConvert( self.__logins[key][1], encode=False) except KeyError: return "", "" def setLogin(self, url, realm, username, password): """ Public method to set the login credentials. @param url URL to set the credentials for (QUrl) @param realm realm to set the credentials for (string) @param username username for the login (string) @param password password for the login (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) self.__logins[key] = (username, Utilities.crypto.pwConvert(password, encode=True)) self.changed.emit() def __createKey(self, url, realm): """ Private method to create the key string for the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return key string (string) """ authority = url.authority() if authority.startswith("@"): authority = authority[1:] if realm: key = "{0}://{1} ({2})".format(url.scheme(), authority, realm) else: key = "{0}://{1}".format(url.scheme(), authority) return key def getFileName(self): """ Public method to get the file name of the passwords file. @return name of the passwords file (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "logins.xml") def save(self): """ Public slot to save the login entries to disk. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate(): from .PasswordWriter import PasswordWriter loginFile = self.getFileName() writer = PasswordWriter() if not writer.write(loginFile, self.__logins, self.__loginForms, self.__never): E5MessageBox.critical( None, self.tr("Saving login data"), self.tr("""<p>Login data could not be saved to""" """ <b>{0}</b></p>""").format(loginFile)) else: self.passwordsSaved.emit() def __load(self): """ Private method to load the saved login credentials. """ if self.__loaded: return loginFile = self.getFileName() if os.path.exists(loginFile): from .PasswordReader import PasswordReader reader = PasswordReader() self.__logins, self.__loginForms, self.__never = reader.read( loginFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading login data"), self.tr("""Error when loading login data on""" """ line {0}, column {1}:\n{2}""").format( reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def reload(self): """ Public method to reload the login data. """ if not self.__loaded: return self.__loaded = False self.__load() def close(self): """ Public method to close the passwords manager. """ self.__saveTimer.saveIfNeccessary() def removePassword(self, site): """ Public method to remove a password entry. @param site web site name (string) """ if site in self.__logins: del self.__logins[site] if site in self.__loginForms: del self.__loginForms[site] self.changed.emit() def allSiteNames(self): """ Public method to get a list of all site names. @return sorted list of all site names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__logins.keys()) def sitesCount(self): """ Public method to get the number of available sites. @return number of sites (integer) """ if not self.__loaded: self.__load() return len(self.__logins) def siteInfo(self, site): """ Public method to get a reference to the named site. @param site web site name (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() if site not in self.__logins: return None return self.__logins[site][0], Utilities.crypto.pwConvert( self.__logins[site][1], encode=False) def formSubmitted(self, urlStr, userName, password, data, page): """ Public method to record login data. @param urlStr form submission URL @type str @param userName name of the user @type str @param password user password @type str @param data data to be submitted @type QByteArray @param page reference to the calling page @type QWebEnginePage """ # shall passwords be saved? if not Preferences.getUser("SavePasswords"): return if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate(): return if not self.__loaded: self.__load() if urlStr in self.__never: return if userName and password: url = QUrl(urlStr) url = self.__stripUrl(url) key = self.__createKey(url, "") if key not in self.__loginForms: mb = E5MessageBox.E5MessageBox( E5MessageBox.Question, self.tr("Save password"), self.tr( """<b>Would you like to save this password?</b><br/>""" """To review passwords you have saved and remove""" """ them, use the password management dialog of the""" """ Settings menu."""), modal=True, parent=page.view()) neverButton = mb.addButton(self.tr("Never for this site"), E5MessageBox.DestructiveRole) noButton = mb.addButton(self.tr("Not now"), E5MessageBox.RejectRole) mb.addButton(E5MessageBox.Yes) mb.exec_() if mb.clickedButton() == neverButton: self.__never.append(url.toString()) return elif mb.clickedButton() == noButton: return self.__logins[key] = (userName, Utilities.crypto.pwConvert(password, encode=True)) from .LoginForm import LoginForm form = LoginForm() form.url = url form.name = userName form.postData = Utilities.crypto.pwConvert( bytes(data).decode("utf-8"), encode=True) self.__loginForms[key] = form self.changed.emit() def __stripUrl(self, url): """ Private method to strip off all unneeded parts of a URL. @param url URL to be stripped (QUrl) @return stripped URL (QUrl) """ cleanUrl = QUrl(url) cleanUrl.setQuery("") cleanUrl.setUserInfo("") authority = cleanUrl.authority() if authority.startswith("@"): authority = authority[1:] cleanUrl = QUrl("{0}://{1}{2}".format(cleanUrl.scheme(), authority, cleanUrl.path())) cleanUrl.setFragment("") return cleanUrl def completePage(self, page): """ Public slot to complete login forms with saved data. @param page reference to the web page (WebBrowserPage) """ if page is None: return if not self.__loaded: self.__load() url = page.url() url = self.__stripUrl(url) key = self.__createKey(url, "") if (key not in self.__loginForms or key not in self.__logins): return form = self.__loginForms[key] if form.url != url: return postData = QByteArray( Utilities.crypto.pwConvert(form.postData, encode=False).encode("utf-8")) script = Scripts.completeFormData(postData) page.runJavaScript(script, WebBrowserPage.SafeJsWorld) def masterPasswordChanged(self, oldPassword, newPassword): """ Public slot to handle the change of the master password. @param oldPassword current master password (string) @param newPassword new master password (string) """ if not self.__loaded: self.__load() progress = E5ProgressDialog( self.tr("Re-encoding saved passwords..."), None, 0, len(self.__logins) + len(self.__loginForms), self.tr("%v/%m Passwords"), QApplication.activeModalWidget()) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Passwords")) count = 0 # step 1: do the logins for key in self.__logins: progress.setValue(count) QCoreApplication.processEvents() username, pwHash = self.__logins[key] pwHash = Utilities.crypto.pwRecode(pwHash, oldPassword, newPassword) self.__logins[key] = (username, pwHash) count += 1 # step 2: do the login forms for key in self.__loginForms: progress.setValue(count) QCoreApplication.processEvents() postData = self.__loginForms[key].postData postData = Utilities.crypto.pwRecode(postData, oldPassword, newPassword) self.__loginForms[key].postData = postData count += 1 progress.setValue(len(self.__logins) + len(self.__loginForms)) QCoreApplication.processEvents() self.changed.emit()
class AdBlockManager(QObject): """ Class implementing the AdBlock manager. @signal rulesChanged() emitted after some rule has changed """ rulesChanged = pyqtSignal() requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription) def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(AdBlockManager, self).__init__(parent) self.__loaded = False self.__subscriptionsLoaded = False self.__enabled = False self.__adBlockDialog = None self.__adBlockExceptionsDialog = None self.__adBlockNetwork = None self.__adBlockPage = None self.__subscriptions = [] self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions") self.__saveTimer = AutoSaver(self, self.save) self.__defaultSubscriptionUrlString = \ "abp:subscribe?location=" \ "https://easylist-downloads.adblockplus.org/easylist.txt&"\ "title=EasyList" self.__customSubscriptionUrlString = \ bytes(self.__customSubscriptionUrl().toEncoded()).decode() self.rulesChanged.connect(self.__saveTimer.changeOccurred) def close(self): """ Public method to close the open search engines manager. """ self.__adBlockDialog and self.__adBlockDialog.close() self.__adBlockExceptionsDialog and \ self.__adBlockExceptionsDialog.close() self.__saveTimer.saveIfNeccessary() def isEnabled(self): """ Public method to check, if blocking ads is enabled. @return flag indicating the enabled state (boolean) """ if not self.__loaded: self.load() return self.__enabled def setEnabled(self, enabled): """ Public slot to set the enabled state. @param enabled flag indicating the enabled state (boolean) """ if self.isEnabled() == enabled: return import Helpviewer.HelpWindow self.__enabled = enabled for mainWindow in Helpviewer.HelpWindow.HelpWindow.mainWindows(): mainWindow.adBlockIcon().setEnabled(enabled) if enabled: self.__loadSubscriptions() self.rulesChanged.emit() def network(self): """ Public method to get a reference to the network block object. @return reference to the network block object (AdBlockNetwork) """ if self.__adBlockNetwork is None: from .AdBlockNetwork import AdBlockNetwork self.__adBlockNetwork = AdBlockNetwork(self) return self.__adBlockNetwork def page(self): """ Public method to get a reference to the page block object. @return reference to the page block object (AdBlockPage) """ if self.__adBlockPage is None: from .AdBlockPage import AdBlockPage self.__adBlockPage = AdBlockPage(self) return self.__adBlockPage def __customSubscriptionLocation(self): """ Private method to generate the path for custom subscriptions. @return URL for custom subscriptions (QUrl) """ dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions") if not os.path.exists(dataDir): os.makedirs(dataDir) fileName = os.path.join(dataDir, "adblock_subscription_custom") return QUrl.fromLocalFile(fileName) def __customSubscriptionUrl(self): """ Private method to generate the URL for custom subscriptions. @return URL for custom subscriptions (QUrl) """ location = self.__customSubscriptionLocation() encodedUrl = bytes(location.toEncoded()).decode() url = QUrl("abp:subscribe?location={0}&title={1}".format( encodedUrl, self.tr("Custom Rules"))) return url def customRules(self): """ Public method to get a subscription for custom rules. @return subscription object for custom rules (AdBlockSubscription) """ location = self.__customSubscriptionLocation() for subscription in self.__subscriptions: if subscription.location() == location: return subscription url = self.__customSubscriptionUrl() customAdBlockSubscription = AdBlockSubscription(url, True, self) self.addSubscription(customAdBlockSubscription) return customAdBlockSubscription def subscriptions(self): """ Public method to get all subscriptions. @return list of subscriptions (list of AdBlockSubscription) """ if not self.__loaded: self.load() return self.__subscriptions[:] def subscription(self, location): """ Public method to get a subscription based on its location. @param location location of the subscription to search for (string) @return subscription or None (AdBlockSubscription) """ if location != "": for subscription in self.__subscriptions: if subscription.location().toString() == location: return subscription return None def updateAllSubscriptions(self): """ Public method to update all subscriptions. """ for subscription in self.__subscriptions: subscription.updateNow() def removeSubscription(self, subscription, emitSignal=True): """ Public method to remove an AdBlock subscription. @param subscription AdBlock subscription to be removed (AdBlockSubscription) @param emitSignal flag indicating to send a signal (boolean) """ if subscription is None: return if subscription.url().toString().startswith( (self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString)): return try: self.__subscriptions.remove(subscription) rulesFileName = subscription.rulesFileName() QFile.remove(rulesFileName) requiresSubscriptions = self.getRequiresSubscriptions(subscription) for requiresSubscription in requiresSubscriptions: self.removeSubscription(requiresSubscription, False) if emitSignal: self.rulesChanged.emit() except ValueError: pass def addSubscription(self, subscription): """ Public method to add an AdBlock subscription. @param subscription AdBlock subscription to be added (AdBlockSubscription) """ if subscription is None: return self.__subscriptions.insert(-1, subscription) subscription.rulesChanged.connect(self.rulesChanged) subscription.changed.connect(self.rulesChanged) subscription.enabledChanged.connect(self.rulesChanged) self.rulesChanged.emit() def save(self): """ Public method to save the AdBlock subscriptions. """ if not self.__loaded: return Preferences.setHelp("AdBlockEnabled", self.__enabled) if self.__subscriptionsLoaded: subscriptions = [] requiresSubscriptions = [] # intermediate store for subscription requiring others for subscription in self.__subscriptions: if subscription is None: continue urlString = bytes(subscription.url().toEncoded()).decode() if "requiresLocation" in urlString: requiresSubscriptions.append(urlString) else: subscriptions.append(urlString) subscription.saveRules() for subscription in requiresSubscriptions: subscriptions.insert(-1, subscription) # custom should be last Preferences.setHelp("AdBlockSubscriptions", subscriptions) def load(self): """ Public method to load the AdBlock subscriptions. """ if self.__loaded: return self.__loaded = True self.__enabled = Preferences.getHelp("AdBlockEnabled") if self.__enabled: self.__loadSubscriptions() def __loadSubscriptions(self): """ Private method to load the set of subscriptions. """ if self.__subscriptionsLoaded: return subscriptions = Preferences.getHelp("AdBlockSubscriptions") if subscriptions: for subscription in subscriptions: if subscription.startswith( self.__defaultSubscriptionUrlString): break else: subscriptions.insert(0, self.__defaultSubscriptionUrlString) for subscription in subscriptions: if subscription.startswith(self.__customSubscriptionUrlString): break else: subscriptions.append(self.__customSubscriptionUrlString) else: subscriptions = [self.__defaultSubscriptionUrlString, self.__customSubscriptionUrlString] for subscription in subscriptions: url = QUrl.fromEncoded(subscription.encode("utf-8")) adBlockSubscription = AdBlockSubscription( url, subscription.startswith(self.__customSubscriptionUrlString), self, subscription.startswith(self.__defaultSubscriptionUrlString)) adBlockSubscription.rulesChanged.connect(self.rulesChanged) adBlockSubscription.changed.connect(self.rulesChanged) adBlockSubscription.enabledChanged.connect(self.rulesChanged) self.__subscriptions.append(adBlockSubscription) self.__subscriptionsLoaded = True def loadRequiredSubscription(self, location, title): """ Public method to load a subscription required by another one. @param location location of the required subscription (string) @param title title of the required subscription (string) """ # Step 1: check, if the subscription is in the list of subscriptions urlString = "abp:subscribe?location={0}&title={1}".format( location, title) for subscription in self.__subscriptions: if subscription.url().toString().startswith(urlString): # We found it! return # Step 2: if it is not, get it url = QUrl.fromEncoded(urlString.encode("utf-8")) adBlockSubscription = AdBlockSubscription(url, False, self) self.addSubscription(adBlockSubscription) self.requiredSubscriptionLoaded.emit(adBlockSubscription) def getRequiresSubscriptions(self, subscription): """ Public method to get a list of subscriptions, that require the given one. @param subscription subscription to check for (AdBlockSubscription) @return list of subscription requiring the given one (list of AdBlockSubscription) """ subscriptions = [] location = subscription.location().toString() for subscription in self.__subscriptions: if subscription.requiresLocation() == location: subscriptions.append(subscription) return subscriptions def showDialog(self): """ Public slot to show the AdBlock subscription management dialog. @return reference to the dialog (AdBlockDialog) """ if self.__adBlockDialog is None: from .AdBlockDialog import AdBlockDialog self.__adBlockDialog = AdBlockDialog() self.__adBlockDialog.show() return self.__adBlockDialog def showRule(self): """ Public slot to show an AdBlock rule. """ act = self.sender() if act is not None: rule = act.data() if rule: self.showDialog().showRule(rule) def elementHidingRules(self): """ Public method to get the element hiding rules. @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: rules += subscription.elementHidingRules() if rules: # remove last ", rules = rules[:-1] return rules def elementHidingRulesForDomain(self, url): """ Public method to get the element hiding rules for a domain. @param url URL to get hiding rules for (QUrl) @return element hiding rules (string) """ if not self.__enabled: return "" rules = "" for subscription in self.__subscriptions: if subscription.elemHideDisabledForUrl(url): return "" rules += subscription.elementHidingRulesForDomain(url.host()) if rules: # remove last ", rules = rules[:-1] return rules def exceptions(self): """ Public method to get a list of excepted hosts. @return list of excepted hosts (list of string) """ return self.__exceptedHosts def setExceptions(self, hosts): """ Public method to set the list of excepted hosts. @param hosts list of excepted hosts (list of string) """ self.__exceptedHosts = hosts[:] Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def addException(self, host): """ Public method to add an exception. @param host to be excepted (string) """ if host and host not in self.__exceptedHosts: self.__exceptedHosts.append(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def removeException(self, host): """ Public method to remove an exception. @param host to be removed from the list of exceptions (string) """ if host in self.__exceptedHosts: self.__exceptedHosts.remove(host) Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts) def isHostExcepted(self, host): """ Public slot to check, if a host is excepted. @param host host to check (string) @return flag indicating an exception (boolean) """ return host in self.__exceptedHosts def showExceptionsDialog(self): """ Public method to show the AdBlock Exceptions dialog. @return reference to the exceptions dialog (AdBlockExceptionsDialog) """ if self.__adBlockExceptionsDialog is None: from .AdBlockExceptionsDialog import AdBlockExceptionsDialog self.__adBlockExceptionsDialog = AdBlockExceptionsDialog() self.__adBlockExceptionsDialog.load(self.__exceptedHosts) self.__adBlockExceptionsDialog.show() return self.__adBlockExceptionsDialog
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded(BookmarkNode) emitted after a bookmark node has been added @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a bookmark node has been removed @signal entryChanged(BookmarkNode) emitted after a bookmark node has been changed @signal bookmarksSaved() emitted after the bookmarks were saved @signal bookmarksReloaded() emitted after the bookmarks were reloaded """ entryAdded = pyqtSignal(BookmarkNode) entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode) entryChanged = pyqtSignal(BookmarkNode) bookmarksSaved = pyqtSignal() bookmarksReloaded = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(BookmarksManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.entryChanged.connect(self.__saveTimer.changeOccurred) self.__initialize() def __initialize(self): """ Private method to initialize some data. """ self.__loaded = False self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() @classmethod def getFileName(cls): """ Class method to get the file name of the bookmark file. @return name of the bookmark file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "bookmarks.xbel") def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def reload(self): """ Public method used to initiate a reloading of the bookmarks. """ self.__initialize() self.load() self.bookmarksReloaded.emit() def load(self): """ Public method to load the bookmarks. @exception RuntimeError raised to indicate an error loading the bookmarks """ if self.__loaded: return self.__loaded = True bookmarkFile = self.getFileName() if not QFile.exists(bookmarkFile): from . import DefaultBookmarks_rc # __IGNORE_WARNING__ bookmarkFile = QFile(":/DefaultBookmarks.xbel") bookmarkFile.open(QIODevice.ReadOnly) from .XbelReader import XbelReader reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading Bookmarks"), self.tr( """Error when loading bookmarks on line {0},""" """ column {1}:\n {2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) others = [] for index in range( len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if (node.title == self.tr("Toolbar Bookmarks") or node.title == BOOKMARKBAR) and \ self.__toolbar is None: node.title = self.tr(BOOKMARKBAR) self.__toolbar = node if (node.title == self.tr("Menu") or node.title == BOOKMARKMENU) and \ self.__menu is None: node.title = self.tr(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.tr(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.tr(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) self.__convertFromOldBookmarks() def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return from .XbelWriter import XbelWriter writer = XbelWriter() bookmarkFile = self.getFileName() # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): E5MessageBox.warning( None, self.tr("Saving Bookmarks"), self.tr("""Error saving bookmarks to <b>{0}</b>.""") .format(bookmarkFile)) # restore localized titles self.__menu.title = self.tr(BOOKMARKMENU) self.__toolbar.title = self.tr(BOOKMARKBAR) self.bookmarksSaved.emit() def addBookmark(self, parent, node, row=-1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return self.setTimestamp(node, BookmarkNode.TsAdded, QDateTime.currentDateTime()) command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def setNodeChanged(self, node): """ Public method to signal changes of bookmarks other than title, URL or timestamp. @param node reference to the bookmark (BookmarkNode) """ self.__saveTimer.changeOccurred() def setTimestamp(self, node, timestampType, timestamp): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param timestampType type of the timestamp to set (BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited) @param timestamp timestamp to set (QDateTime) """ if not self.__loaded: return assert timestampType in [BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited] if timestampType == BookmarkNode.TsAdded: node.added = timestamp elif timestampType == BookmarkNode.TsModified: node.modified = timestamp elif timestampType == BookmarkNode.TsVisited: node.visited = timestamp self.__saveTimer.changeOccurred() def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: from .BookmarksModel import BookmarksModel self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ from .BookmarksImportDialog import BookmarksImportDialog dlg = BookmarksImportDialog() if dlg.exec_() == QDialog.Accepted: importRootNode = dlg.getImportedBookmarks() if importRootNode is not None: self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( None, self.tr("Export Bookmarks"), "eric6_bookmarks.xbel", self.tr("XBEL bookmarks (*.xbel);;" "XBEL bookmarks (*.xml);;" "HTML Bookmarks (*.html)")) if not fileName: return ext = QFileInfo(fileName).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fileName += ex ext = QFileInfo(fileName).suffix() if ext == "html": from .NsHtmlWriter import NsHtmlWriter writer = NsHtmlWriter() else: from .XbelWriter import XbelWriter writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): E5MessageBox.critical( None, self.tr("Exporting Bookmarks"), self.tr("""Error exporting bookmarks to <b>{0}</b>.""") .format(fileName)) def __convertFromOldBookmarks(self): """ Private method to convert the old bookmarks into the new ones. """ bmNames = Preferences.Prefs.settings.value('Bookmarks/Names') bmFiles = Preferences.Prefs.settings.value('Bookmarks/Files') if bmNames is not None and bmFiles is not None: if len(bmNames) == len(bmFiles): convertedRootNode = BookmarkNode(BookmarkNode.Folder) convertedRootNode.title = self.tr("Converted {0}")\ .format(QDate.currentDate().toString( Qt.SystemLocaleShortDate)) for i in range(len(bmNames)): node = BookmarkNode(BookmarkNode.Bookmark, convertedRootNode) node.title = bmNames[i] url = QUrl(bmFiles[i]) if not url.scheme(): url.setScheme("file") node.url = url.toString() self.addBookmark(self.menu(), convertedRootNode) Preferences.Prefs.settings.remove('Bookmarks') def iconChanged(self, url): """ Public slot to update the icon image for an URL. @param url URL of the icon to update (QUrl or string) """ if isinstance(url, QUrl): url = url.toString() nodes = self.bookmarksForUrl(url) for node in nodes: self.bookmarksModel().entryChanged(node) def bookmarkForUrl(self, url, start=StartRoot): """ Public method to get a bookmark node for a given URL. @param url URL of the bookmark to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return bookmark node for the given url (BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmark(url, startNode) def __searchBookmark(self, url, startNode): """ Private method get a bookmark node for a given URL. @param url URL of the bookmark to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return bookmark node for the given url (BookmarkNode) """ bm = None for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm = self.__searchBookmark(url, node) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm = node if bm is not None: return bm return None def bookmarksForUrl(self, url, start=StartRoot): """ Public method to get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return list of bookmark nodes for the given url (list of BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmarks(url, startNode) def __searchBookmarks(self, url, startNode): """ Private method get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return list of bookmark nodes for the given url (list of BookmarkNode) """ bm = [] for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm.extend(self.__searchBookmarks(url, node)) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm.append(node) return bm
class CookieJar(QNetworkCookieJar): """ Class implementing a QNetworkCookieJar subclass with various accept policies. @signal cookiesChanged() emitted after the cookies have been changed """ JAR_VERSION = 23 AcceptAlways = 0 AcceptNever = 1 AcceptOnlyFromSitesNavigatedTo = 2 KeepUntilExpire = 0 KeepUntilExit = 1 KeepUntilTimeLimit = 2 Allow = 0 Block = 1 AllowForSession = 2 def __init__(self, parent = None): """ Constructor @param parent reference to the parent object (QObject) """ QNetworkCookieJar.__init__(self, parent) self.__loaded = False self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo self.__saveTimer = AutoSaver(self, self.save) self.__cookiesFile = os.path.join(Utilities.getConfigDir(), "browser", "cookies.ini") def saveCookies(self, cookiesList): """ Public method to save the cookies. @param cookiesList list of cookies to be saved @return saved cookies as a byte array (QByteArray) """ data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.writeUInt16(self.JAR_VERSION) stream.writeUInt32(len(cookiesList)) for cookie in cookiesList: stream << cookie.toRawForm() return data def loadCookies(self, cookies): """ Public method to restore the saved cookies. @param cookies byte array containing the saved cookies (QByteArray) @return list of cookies """ if cookies.isEmpty(): return [] cookiesList = [] data = QByteArray(cookies) stream = QDataStream(data, QIODevice.ReadOnly) version = stream.readUInt16() if version != self.JAR_VERSION: return [] noCookies = stream.readUInt32() rawCookie = QByteArray() while not stream.atEnd(): stream >> rawCookie newCookies = QNetworkCookie.parseCookies(rawCookie) for newCookie in newCookies: cookiesList.append(newCookie) return cookiesList def close(self): """ Public slot to close the cookie jar. """ if self.__loaded and self.__keepCookies == self.KeepUntilExit: self.clear() self.__saveTimer.saveIfNeccessary() def clear(self): """ Public method to clear all cookies. """ if not self.__loaded: self.load() self.setAllCookies([]) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) def load(self): """ Public method to load the cookies. """ if self.__loaded: return cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) # load cookies cookies = cookieSettings.value("Cookies") if cookies.isValid(): cookiesList = self.loadCookies(cookies.toByteArray()) else: cookiesList = [] self.setAllCookies(cookiesList) # load exceptions self.__exceptionsBlock = \ cookieSettings.value("Exceptions/block").toStringList() self.__exceptionsAllow = \ cookieSettings.value("Exceptions/allow").toStringList() self.__exceptionsAllowForSession = \ cookieSettings.value("Exceptions/allowForSession").toStringList() self.__exceptionsBlock.sort() self.__exceptionsAllow.sort() self.__exceptionsAllowForSession.sort() self.__acceptCookies = Preferences.getHelp("AcceptCookies") self.__keepCookies = Preferences.getHelp("KeepCookiesUntil") if self.__keepCookies == self.KeepUntilExit: self.setAllCookies([]) self.__filterTrackingCookies = Preferences.getHelp("FilterTrackingCookies") self.__loaded = True self.emit(SIGNAL("cookiesChanged()")) def save(self): """ Public method to save the cookies. """ if not self.__loaded: return self.__purgeOldCookies() cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat) cookiesList = self.allCookies() for index in range(len(cookiesList) -1, -1, -1): if cookiesList[index].isSessionCookie(): del cookiesList[index] cookies = self.saveCookies(cookiesList) cookieSettings.setValue("Cookies", QVariant(cookies)) cookieSettings.setValue("Exceptions/block", QVariant(self.__exceptionsBlock)) cookieSettings.setValue("Exceptions/allow", QVariant(self.__exceptionsAllow)) cookieSettings.setValue("Exceptions/allowForSession", QVariant(self.__exceptionsAllowForSession)) Preferences.setHelp("AcceptCookies", self.__acceptCookies) Preferences.setHelp("KeepCookiesUntil", self.__keepCookies) Preferences.setHelp("FilterTrackingCookies", int(self.__filterTrackingCookies)) def __purgeOldCookies(self): """ Private method to purge old cookies """ cookies = self.allCookies() if len(cookies) == 0: return oldCount = len(cookies) now = QDateTime.currentDateTime() for index in range(len(cookies) - 1, -1, -1): if not cookies[index].isSessionCookie() and \ cookies[index].expirationDate() < now: del cookies[index] if oldCount == len(cookies): return self.setAllCookies(cookies) self.emit(SIGNAL("cookiesChanged()")) def cookiesForUrl(self, url): """ Public method to get the cookies for a URL. @param url URL to get cookies for (QUrl) @return list of cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return [] return QNetworkCookieJar.cookiesForUrl(self, url) def setCookiesFromUrl(self, cookieList, url): """ Public method to set cookies for a URL. @param cookieList list of cookies to set (list of QNetworkCookie) @param url url to set cookies for (QUrl) @return flag indicating cookies were set (boolean) """ if not self.__loaded: self.load() globalSettings = QWebSettings.globalSettings() if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled): return False host = url.host() eBlock = self.__isOnDomainList(self.__exceptionsBlock, host) eAllow = not eBlock and \ self.__isOnDomainList(self.__exceptionsAllow, host) eAllowSession = not eBlock and \ not eAllow and \ self.__isOnDomainList(self.__exceptionsAllowForSession, host) addedCookies = False acceptInitially = self.__acceptCookies != self.AcceptNever if (acceptInitially and not eBlock) or \ (not acceptInitially and (eAllow or eAllowSession)): # url domain == cookie domain soon = QDateTime.currentDateTime() soon = soon.addDays(90) for cookie in cookieList: lst = [] if not (self.__filterTrackingCookies and \ cookie.name().startsWith("__utm")): if eAllowSession: cookie.setExpirationDate(QDateTime()) if self.__keepCookies == self.KeepUntilTimeLimit and \ not cookie.isSessionCookie and \ cookie.expirationDate() > soon: cookie.setExpirationDate(soon) lst.append(cookie) if QNetworkCookieJar.setCookiesFromUrl(self, lst, url): addedCookies = True elif self.__acceptCookies == self.AcceptAlways: # force it in if wanted cookies = self.allCookies() for ocookie in cookies[:]: # does the cookie exist already? if cookie.name() == ocookie.name() and \ cookie.domain() == ocookie.domain() and \ cookie.path() == ocookie.path(): # found a match cookies.remove(ocookie) cookies.append(cookie) self.setAllCookies(cookies) addedCookies = True if addedCookies: self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) return addedCookies def acceptPolicy(self): """ Public method to get the accept policy. @return current accept policy """ if not self.__loaded: self.load() return self.__acceptCookies def setAcceptPolicy(self, policy): """ Public method to set the accept policy. @param policy accept policy to be set """ if not self.__loaded: self.load() if policy > self.AcceptOnlyFromSitesNavigatedTo: return if policy == self.__acceptCookies: return self.__acceptCookies = policy self.__saveTimer.changeOccurred() def keepPolicy(self): """ Private method to get the keep policy. """ if not self.__loaded: self.load() return self.__keepCookies def setKeepPolicy(self, policy): """ Public method to set the keep policy. @param policy keep policy to be set """ if not self.__loaded: self.load() if policy > self.KeepUntilTimeLimit: return if policy == self.__keepCookies: return self.__keepCookies = policy self.__saveTimer.changeOccurred() def blockedCookies(self): """ Public method to return the blocked cookies. @return list of blocked cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsBlock def allowedCookies(self): """ Public method to return the allowed cookies. @return list of allowed cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsAllow def allowForSessionCookies(self): """ Public method to return the allowed session cookies. @return list of allowed session cookies (QStringList) """ if not self.__loaded: self.load() return self.__exceptionsAllowForSession def setBlockedCookies(self, list_): """ Public method to set the list of blocked cookies. @param list_ list of blocked cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsBlock = QStringList(list_) self.__exceptionsBlock.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowedCookies(self, list_): """ Public method to set the list of allowed cookies. @param list_ list of allowed cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsAllow = QStringList(list_) self.__exceptionsAllow.sort() self.__applyRules() self.__saveTimer.changeOccurred() def setAllowForSessionCookies(self, list_): """ Public method to set the list of allowed session cookies. @param list_ list of allowed session cookies (QStringList) """ if not self.__loaded: self.load() self.__exceptionsAllowForSession = QStringList(list_) self.__exceptionsAllowForSession.sort() self.__applyRules() self.__saveTimer.changeOccurred() def filterTrackingCookies(self): """ Public method to get the filter tracking cookies flag. @return filter tracking cookies flag (boolean) """ return self.__filterTrackingCookies def setFilterTrackingCookies(self, filterTrackingCookies): """ Public method to set the filter tracking cookies flag. @param filterTrackingCookies filter tracking cookies flag (boolean) """ self.__filterTrackingCookies = filterTrackingCookies def __isOnDomainList(self, rules, domain): """ Private method to check, if either the rule matches the domain exactly or the domain ends with ".rule". @param rules list of rules (QStringList) @param domain domain name to check (QString) @return flag indicating a match (boolean) """ domain = QString(domain) for rule in rules: if rule.startsWith("."): if domain.endsWith(rule): return True withoutDot = rule.right(rule.size() - 1) if domain == withoutDot: return True else: domainEnding = domain.right(rule.size() + 1) if not domainEnding.isEmpty() and \ domainEnding[0] == "." and \ domain.endsWith(rule): return True if rule == domain: return True return False def __applyRules(self): """ Private method to apply the cookie rules. """ cookiesList = self.allCookies() changed = False for index in range(len(cookiesList) - 1, -1, -1): cookie = cookiesList[index] if self.__isOnDomainList(self.__exceptionsBlock, cookie.domain()): del cookiesList[index] changed = True elif self.__isOnDomainList(self.__exceptionsAllowForSession, cookie.domain()): cookie.setExpirationDate(QDateTime()) changed = True if changed: self.setAllCookies(cookiesList) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()")) def cookies(self): """ Public method to get the cookies of the cookie jar. @return list of all cookies (list of QNetworkCookie) """ if not self.__loaded: self.load() return self.allCookies() def setCookies(self, cookies): """ Public method to set all cookies. @param cookies list of cookies to be set. """ if not self.__loaded: self.load() self.setAllCookies(cookies) self.__saveTimer.changeOccurred() self.emit(SIGNAL("cookiesChanged()"))
class SpeedDial(QObject): """ Class implementing the speed dial. @signal pagesChanged() emitted after the list of pages changed @signal speedDialSaved() emitted after the speed dial data was saved """ pagesChanged = pyqtSignal() speedDialSaved = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(SpeedDial, self).__init__(parent) self.__regenerateScript = True self.__webPages = [] self.__webFrames = [] self.__initialScript = "" self.__thumbnailsDirectory = "" self.__thumbnailers = [] self.__initialize() self.pagesChanged.connect(self.__pagesChanged) self.__saveTimer = AutoSaver(self, self.save) self.pagesChanged.connect(self.__saveTimer.changeOccurred) def addWebFrame(self, frame): """ Public method to add a web frame. @param frame reference to the frame to be added (QWebFrame) """ if frame not in self.__webFrames: self.__webFrames.append(frame) def addPage(self, url, title): """ Public method to add a page for the given data. @param url URL of the page (QUrl) @param title title of the page (string) """ if url.isEmpty(): return from .Page import Page page = Page(url.toString(), title) self.__webPages.append(page) self.pagesChanged.emit() def removePage(self, url): """ Public method to remove a page. @param url URL of the page (QUrl) """ page = self.pageForUrl(url) if not page.url: return self.removeImageForUrl(page.url) self.__webPages.remove(page) self.pagesChanged.emit() def __imageFileName(self, url): """ Private method to generate the image file name for a URL. @param url URL to generate the file name from (string) @return name of the image file (string) """ return os.path.join( self.__thumbnailsDirectory, str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")), QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png") def initialScript(self): """ Public method to get the 'initial' JavaScript script. @return initial JavaScript script (string) """ if self.__regenerateScript: self.__regenerateScript = False self.__initialScript = "" for page in self.__webPages: if page.broken: imgSource = "qrc:icons/brokenPage.png" else: imgSource = self.__imageFileName(page.url) if not os.path.exists(imgSource): self.loadThumbnail(page.url) imgSource = "qrc:icons/loading.gif" if not page.url: imgSource = "" else: imgSource = QUrl.fromLocalFile(imgSource).toString() self.__initialScript += \ "addBox('{0}', '{1}', '{2}');\n".format( page.url, Utilities.html_uencode(page.title), imgSource) return self.__initialScript def getFileName(self): """ Public method to get the file name of the user agents file. @return name of the user agents file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "speedDial.xml") def __initialize(self): """ Private method to initialize the speed dial. """ self.__thumbnailsDirectory = os.path.join(Utilities.getConfigDir(), "browser", "thumbnails") # Create directory if it does not exist yet if not os.path.exists(self.__thumbnailsDirectory): os.makedirs(self.__thumbnailsDirectory) self.__load() def reload(self): """ Public method to reload the speed dial data. """ self.__load() def __load(self): """ Private method to load the speed dial configuration. """ allPages, pagesPerRow, speedDialSize = [], 0, 0 speedDialFile = self.getFileName() if os.path.exists(speedDialFile): from .SpeedDialReader import SpeedDialReader reader = SpeedDialReader() allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile) self.__pagesPerRow = pagesPerRow if pagesPerRow else 4 self.__speedDialSize = speedDialSize if speedDialSize else 231 if allPages: self.__webPages = allPages self.pagesChanged.emit() else: allPages = \ 'url:"http://eric-ide.python-projects.org/"|'\ 'title:"Eric Web Site";'\ 'url:"http://www.riverbankcomputing.com/"|'\ 'title:"PyQt Web Site";'\ 'url:"http://www.qt.io/"|title:"Qt Web Site";'\ 'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\ 'url:"http://www.python.org"|title:"Python Language Website";'\ 'url:"http://www.google.com"|title:"Google";' self.changed(allPages) def save(self): """ Public method to save the speed dial configuration. """ from .SpeedDialWriter import SpeedDialWriter speedDialFile = self.getFileName() writer = SpeedDialWriter() if not writer.write(speedDialFile, self.__webPages, self.__pagesPerRow, self.__speedDialSize): E5MessageBox.critical( None, self.tr("Saving Speed Dial data"), self.tr("""<p>Speed Dial data could not be saved to""" """ <b>{0}</b></p>""").format(speedDialFile)) else: self.speedDialSaved.emit() def close(self): """ Public method to close the user agents manager. """ self.__saveTimer.saveIfNeccessary() def pageForUrl(self, url): """ Public method to get the page for the given URL. @param url URL to be searched for (QUrl) @return page for the URL (Page) """ urlString = url.toString() for page in self.__webPages: if page.url == urlString: return page from .Page import Page return Page() def urlForShortcut(self, key): """ Public method to get the URL for the given shortcut key. @param key shortcut key (integer) @return URL for the key (QUrl) """ if key < 0 or len(self.__webPages) <= key: return QUrl() return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8")) @pyqtSlot(str) def changed(self, allPages): """ Public slot to react on changed pages. @param allPages string giving all pages (string) """ if not allPages: return entries = allPages.split('";') self.__webPages = [] from .Page import Page for entry in entries: if not entry: continue tmp = entry.split('"|') if len(tmp) == 2: broken = False elif len(tmp) == 3: broken = "brokenPage" in tmp[2][5:] else: continue page = Page(tmp[0][5:], tmp[1][7:], broken) self.__webPages.append(page) self.pagesChanged.emit() @pyqtSlot(str) @pyqtSlot(str, bool) def loadThumbnail(self, url, loadTitle=False): """ Public slot to load a thumbnail of the given URL. @param url URL of the thumbnail (string) @param loadTitle flag indicating to get the title for the thumbnail from the site (boolean) """ if not url: return from .PageThumbnailer import PageThumbnailer thumbnailer = PageThumbnailer(self) thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8"))) thumbnailer.setLoadTitle(loadTitle) thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated) self.__thumbnailers.append(thumbnailer) thumbnailer.start() @pyqtSlot(str) def removeImageForUrl(self, url): """ Public slot to remove the image for a URL. @param url URL to remove the image for (string) """ fileName = self.__imageFileName(url) if os.path.exists(fileName): os.remove(fileName) @pyqtSlot(str, result=str) def urlFromUserInput(self, url): """ Public slot to get the URL from user input. @param url URL entered by the user (string) @return sanitized URL (string) """ return QUrl.fromUserInput(url).toString() @pyqtSlot(str, result=str) def unescapeTitle(self, title): """ Public slot to unescape the titel string. @param title escaped title (string) @return un-escaped title (string) """ return Utilities.html_udecode(title) @pyqtSlot(int) def setPagesInRow(self, count): """ Public slot to set the number of pages per row. @param count number of pages per row (integer) """ self.__pagesPerRow = count self.__saveTimer.changeOccurred() def pagesInRow(self): """ Public method to get the number of dials per row. @return number of dials per row (integer) """ return self.__pagesPerRow @pyqtSlot(int) def setSdSize(self, size): """ Public slot to set the size of the speed dial. @param size size of the speed dial (integer) """ self.__speedDialSize = size self.__saveTimer.changeOccurred() def sdSize(self): """ Public method to get the speed dial size. @return speed dial size (integer) """ return self.__speedDialSize def __thumbnailCreated(self, image): """ Private slot to handle the creation of a thumbnail image. @param image thumbnail image (QPixmap) """ from .PageThumbnailer import PageThumbnailer thumbnailer = self.sender() if not isinstance(thumbnailer, PageThumbnailer) or \ thumbnailer not in self.__thumbnailers: return loadTitle = thumbnailer.loadTitle() title = thumbnailer.title() url = thumbnailer.url().toString() fileName = self.__imageFileName(url) if image.isNull(): fileName = "qrc:icons/brokenPage.png" title = self.tr("Unable to load") loadTitle = True page = self.pageForUrl(thumbnailer.url()) page.broken = True else: if not image.save(fileName): qWarning("SpeedDial.__thumbnailCreated: Cannot save thumbnail" " to {0}".format(fileName)) fileName = QUrl.fromLocalFile(fileName).toString() self.__regenerateScript = True for frame in self.__cleanFrames(): frame.evaluateJavaScript("setImageToUrl('{0}', '{1}');".format( url, fileName)) if loadTitle: frame.evaluateJavaScript("setTitleToUrl('{0}', '{1}');".format( url, Utilities.html_uencode(title))) thumbnailer.deleteLater() self.__thumbnailers.remove(thumbnailer) def __cleanFrames(self): """ Private method to clean all frames. @return list of speed dial frames (list of QWebFrame) """ frames = [] for frame in self.__webFrames[:]: if frame.url().toString() == "eric:speeddial": frames.append(frame) else: self.__webFrames.remove(frame) return frames def __pagesChanged(self): """ Private slot to react on a change of the pages configuration. """ # update all speed dial pages self.__regenerateScript = True for frame in self.__cleanFrames(): frame.page().triggerAction(QWebPage.Reload)
class BookmarksManager(QObject): """ Class implementing the bookmarks manager. @signal entryAdded(BookmarkNode) emitted after a bookmark node has been added @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a bookmark node has been removed @signal entryChanged(BookmarkNode) emitted after a bookmark node has been changed @signal bookmarksSaved() emitted after the bookmarks were saved @signal bookmarksReloaded() emitted after the bookmarks were reloaded """ entryAdded = pyqtSignal(BookmarkNode) entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode) entryChanged = pyqtSignal(BookmarkNode) bookmarksSaved = pyqtSignal() bookmarksReloaded = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(BookmarksManager, self).__init__(parent) self.__saveTimer = AutoSaver(self, self.save) self.entryAdded.connect(self.__saveTimer.changeOccurred) self.entryRemoved.connect(self.__saveTimer.changeOccurred) self.entryChanged.connect(self.__saveTimer.changeOccurred) self.__initialize() def __initialize(self): """ Private method to initialize some data. """ self.__loaded = False self.__bookmarkRootNode = None self.__toolbar = None self.__menu = None self.__bookmarksModel = None self.__commands = QUndoStack() @classmethod def getFileName(cls): """ Class method to get the file name of the bookmark file. @return name of the bookmark file (string) """ return os.path.join(Utilities.getConfigDir(), "web_browser", "bookmarks.xbel") def close(self): """ Public method to close the bookmark manager. """ self.__saveTimer.saveIfNeccessary() def undoRedoStack(self): """ Public method to get a reference to the undo stack. @return reference to the undo stack (QUndoStack) """ return self.__commands def changeExpanded(self): """ Public method to handle a change of the expanded state. """ self.__saveTimer.changeOccurred() def reload(self): """ Public method used to initiate a reloading of the bookmarks. """ self.__initialize() self.load() self.bookmarksReloaded.emit() def load(self): """ Public method to load the bookmarks. @exception RuntimeError raised to indicate an error loading the bookmarks """ if self.__loaded: return self.__loaded = True bookmarkFile = self.getFileName() if not QFile.exists(bookmarkFile): from . import DefaultBookmarks_rc # __IGNORE_WARNING__ bookmarkFile = QFile(":/DefaultBookmarks.xbel") bookmarkFile.open(QIODevice.ReadOnly) from .XbelReader import XbelReader reader = XbelReader() self.__bookmarkRootNode = reader.read(bookmarkFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading Bookmarks"), self.tr( """Error when loading bookmarks on line {0},""" """ column {1}:\n {2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) others = [] for index in range( len(self.__bookmarkRootNode.children()) - 1, -1, -1): node = self.__bookmarkRootNode.children()[index] if node.type() == BookmarkNode.Folder: if ( (node.title == self.tr("Toolbar Bookmarks") or node.title == BOOKMARKBAR) and self.__toolbar is None ): node.title = self.tr(BOOKMARKBAR) self.__toolbar = node if ( (node.title == self.tr("Menu") or node.title == BOOKMARKMENU) and self.__menu is None ): node.title = self.tr(BOOKMARKMENU) self.__menu = node else: others.append(node) self.__bookmarkRootNode.remove(node) if len(self.__bookmarkRootNode.children()) > 0: raise RuntimeError("Error loading bookmarks.") if self.__toolbar is None: self.__toolbar = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__toolbar.title = self.tr(BOOKMARKBAR) else: self.__bookmarkRootNode.add(self.__toolbar) if self.__menu is None: self.__menu = BookmarkNode(BookmarkNode.Folder, self.__bookmarkRootNode) self.__menu.title = self.tr(BOOKMARKMENU) else: self.__bookmarkRootNode.add(self.__menu) for node in others: self.__menu.add(node) def save(self): """ Public method to save the bookmarks. """ if not self.__loaded: return from .XbelWriter import XbelWriter writer = XbelWriter() bookmarkFile = self.getFileName() # save root folder titles in English (i.e. not localized) self.__menu.title = BOOKMARKMENU self.__toolbar.title = BOOKMARKBAR if not writer.write(bookmarkFile, self.__bookmarkRootNode): E5MessageBox.warning( None, self.tr("Saving Bookmarks"), self.tr("""Error saving bookmarks to <b>{0}</b>.""") .format(bookmarkFile)) # restore localized titles self.__menu.title = self.tr(BOOKMARKMENU) self.__toolbar.title = self.tr(BOOKMARKBAR) self.bookmarksSaved.emit() def addBookmark(self, parent, node, row=-1): """ Public method to add a bookmark. @param parent reference to the node to add to (BookmarkNode) @param node reference to the node to add (BookmarkNode) @param row row number (integer) """ if not self.__loaded: return self.setTimestamp(node, BookmarkNode.TsAdded, QDateTime.currentDateTime()) command = InsertBookmarksCommand(self, parent, node, row) self.__commands.push(command) def removeBookmark(self, node): """ Public method to remove a bookmark. @param node reference to the node to be removed (BookmarkNode) """ if not self.__loaded: return parent = node.parent() row = parent.children().index(node) command = RemoveBookmarksCommand(self, parent, row) self.__commands.push(command) def setTitle(self, node, newTitle): """ Public method to set the title of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newTitle title to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newTitle, True) self.__commands.push(command) def setUrl(self, node, newUrl): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param newUrl URL to be set (string) """ if not self.__loaded: return command = ChangeBookmarkCommand(self, node, newUrl, False) self.__commands.push(command) def setNodeChanged(self, node): """ Public method to signal changes of bookmarks other than title, URL or timestamp. @param node reference to the bookmark (BookmarkNode) """ self.__saveTimer.changeOccurred() def setTimestamp(self, node, timestampType, timestamp): """ Public method to set the URL of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param timestampType type of the timestamp to set (BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited) @param timestamp timestamp to set (QDateTime) """ if not self.__loaded: return assert timestampType in [BookmarkNode.TsAdded, BookmarkNode.TsModified, BookmarkNode.TsVisited] if timestampType == BookmarkNode.TsAdded: node.added = timestamp elif timestampType == BookmarkNode.TsModified: node.modified = timestamp elif timestampType == BookmarkNode.TsVisited: node.visited = timestamp self.__saveTimer.changeOccurred() def incVisitCount(self, node): """ Public method to increment the visit count of a bookmark. @param node reference to the node to be changed (BookmarkNode) """ if not self.__loaded: return if node: node.visitCount += 1 self.__saveTimer.changeOccurred() def setVisitCount(self, node, count): """ Public method to set the visit count of a bookmark. @param node reference to the node to be changed (BookmarkNode) @param count visit count to be set (int or str) """ try: node.visitCount = int(count) self.__saveTimer.changeOccurred() except ValueError: # ignore invalid values pass def bookmarks(self): """ Public method to get a reference to the root bookmark node. @return reference to the root bookmark node (BookmarkNode) """ if not self.__loaded: self.load() return self.__bookmarkRootNode def menu(self): """ Public method to get a reference to the bookmarks menu node. @return reference to the bookmarks menu node (BookmarkNode) """ if not self.__loaded: self.load() return self.__menu def toolbar(self): """ Public method to get a reference to the bookmarks toolbar node. @return reference to the bookmarks toolbar node (BookmarkNode) """ if not self.__loaded: self.load() return self.__toolbar def bookmarksModel(self): """ Public method to get a reference to the bookmarks model. @return reference to the bookmarks model (BookmarksModel) """ if self.__bookmarksModel is None: from .BookmarksModel import BookmarksModel self.__bookmarksModel = BookmarksModel(self, self) return self.__bookmarksModel def importBookmarks(self): """ Public method to import bookmarks. """ from .BookmarksImportDialog import BookmarksImportDialog dlg = BookmarksImportDialog() if dlg.exec_() == QDialog.Accepted: importRootNode = dlg.getImportedBookmarks() if importRootNode is not None: self.addBookmark(self.menu(), importRootNode) def exportBookmarks(self): """ Public method to export the bookmarks. """ fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( None, self.tr("Export Bookmarks"), "eric6_bookmarks.xbel", self.tr("XBEL bookmarks (*.xbel);;" "XBEL bookmarks (*.xml);;" "HTML Bookmarks (*.html)")) if not fileName: return ext = QFileInfo(fileName).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fileName += ex ext = QFileInfo(fileName).suffix() if ext == "html": from .NsHtmlWriter import NsHtmlWriter writer = NsHtmlWriter() else: from .XbelWriter import XbelWriter writer = XbelWriter() if not writer.write(fileName, self.__bookmarkRootNode): E5MessageBox.critical( None, self.tr("Exporting Bookmarks"), self.tr("""Error exporting bookmarks to <b>{0}</b>.""") .format(fileName)) def faviconChanged(self, url): """ Public slot to update the icon image for an URL. @param url URL of the icon to update (QUrl or string) """ if isinstance(url, QUrl): url = url.toString() nodes = self.bookmarksForUrl(url) for node in nodes: self.bookmarksModel().entryChanged(node) def bookmarkForUrl(self, url, start=StartRoot): """ Public method to get a bookmark node for a given URL. @param url URL of the bookmark to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return bookmark node for the given url (BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return None if isinstance(url, QUrl): url = url.toString() return self.__searchBookmark(url, startNode) def __searchBookmark(self, url, startNode): """ Private method get a bookmark node for a given URL. @param url URL of the bookmark to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return bookmark node for the given url (BookmarkNode) """ bm = None for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm = self.__searchBookmark(url, node) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm = node if bm is not None: return bm return None def bookmarksForUrl(self, url, start=StartRoot): """ Public method to get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (QUrl or string) @keyparam start indicator for the start of the search (StartRoot, StartMenu, StartToolBar) @return list of bookmark nodes for the given url (list of BookmarkNode) """ if start == StartMenu: startNode = self.__menu elif start == StartToolBar: startNode = self.__toolbar else: startNode = self.__bookmarkRootNode if startNode is None: return [] if isinstance(url, QUrl): url = url.toString() return self.__searchBookmarks(url, startNode) def __searchBookmarks(self, url, startNode): """ Private method get a list of bookmark nodes for a given URL. @param url URL of the bookmarks to search for (string) @param startNode reference to the node to start searching (BookmarkNode) @return list of bookmark nodes for the given url (list of BookmarkNode) """ bm = [] for node in startNode.children(): if node.type() == BookmarkNode.Folder: bm.extend(self.__searchBookmarks(url, node)) elif node.type() == BookmarkNode.Bookmark: if node.url == url: bm.append(node) return bm
class WebIconProvider(QObject): """ Class implementing a web site icon storage. @signal changed() emitted to indicate a change of the icons database """ changed = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(WebIconProvider, self).__init__(parent) self.__encoding = "iso-8859-1" self.__iconsFileName = "web_site_icons.json" self.__iconDatabasePath = "" # saving of icons disabled self.__iconsDB = {} self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def setIconDatabasePath(self, path): """ Public method to set the path for the web site icons store. @param path path to store the icons file to @type str """ if path != self.__iconDatabasePath: self.close() self.__iconDatabasePath = path def iconDatabasePath(self): """ Public method o get the path for the web site icons store. @return path to store the icons file to @rtype str """ return self.__iconDatabasePath def close(self): """ Public method to close the web icon provider. """ self.__saveTimer.saveIfNeccessary() self.__loaded = False self.__iconsDB = {} def load(self): """ Public method to load the bookmarks. """ if self.__loaded: return if self.__iconDatabasePath: filename = os.path.join(self.__iconDatabasePath, self.__iconsFileName) try: f = open(filename, "r") db = json.load(f) f.close() except (IOError, OSError): # ignore silentyl db = {} self.__iconsDB = {} for url, data in db.items(): self.__iconsDB[url] = QIcon( QPixmap.fromImage( QImage.fromData( QByteArray(data.encode(self.__encoding))))) self.__loaded = True def save(self): """ Public method to save the zoom values. """ if not self.__loaded: return from WebBrowser.WebBrowserWindow import WebBrowserWindow if not WebBrowserWindow.isPrivate() and bool(self.__iconDatabasePath): db = {} for url, icon in self.__iconsDB.items(): ba = QByteArray() buffer = QBuffer(ba) buffer.open(QIODevice.WriteOnly) icon.pixmap(32).toImage().save(buffer, "PNG") db[url] = bytes(buffer.data()).decode(self.__encoding) filename = os.path.join(self.__iconDatabasePath, self.__iconsFileName) try: f = open(filename, "w") json.dump(db, f) f.close() except (IOError, OSError): # ignore silentyl pass def saveIcon(self, view): """ Public method to save a web site icon. @param view reference to the view object @type WebBrowserView """ scheme = view.url().scheme() if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]: return self.load() if view.mainWindow().isPrivate(): return urlStr = self.__urlToString(view.url()) self.__iconsDB[urlStr] = view.icon() self.changed.emit() def __urlToString(self, url): """ Private method to convert an URL to a string. @param url URL to be converted @type QUrl @return string representation of the URL @rtype str """ return url.toString(QUrl.PrettyDecoded | QUrl.RemoveUserInfo | QUrl.RemoveFragment) def iconForUrl(self, url): """ Public method to get an icon for an URL. @param url URL to get icon for @type QUrl @return icon for the URL @rtype QIcon """ scheme = url.scheme() if scheme in ["eric", "about"]: return UI.PixmapCache.getIcon("ericWeb.png") elif scheme == "qthelp": return UI.PixmapCache.getIcon("qthelp.png") elif scheme == "file": return UI.PixmapCache.getIcon("fileMisc.png") elif scheme == "abp": return UI.PixmapCache.getIcon("adBlockPlus.png") elif scheme == "ftp": return UI.PixmapCache.getIcon("network-server.png") self.load() urlStr = self.__urlToString(url) for iconUrlStr in self.__iconsDB: if iconUrlStr.startswith(urlStr): return self.__iconsDB[iconUrlStr] # try replacing http scheme with https scheme url = QUrl(url) if url.scheme() == "http": url.setScheme("https") urlStr = self.__urlToString(url) for iconUrlStr in self.__iconsDB: if iconUrlStr.startswith(urlStr): return self.__iconsDB[iconUrlStr] if scheme == "https": return UI.PixmapCache.getIcon("securityHigh32.png") else: return UI.PixmapCache.getIcon("defaultIcon.png") def clear(self): """ Public method to clear the icons cache. """ self.load() self.__iconsDB = {} self.changed.emit() self.__saveTimer.saveIfNeccessary() def showWebIconDialog(self): """ Public method to show a dialog to manage the Favicons. """ self.load() from .WebIconDialog import WebIconDialog dlg = WebIconDialog(self.__iconsDB) if dlg.exec_() == QDialog.Accepted: changed = False urls = dlg.getUrls() for url in list(self.__iconsDB.keys())[:]: if url not in urls: del self.__iconsDB[url] changed = True if changed: self.changed.emit()
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged() emitted to indicate a change of the current search engine """ changed = pyqtSignal() currentEngineChanged = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e5App() super(OpenSearchManager, self).__init__(parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = "" self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (string) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string) """ if name not in self.__engines: return self.__current = name self.currentEngineChanged.emit() self.changed.emit() def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if not self.__current or self.__current not in self.__engines: return None return self.__engines[self.__current] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string) @return reference to the engine (OpenSearchEngine) """ if name not in self.__engines: return None return self.__engines[name] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string) @return flag indicating an existing engine (boolean) """ return name in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (list of strings) """ return sorted(self.__engines.keys()) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ from .OpenSearchEngine import OpenSearchEngine if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return from Helpviewer.HelpWindow import HelpWindow reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url)) reply.finished.connect(self.__engineFromUrlAvailable) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if engine.name() in self.__engines: return False engine.setParent(self) self.__engines[engine.name()] = engine self.changed.emit() return True def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string) """ if len(self.__engines) <= 1: return if name not in self.__engines: return engine = self.__engines[name] for keyword in [k for k in self.__keywords if self.__keywords[k] == engine]: del self.__keywords[keyword] del self.__engines[name] file_ = QDir(self.enginesDirectory()).filePath( self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(list(self.__engines.keys())[0]) self.changed.emit() def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string) @return valid engine file name (string) """ fileName = "" # strip special characters for c in engineName: if c.isspace(): fileName += '_' continue if c.isalnum(): fileName += c fileName += ".xml" return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string) """ dir = QDir() if not dir.mkpath(dirName): return dir.setPath(dirName) from .OpenSearchWriter import OpenSearchWriter writer = OpenSearchWriter() for engine in list(self.__engines.values()): name = self.generateEngineFileName(engine.name()) fileName = dir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setHelp("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setHelp("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False dir = QDir(dirName) for name in dir.entryList(["*.xml"]): fileName = dir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getHelp("WebSearchEngine") keywords = Preferences.getHelp("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[keyword] = self.engine(engineName) if self.__current not in self.__engines and \ len(self.__engines) > 0: self.__current = list(self.__engines.keys())[0] self.__loading = False self.currentEngineChanged.emit() def restoreDefaults(self): """ Public method to restore the default search engines. """ from .OpenSearchReader import OpenSearchReader from .DefaultSearchEngines import DefaultSearchEngines_rc # __IGNORE_WARNING__ defaultEngineFiles = ["YouTube.xml", "Amazoncom.xml", "Bing.xml", "DeEn_Beolingus.xml", "Facebook.xml", "Google_Im_Feeling_Lucky.xml", "Google.xml", "LEO_DeuEng.xml", "LinuxMagazin.xml", "Reddit.xml", "Wikia_en.xml", "Wikia.xml", "Wikipedia.xml", "Wiktionary.xml", "Yahoo.xml"] # Keep this list in sync with the contents of the resource file. reader = OpenSearchReader() for engineFileName in defaultEngineFiles: engineFile = QFile(":/" + engineFileName) if not engineFile.open(QIODevice.ReadOnly): continue engine = reader.read(engineFile) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (string) """ return os.path.join( Utilities.getConfigDir(), "browser", "searchengines") def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = E5MessageBox.yesNo( None, "", self.tr( """<p>Do you want to add the following engine to your""" """ list of search engines?<br/><br/>Name: {0}<br/>""" """Searches on: {1}</p>""").format(engine.name(), host)) return res def __engineFromUrlAvailable(self): """ Private slot to add a search engine from the net. """ reply = self.sender() if reply is None: return if reply.error() != QNetworkReply.NoError: reply.close() if reply in self.__replies: self.__replies.remove(reply) return from .OpenSearchReader import OpenSearchReader reader = OpenSearchReader() engine = reader.read(reply) reply.close() if reply in self.__replies: self.__replies.remove(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string) @return search URL (QUrl) """ try: keyword, term = keywordSearch.split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string) @return reference to the search engine object (OpenSearchEngine) """ if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.changed.emit() def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (list of strings) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if not keyword: continue self.__keywords[keyword] = engine self.changed.emit() def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.changed.emit()
class PasswordManager(QObject): """ Class implementing the password manager. @signal changed() emitted to indicate a change @signal passwordsSaved() emitted after the passwords were saved """ changed = pyqtSignal() passwordsSaved = pyqtSignal() SEPARATOR = "====================" FORMS = "=====FORMS=====" NEVER = "=====NEVER=====" def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ super(PasswordManager, self).__init__(parent) self.__logins = {} self.__loginForms = {} self.__never = [] self.__loaded = False self.__saveTimer = AutoSaver(self, self.save) self.changed.connect(self.__saveTimer.changeOccurred) def clear(self): """ Public slot to clear the saved passwords. """ if not self.__loaded: self.__load() self.__logins = {} self.__loginForms = {} self.__never = [] self.__saveTimer.changeOccurred() self.__saveTimer.saveIfNeccessary() self.changed.emit() def getLogin(self, url, realm): """ Public method to get the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) try: return self.__logins[key][0], Utilities.crypto.pwConvert( self.__logins[key][1], encode=False) except KeyError: return "", "" def setLogin(self, url, realm, username, password): """ Public method to set the login credentials. @param url URL to set the credentials for (QUrl) @param realm realm to set the credentials for (string) @param username username for the login (string) @param password password for the login (string) """ if not self.__loaded: self.__load() key = self.__createKey(url, realm) self.__logins[key] = ( username, Utilities.crypto.pwConvert(password, encode=True) ) self.changed.emit() def __createKey(self, url, realm): """ Private method to create the key string for the login credentials. @param url URL to get the credentials for (QUrl) @param realm realm to get the credentials for (string) @return key string (string) """ authority = url.authority() if authority.startswith("@"): authority = authority[1:] if realm: key = "{0}://{1} ({2})".format( url.scheme(), authority, realm) else: key = "{0}://{1}".format(url.scheme(), authority) return key def getFileName(self): """ Public method to get the file name of the passwords file. @return name of the passwords file (string) """ return os.path.join(Utilities.getConfigDir(), "browser", "logins.xml") def save(self): """ Public slot to save the login entries to disk. """ if not self.__loaded: return from .PasswordWriter import PasswordWriter loginFile = self.getFileName() writer = PasswordWriter() if not writer.write( loginFile, self.__logins, self.__loginForms, self.__never): E5MessageBox.critical( None, self.tr("Saving login data"), self.tr( """<p>Login data could not be saved to <b>{0}</b></p>""" ).format(loginFile)) else: self.passwordsSaved.emit() def __load(self): """ Private method to load the saved login credentials. """ loginFile = self.getFileName() if not os.path.exists(loginFile): self.__loadNonXml(os.path.splitext(loginFile)[0]) else: from .PasswordReader import PasswordReader reader = PasswordReader() self.__logins, self.__loginForms, self.__never = \ reader.read(loginFile) if reader.error() != QXmlStreamReader.NoError: E5MessageBox.warning( None, self.tr("Loading login data"), self.tr("""Error when loading login data on""" """ line {0}, column {1}:\n{2}""") .format(reader.lineNumber(), reader.columnNumber(), reader.errorString())) self.__loaded = True def __loadNonXml(self, loginFile): """ Private method to load non-XML password files. This method is to convert from the old, non-XML format to the new XML based format. @param loginFile name of the non-XML password file (string) """ if os.path.exists(loginFile): try: f = open(loginFile, "r", encoding="utf-8") lines = f.read() f.close() except IOError as err: E5MessageBox.critical( None, self.tr("Loading login data"), self.tr("""<p>Login data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: {1}</p>""") .format(loginFile, str(err))) return data = [] section = 0 # 0 = login data, 1 = forms data, 2 = never store info for line in lines.splitlines(): if line == self.FORMS: section = 1 continue elif line == self.NEVER: section = 2 continue if section == 0: if line != self.SEPARATOR: data.append(line) else: if len(data) != 3: E5MessageBox.critical( None, self.tr("Loading login data"), self.tr( """<p>Login data could not be loaded """ """from <b>{0}</b></p>""" """<p>Reason: Wrong input format</p>""") .format(loginFile)) return self.__logins[data[0]] = (data[1], data[2]) data = [] elif section == 1: if line != self.SEPARATOR: data.append(line) else: from .LoginForm import LoginForm key = data[0] form = LoginForm() form.url = QUrl(data[1]) form.name = data[2] form.hasAPassword = data[3] == "True" for element in data[4:]: name, value = element.split(" = ", 1) form.elements.append((name, value)) self.__loginForms[key] = form data = [] elif section == 2: self.__never.append(line) os.remove(loginFile) self.__loaded = True # this does the conversion self.save() def reload(self): """ Public method to reload the login data. """ if not self.__loaded: return self.__load() def close(self): """ Public method to close the passwords manager. """ self.__saveTimer.saveIfNeccessary() def removePassword(self, site): """ Public method to remove a password entry. @param site web site name (string) """ if site in self.__logins: del self.__logins[site] if site in self.__loginForms: del self.__loginForms[site] self.changed.emit() def allSiteNames(self): """ Public method to get a list of all site names. @return sorted list of all site names (list of strings) """ if not self.__loaded: self.__load() return sorted(self.__logins.keys()) def sitesCount(self): """ Public method to get the number of available sites. @return number of sites (integer) """ if not self.__loaded: self.__load() return len(self.__logins) def siteInfo(self, site): """ Public method to get a reference to the named site. @param site web site name (string) @return tuple containing the user name (string) and password (string) """ if not self.__loaded: self.__load() if site not in self.__logins: return None return self.__logins[site][0], Utilities.crypto.pwConvert( self.__logins[site][1], encode=False) def post(self, request, data): """ Public method to check, if the data to be sent contains login data. @param request reference to the network request (QNetworkRequest) @param data data to be sent (QByteArray) """ # shall passwords be saved? if not Preferences.getUser("SavePasswords"): return # observe privacy if QWebSettings.globalSettings().testAttribute( QWebSettings.PrivateBrowsingEnabled): return if not self.__loaded: self.__load() # determine the url refererHeader = request.rawHeader(b"Referer") if refererHeader.isEmpty(): return url = QUrl.fromEncoded(refererHeader) url = self.__stripUrl(url) # check that url isn't in __never if url.toString() in self.__never: return # check the request type navType = request.attribute(QNetworkRequest.User + 101) if navType is None: return if navType != QWebPage.NavigationTypeFormSubmitted: return # determine the QWebPage webPage = request.attribute(QNetworkRequest.User + 100) if webPage is None: return # determine the requests content type contentTypeHeader = request.rawHeader(b"Content-Type") if contentTypeHeader.isEmpty(): return multipart = contentTypeHeader.startsWith(b"multipart/form-data") if multipart: boundary = contentTypeHeader.split(" ")[1].split("=")[1] else: boundary = None # find the matching form on the web page form = self.__findForm(webPage, data, boundary=boundary) if not form.isValid(): return form.url = QUrl(url) # check, if the form has a password if not form.hasAPassword: return # prompt, if the form has never be seen key = self.__createKey(url, "") if key not in self.__loginForms: mb = E5MessageBox.E5MessageBox( E5MessageBox.Question, self.tr("Save password"), self.tr( """<b>Would you like to save this password?</b><br/>""" """To review passwords you have saved and remove them, """ """use the password management dialog of the Settings""" """ menu."""), modal=True) neverButton = mb.addButton( self.tr("Never for this site"), E5MessageBox.DestructiveRole) noButton = mb.addButton( self.tr("Not now"), E5MessageBox.RejectRole) mb.addButton(E5MessageBox.Yes) mb.exec_() if mb.clickedButton() == neverButton: self.__never.append(url.toString()) return elif mb.clickedButton() == noButton: return # extract user name and password user = "" password = "" for index in range(len(form.elements)): element = form.elements[index] type_ = form.elementTypes[element[0]] if user == "" and \ type_ == "text": user = element[1] elif password == "" and \ type_ == "password": password = element[1] form.elements[index] = (element[0], "--PASSWORD--") if user and password: self.__logins[key] = \ (user, Utilities.crypto.pwConvert(password, encode=True)) self.__loginForms[key] = form self.changed.emit() def __stripUrl(self, url): """ Private method to strip off all unneeded parts of a URL. @param url URL to be stripped (QUrl) @return stripped URL (QUrl) """ cleanUrl = QUrl(url) if qVersion() >= "5.0.0": cleanUrl.setQuery("") else: cleanUrl.setQueryItems([]) cleanUrl.setUserInfo("") authority = cleanUrl.authority() if authority.startswith("@"): authority = authority[1:] cleanUrl = QUrl("{0}://{1}{2}".format( cleanUrl.scheme(), authority, cleanUrl.path())) cleanUrl.setFragment("") return cleanUrl def __findForm(self, webPage, data, boundary=None): """ Private method to find the form used for logging in. @param webPage reference to the web page (QWebPage) @param data data to be sent (QByteArray) @keyparam boundary boundary string (QByteArray) for multipart encoded data, None for urlencoded data @return parsed form (LoginForm) """ from .LoginForm import LoginForm form = LoginForm() if boundary is not None: args = self.__extractMultipartQueryItems(data, boundary) else: if qVersion() >= "5.0.0": from PyQt5.QtCore import QUrlQuery argsUrl = QUrl.fromEncoded( QByteArray(b"foo://bar.com/?" + QUrl.fromPercentEncoding( data.replace(b"+", b"%20")).encode("utf-8"))) encodedArgs = QUrlQuery(argsUrl).queryItems() else: argsUrl = QUrl.fromEncoded( QByteArray(b"foo://bar.com/?" + data.replace(b"+", b"%20")) ) encodedArgs = argsUrl.queryItems() args = set() for arg in encodedArgs: key = arg[0] value = arg[1] args.add((key, value)) # extract the forms from Helpviewer.JavaScriptResources import parseForms_js lst = webPage.mainFrame().evaluateJavaScript(parseForms_js) for map in lst: formHasPasswords = False formName = map["name"] formIndex = map["index"] if isinstance(formIndex, float) and formIndex.is_integer(): formIndex = int(formIndex) elements = map["elements"] formElements = set() formElementTypes = {} deadElements = set() for elementMap in elements: try: name = elementMap["name"] value = elementMap["value"] type_ = elementMap["type"] except KeyError: continue if type_ == "password": formHasPasswords = True t = (name, value) try: if elementMap["autocomplete"] == "off": deadElements.add(t) except KeyError: pass if name: formElements.add(t) formElementTypes[name] = type_ if formElements.intersection(args) == args: form.hasAPassword = formHasPasswords if not formName: form.name = formIndex else: form.name = formName args.difference_update(deadElements) for elt in deadElements: if elt[0] in formElementTypes: del formElementTypes[elt[0]] form.elements = list(args) form.elementTypes = formElementTypes break return form def __extractMultipartQueryItems(self, data, boundary): """ Private method to extract the query items for a post operation. @param data data to be sent (QByteArray) @param boundary boundary string (QByteArray) @return set of name, value pairs (set of tuple of string, string) """ args = set() dataStr = bytes(data).decode() boundaryStr = bytes(boundary).decode() parts = dataStr.split(boundaryStr + "\r\n") for part in parts: if part.startswith("Content-Disposition"): lines = part.split("\r\n") name = lines[0].split("=")[1][1:-1] value = lines[2] args.add((name, value)) return args def fill(self, page): """ Public slot to fill login forms with saved data. @param page reference to the web page (QWebPage) """ if page is None or page.mainFrame() is None: return if not self.__loaded: self.__load() url = page.mainFrame().url() url = self.__stripUrl(url) key = self.__createKey(url, "") if key not in self.__loginForms or \ key not in self.__logins: return form = self.__loginForms[key] if form.url != url: return if form.name == "": formName = "0" else: try: formName = "{0:d}".format(int(form.name)) except ValueError: formName = '"{0}"'.format(form.name) for element in form.elements: name = element[0] value = element[1] disabled = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].disabled'.format( formName, name)) if disabled: continue readOnly = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].readOnly'.format( formName, name)) if readOnly: continue type_ = page.mainFrame().evaluateJavaScript( 'document.forms[{0}].elements["{1}"].type'.format( formName, name)) if type_ == "" or \ type_ in ["hidden", "reset", "submit"]: continue if type_ == "password": value = Utilities.crypto.pwConvert( self.__logins[key][1], encode=False) setType = type_ == "checkbox" and "checked" or "value" value = value.replace("\\", "\\\\") value = value.replace('"', '\\"') javascript = \ 'document.forms[{0}].elements["{1}"].{2}="{3}";'.format( formName, name, setType, value) page.mainFrame().evaluateJavaScript(javascript) def masterPasswordChanged(self, oldPassword, newPassword): """ Public slot to handle the change of the master password. @param oldPassword current master password (string) @param newPassword new master password (string) """ if not self.__loaded: self.__load() progress = E5ProgressDialog( self.tr("Re-encoding saved passwords..."), None, 0, len(self.__logins), self.tr("%v/%m Passwords"), QApplication.activeModalWidget()) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Passwords")) count = 0 for key in self.__logins: progress.setValue(count) QCoreApplication.processEvents() username, hash = self.__logins[key] hash = Utilities.crypto.pwRecode(hash, oldPassword, newPassword) self.__logins[key] = (username, hash) count += 1 progress.setValue(len(self.__logins)) QCoreApplication.processEvents() self.changed.emit()
class OpenSearchManager(QObject): """ Class implementing a manager for open search engines. @signal changed() emitted to indicate a change @signal currentEngineChanged emitted to indicate a change of the current search engine """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent object (QObject) """ if parent is None: parent = e4App() QObject.__init__(self, parent) self.__replies = [] self.__engines = {} self.__keywords = {} self.__current = QString() self.__loading = False self.__saveTimer = AutoSaver(self, self.save) self.connect(self, SIGNAL("changed()"), self.__saveTimer.changeOccurred) self.load() def close(self): """ Public method to close the open search engines manager. """ self.__saveTimer.saveIfNeccessary() def currentEngineName(self): """ Public method to get the name of the current search engine. @return name of the current search engine (QString) """ return self.__current def setCurrentEngineName(self, name): """ Public method to set the current engine by name. @param name name of the new current engine (string or QString) """ if unicode(name) not in self.__engines: return self.__current = QString(name) self.emit(SIGNAL("currentEngineChanged()")) self.emit(SIGNAL("changed()")) def currentEngine(self): """ Public method to get a reference to the current engine. @return reference to the current engine (OpenSearchEngine) """ if self.__current.isEmpty() or unicode(self.__current) not in self.__engines: return None return self.__engines[unicode(self.__current)] def setCurrentEngine(self, engine): """ Public method to set the current engine. @param engine reference to the new current engine (OpenSearchEngine) """ if engine is None: return for engineName in self.__engines: if self.__engines[engineName] == engine: self.setCurrentEngineName(engineName) break def engine(self, name): """ Public method to get a reference to the named engine. @param name name of the engine (string or QString) @return reference to the engine (OpenSearchEngine) """ if unicode(name) not in self.__engines: return None return self.__engines[unicode(name)] def engineExists(self, name): """ Public method to check, if an engine exists. @param name name of the engine (string or QString) """ return unicode(name) in self.__engines def allEnginesNames(self): """ Public method to get a list of all engine names. @return sorted list of all engine names (QStringList) """ return QStringList(sorted(self.__engines.keys())) def enginesCount(self): """ Public method to get the number of available engines. @return number of engines (integer) """ return len(self.__engines) def addEngine(self, engine): """ Public method to add a new search engine. @param engine URL of the engine definition file (QUrl) or name of a file containing the engine definition (string or QString) or reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if isinstance(engine, QUrl): return self.__addEngineByUrl(engine) elif isinstance(engine, OpenSearchEngine): return self.__addEngineByEngine(engine) else: return self.__addEngineByFile(engine) def __addEngineByUrl(self, url): """ Private method to add a new search engine given its URL. @param url URL of the engine definition file (QUrl) @return flag indicating success (boolean) """ if not url.isValid(): return from Helpviewer.HelpWindow import HelpWindow reply = HelpWindow.networkAccessManager().get(QNetworkRequest(url)) self.connect(reply, SIGNAL("finished()"), self.__engineFromUrlAvailable) reply.setParent(self) self.__replies.append(reply) return True def __addEngineByFile(self, filename): """ Private method to add a new search engine given a filename. @param filename name of a file containing the engine definition (string or QString) @return flag indicating success (boolean) """ file_ = QFile(filename) if not file_.open(QIODevice.ReadOnly): return False reader = OpenSearchReader() engine = reader.read(file_) if not self.__addEngineByEngine(engine): return False return True def __addEngineByEngine(self, engine): """ Private method to add a new search engine given a reference to an engine. @param engine reference to an engine object (OpenSearchEngine) @return flag indicating success (boolean) """ if engine is None: return False if not engine.isValid(): return False if unicode(engine.name()) in self.__engines: return False engine.setParent(self) self.__engines[unicode(engine.name())] = engine self.emit(SIGNAL("changed()")) return True def removeEngine(self, name): """ Public method to remove an engine. @param name name of the engine (string or QString) """ if len(self.__engines) <= 1: return if unicode(name) not in self.__engines: return engine = self.__engines[unicode(name)] for keyword in [k for k in self.__keywords if self.__keywords[k] == engine]: del self.__keywords[keyword] del self.__engines[unicode(name)] file_ = QDir(self.enginesDirectory()).filePath(self.generateEngineFileName(name)) QFile.remove(file_) if name == self.__current: self.setCurrentEngineName(self.__engines.keys()[0]) self.emit(SIGNAL("changed()")) def generateEngineFileName(self, engineName): """ Public method to generate a valid engine file name. @param engineName name of the engine (string or QString) @return valid engine file name (QString) """ fileName = QString() # strip special characters for c in unicode(engineName): if c.isspace(): fileName.append("_") continue if c.isalnum(): fileName.append(c) fileName.append(".xml") return fileName def saveDirectory(self, dirName): """ Public method to save the search engine definitions to files. @param dirName name of the directory to write the files to (string or QString) """ dir = QDir() if not dir.mkpath(dirName): return dir.setPath(dirName) writer = OpenSearchWriter() for engine in self.__engines.values(): name = self.generateEngineFileName(engine.name()) fileName = dir.filePath(name) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): continue writer.write(file, engine) def save(self): """ Public method to save the search engines configuration. """ if self.__loading: return self.saveDirectory(self.enginesDirectory()) Preferences.setHelp("WebSearchEngine", self.__current) keywords = [] for k in self.__keywords: if self.__keywords[k]: keywords.append((k, self.__keywords[k].name())) Preferences.setHelp("WebSearchKeywords", keywords) def loadDirectory(self, dirName): """ Public method to load the search engine definitions from files. @param dirName name of the directory to load the files from (string or QString) @return flag indicating success (boolean) """ if not QFile.exists(dirName): return False success = False dir = QDir(dirName) for name in dir.entryList(["*.xml"]): fileName = dir.filePath(name) if self.__addEngineByFile(fileName): success = True return success def load(self): """ Public method to load the search engines configuration. """ self.__loading = True self.__current = Preferences.getHelp("WebSearchEngine") keywords = Preferences.getHelp("WebSearchKeywords") if not self.loadDirectory(self.enginesDirectory()): self.restoreDefaults() for keyword, engineName in keywords: self.__keywords[unicode(keyword)] = self.engine(engineName) if unicode(self.__current) not in self.__engines and len(self.__engines) > 0: self.__current = QString(self.__engines.keys()[0]) self.__loading = False self.emit(SIGNAL("currentEngineChanged()")) def restoreDefaults(self): """ Public method to restore the default search engines. """ reader = OpenSearchReader() for engine in OpenSearchDefaultEngines: engineDescription = QByteArray(OpenSearchDefaultEngines[engine]) buffer_ = QBuffer(engineDescription) if not buffer_.open(QIODevice.ReadOnly): continue engine = reader.read(buffer_) self.__addEngineByEngine(engine) def enginesDirectory(self): """ Public method to determine the directory containing the search engine descriptions. @return directory name (QString) """ return QString(os.path.join(Utilities.getConfigDir(), "browser", "searchengines")) def __confirmAddition(self, engine): """ Private method to confirm the addition of a new search engine. @param engine reference to the engine to be added (OpenSearchEngine) @return flag indicating the engine shall be added (boolean) """ if engine is None or not engine.isValid(): return False host = QUrl(engine.searchUrlTemplate()).host() res = KQMessageBox.question( None, QString(""), self.trUtf8( """<p>Do you want to add the following engine to your list of""" """ search engines?<br/><br/>Name: %1<br/>Searches on: %2</p>""" ) .arg(engine.name()) .arg(host), QMessageBox.StandardButtons(QMessageBox.No | QMessageBox.Yes), QMessageBox.No, ) return res == QMessageBox.Yes def __engineFromUrlAvailable(self): """ Private slot to add a search engine from the net. """ reply = self.sender() if reply is None: return if reply.error() != QNetworkReply.NoError: reply.close() if reply in self.__replies: self.__replies.remove(reply) return reader = OpenSearchReader() engine = reader.read(reply) reply.close() if reply in self.__replies: self.__replies.remove(reply) if not engine.isValid(): return if self.engineExists(engine.name()): return if not self.__confirmAddition(engine): return if not self.__addEngineByEngine(engine): return def convertKeywordSearchToUrl(self, keywordSearch): """ Public method to get the search URL for a keyword search. @param keywordSearch search string for keyword search (string or QString) @return search URL (QUrl) """ try: keyword, term = unicode(keywordSearch).split(" ", 1) except ValueError: return QUrl() if not term: return QUrl() engine = self.engineForKeyword(keyword) if engine: return engine.searchUrl(term) return QUrl() def engineForKeyword(self, keyword): """ Public method to get the engine for a keyword. @param keyword keyword to get engine for (string or QString) @return reference to the search engine object (OpenSearchEngine) """ keyword = unicode(keyword) if keyword and keyword in self.__keywords: return self.__keywords[keyword] return None def setEngineForKeyword(self, keyword, engine): """ Public method to set the engine for a keyword. @param keyword keyword to get engine for (string or QString) @param engine reference to the search engine object (OpenSearchEngine) or None to remove the keyword """ keyword = unicode(keyword) if not keyword: return if engine is None: try: del self.__keywords[keyword] except KeyError: pass else: self.__keywords[keyword] = engine self.emit(SIGNAL("changed()")) def keywordsForEngine(self, engine): """ Public method to get the keywords for a given engine. @param engine reference to the search engine object (OpenSearchEngine) @return list of keywords (list of strings) """ return [k for k in self.__keywords if self.__keywords[k] == engine] def setKeywordsForEngine(self, engine, keywords): """ Public method to set the keywords for an engine. @param engine reference to the search engine object (OpenSearchEngine) @param keywords list of keywords (QStringList) """ if engine is None: return for keyword in self.keywordsForEngine(engine): del self.__keywords[keyword] for keyword in keywords: if keyword.isEmpty(): continue self.__keywords[unicode(keyword)] = engine self.emit(SIGNAL("changed()")) def enginesChanged(self): """ Public slot to tell the search engine manager, that something has changed. """ self.emit(SIGNAL("changed()"))