WebBrowser/SafeBrowsing/SafeBrowsingAPIClient.py

Mon, 17 Jul 2017 19:58:37 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 17 Jul 2017 19:58:37 +0200
branch
safe_browsing
changeset 5809
5b53c17b7d93
child 5811
5358a3c7995f
permissions
-rw-r--r--

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)

eric ide

mercurial