Mon, 17 Jul 2017 19:58:37 +0200
Done implementing the SafeBrowsingUrl class.
Started implementing the SafeBrowsingAPIClient class.
# -*- coding: utf-8 -*- # Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the low level interface for Google Safe Browsing. """ from __future__ import unicode_literals try: str = unicode # __IGNORE_EXCEPTION__ except NameError: pass import json import random from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QDateTime, QTimer, \ QUrl from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from WebBrowser.WebBrowserWindow import WebBrowserWindow class SafeBrowsingAPIClient(QObject): """ Class implementing the low level interface for Google Safe Browsing. """ ClientId = "eric6_API_client" ClientVersion = "1.0.0" GsbUrlTemplate = "https://safebrowsing.googleapis.com/v4/{0}?key={1}" networkError = pyqtSignal(str) threatLists = pyqtSignal(list) # threatListUpdates:fetch Content-Type: application/json POST # fullHashes:find Content-Type: application/json POST def __init__(self, apiKey, fairUse=True, parent=None): """ Constructor @param apiKey API key to be used @type str @param fairUse flag indicating to follow the fair use policy @type bool @param parent reference to the parent object @type QObject """ self.__apiKey = apiKey self.__fairUse = fairUse self.__nextRequestNoSoonerThan = QDateTime() self.__replies = [] self.__failCount = 0 def getThreatLists(self): """ Public method to retrieve all available threat lists. @return threat lists @rtype list of dictionaries """ url = QUrl(self.GsbUrlTemplate.format("threatLists", self.__apiKey)) req = QNetworkRequest(url) reply = WebBrowserWindow.networkManager().get(req) reply.finished.connect(self.__threatListsReceived) @pyqtSlot() def __threatListsReceived(self): """ Private slot handling the threat lists. """ reply = self.sender() result, hasError = self.__extractData(reply) if hasError: # reschedule self.networkError.emit(reply.errorString()) self.__reschedule(reply.error(), self.getThreatLists) else: self.__setWaitDuration(result.get("minimumWaitDuration")) self.threatLists.emit(result["threatLists"]) self.__failCount = 0 if reply in self.__replies: self.__replies.remove(reply) reply.deleteLater() def __extractData(self, reply): """ Private method to extract the data of a network reply. @param reply reference to the network reply object @type QNetworkReply @return tuple containing the extracted data and an error flag @type tuple of (list or dict, bool) """ if reply.error() != QNetworkReply.NoError: return None, True result = json.loads(str(reply.readAll(), "utf-8")) return result, False def __setWaitDuration(self, minimumWaitDuration): """ Private method to set the minimum wait duration. @param minimumWaitDuration duration to be set @type str """ if not self.__fairUse or minimumWaitDuration is None: self.__nextRequestNoSoonerThan = QDateTime() else: waitDuration = int(minimumWaitDuration.rstrip("s")) self.__nextRequestNoSoonerThan = \ QDateTime.currentDateTime().addSecs(waitDuration) def __reschedule(self, errorCode, func): """ Private method to reschedule an API access. @param errorCode error code returned by the function to be rescheduled @type int @param func function to be rescheduled @type func """ if errorCode >= 500: return self.__failCount += 1 waitDuration = min( int(2 ** (self.__failCount - 1) * 15 * 60 * (1 + random.random())), 24 * 60 * 60) QTimer.singleShot(waitDuration * 1000, func)