Beispiel #1
0
class NotBitProtocol(LineReaderProcessProtocol):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)

        self.sPowCalculated = AsyncSignal()
        self.sMessageAccepted = AsyncSignal(str)

    def lineReceived(self, fd, line):
        log.debug(f'NotBit daemon message: {line}')

        match = re.search(r'\[\d*\-\d*\-\d.*?\]\s(.*)$', line)

        if match:
            # Handle specific events

            msg = match.group(1)

            # POW
            if msg.startswith('Finished calculating proof-of-work'):
                ensure(self.sPowCalculated.emit())

            # Message accepted
            ma = re.search(r'Accepted message from (BM-\w*)$', msg)
            if ma:
                ensure(self.sMessageAccepted.emit(ma.group(1)))

        if line.startswith('Failed to bind socket'):
            log.debug('Notbit failed to start')
Beispiel #2
0
    def __init__(self, treeItem, obj, parent=None):
        super().__init__(parent)

        self.sDownload = AsyncSignal()

        self.treeItem = treeItem
        self.obj = obj
        self.ipfsPath = IPFSPath(self.obj.get('path'))

        self.hlayout = QHBoxLayout(self)
        self.setLayout(self.hlayout)

        self.btnOpen = QPushButton(iOpen())
        self.btnOpen.setText(iOpen())
        self.btnOpen.setIcon(getIcon('open.png'))
        self.btnOpen.clicked.connect(partialEnsure(self.onOpen))

        self.btnHashmark = HashmarkThisButton(self.ipfsPath)
        self.btnClipboard = IPFSPathClipboardButton(self.ipfsPath)

        self.btnDownload = QToolButton(self)
        self.btnDownload.setText('Download')
        self.btnDownload.clicked.connect(self.onDl)
        self.btnDownload.hide()

        self.hlayout.addWidget(self.btnClipboard)
        self.hlayout.addWidget(self.btnHashmark)
        self.hlayout.addWidget(self.btnOpen)
        self.hlayout.addWidget(self.btnDownload)
Beispiel #3
0
    def __init__(self, dagMetaMfsPath, dagMetaHistoryMax=12, offline=False,
                 unpinOnUpdate=False, autoPreviousNode=True,
                 cipheredMeta=False,
                 autoUpdateDates=False, loop=None,
                 portalCacheTtl=60):
        super().__init__()

        self.loop = loop if loop else asyncio.get_event_loop()
        self.lock = aiorwlock.RWLock(loop=loop)
        self.loaded = asyncio.Future()
        self.portalCache = TTLCache(1, portalCacheTtl)

        self._curMetaEntry = None
        self._dagRoot = None
        self._dagMeta = None
        self._dagCid = None
        self._offline = offline
        self._dagMetaMaxHistoryItems = dagMetaHistoryMax
        self._dagMetaMfsPath = dagMetaMfsPath
        self._unpinOnUpdate = unpinOnUpdate
        self._autoPrevious = autoPreviousNode
        self._autoUpdateDates = autoUpdateDates
        self._cipheredMeta = cipheredMeta

        self.dagUpdated = AsyncSignal(str)
        self.available = AsyncSignal(object)

        self.changed.connect(lambda: ensure(self.ipfsSave()))
Beispiel #4
0
    def __init__(self, app):
        super().__init__()

        self._app = app

        self.objectStats = {}
        self.resources = {}
        self.profiles = {}
        self._currentProfile = None
        self.ipfsClient = None
        self._softIdent = None

        # Async signals
        self.ipfsConnectionReady = AsyncSignal()
        self.ipfsRepositoryReady = AsyncSignal()
        self.ipfsDaemonStarted = AsyncSignal()

        self.peers = Peers(self)
        self.node = Node(self)
        self.p2p = P2PServices(self)
        self.pubsub = PubsubMaster(self)

        self.pinner = None
        self.pinnerTask = None
        self.orbitConnector = None

        self.rsaExec = RSAExecutor(loop=self.loop,
                                   executor=self.app.executor)
        self.eccExec = ECCExecutor(loop=self.loop,
                                   executor=self.app.executor)
        self.curve25Exec = Curve25519(loop=self.loop,
                                      executor=self.app.executor)
Beispiel #5
0
    def __init__(self, ipfsCtx, peerId, ipHandle,
                 ipIdentifier: IPIdentifier,
                 pingavg=0,
                 validated=False,
                 authenticated=False):
        self._ipfsCtx = ipfsCtx
        self._ipid = ipIdentifier
        self.peerId = peerId
        self.iphandle = ipHandle
        self.pinghist = collections.deque([], 4)
        self._didPongLast = None
        self._identLast = None
        self._validated = validated
        self._authenticated = authenticated
        self._authFailedAttemptsCn = 0
        self._authFailedLtLast = None
        self._identMsg = None

        self._avatarPath = None
        self._avatarImage = self.defaultAvatarImage()

        self._dtReg = datetime.now()
        self._processData = {}

        self.sInactive = AsyncSignal(str)
        self.sStatusChanged = AsyncSignal()
Beispiel #6
0
    def __init__(self, dagNode: dict, ipid):
        self._srv = dagNode
        self._didEx = didExplode(self.id)
        self._ipid = ipid

        self.sChanged = AsyncSignal()
        self.sChanged.connectTo(self.onChanged)
Beispiel #7
0
class StoredSeedObjectActionsWidget(ObjectActionsWidget):
    def __init__(self, treeItem, obj, parent=None):
        super().__init__(treeItem, obj, parent=parent)

        self.sCancel = AsyncSignal()
        self.sRestart = AsyncSignal()

        self.btnCancel = QPushButton(self)
        self.btnCancel.setText('Cancel')
        self.btnCancel.setIcon(getIcon('cancel.png'))
        self.btnCancel.clicked.connect(
            lambda: ensure(self.sCancel.emit()))
        self.btnCancel.hide()

        self.btnRestart = QPushButton(self)
        self.btnRestart.setText('Restart')
        self.btnRestart.clicked.connect(
            lambda: ensure(self.sRestart.emit()))
        self.btnRestart.hide()

        self.hlayout.addWidget(self.btnCancel)
        self.hlayout.addWidget(self.btnRestart)

    def showControls(self, download=False, cancel=False, restart=False):
        self.btnDownload.setVisible(download)
        self.btnCancel.setVisible(cancel)
        self.btnRestart.setVisible(restart)
Beispiel #8
0
    def __init__(self, inactiveMax=40):
        self.__tokens = {}
        self.__byChannel = {}
        self.inactiveMax = inactiveMax
        self.active = True

        self.sChanChanged = AsyncSignal(str)
        self.sTokenStatus = AsyncSignal(str, str, int)
Beispiel #9
0
    def __init__(self, loop, exitFuture, startedFuture, debug=False):
        self._loop = loop
        self.debug = debug
        self.eventStarted = asyncio.Event()
        self.exitFuture = exitFuture
        self.startedFuture = startedFuture
        self.errAlreadyRunning = False

        self.bstrapRe = re.compile(
            r'\s*\d+:\d+:\d+\.\d*\s\[\w*\] Bootstrapped (\d+)\%')

        self.sTorBootstrapStatus = AsyncSignal(int, str)
Beispiel #10
0
    def __init__(self, ipfsCtx, **kw):
        self._igraphs = kw.pop('igraphs', {})

        super().__init__(ipfsCtx,
                         TOPIC_RDFBAZAAR,
                         runPeriodic=True,
                         filterSelfMessages=True,
                         **kw)

        self.__serviceToken = secrets.token_hex(64)
        self.curRevUid = uid4()

        self.sExch = AsyncSignal(RDFGraphsExchangeMessage)
        self.sSparql = AsyncSignal(SparQLHeartbeatMessage)
Beispiel #11
0
    def __init__(self, did, localId=False, ldCache=None):
        self._did = did
        self._document = {}
        self._docCid = None
        self._localId = localId
        self._lastResolve = None
        self._latestModified = None

        # JSON-LD expanded cache
        self.cache = ldCache if ldCache else LRUCache(4)

        # Async sigs
        self.sChanged = AsyncSignal(str)
        self.sServicesChanged = AsyncSignal()
        self.sChanged.connectTo(self.onDidChanged)
Beispiel #12
0
    def __init__(self,
                 dataPath: Path = None,
                 runtimeConfig=None,
                 dotPath=None,
                 app=None,
                 config=None,
                 parent=None):
        self.app = app if app else runningApp()
        self.rtCfg = runtimeConfig
        self.dotPath = dotPath
        self._config = config

        self.sServiceStarted = AsyncSignal()

        if self.dotPath is None and parent and parent.dotPath:
            self.dotPath = f'{parent.dotPath}.{self.name}'

        if dataPath:
            self.rootPath = dataPath
        else:
            self.rootPath = self.app.dataPathForService(
                self.dotPath if self.dotPath else 'tmp')

        Service.__init__(self)
        KeyListener.__init__(self)

        self.pubsubServices = []

        self.serviceConfigBind()
Beispiel #13
0
    def __init__(self,
                 ipfsCtx,
                 topic='galacteek.default',
                 runPeriodic=False,
                 filterSelfMessages=True,
                 maxMsgTsDiff=None,
                 minMsgTsDiff=None,
                 maxMessageSize=32768,
                 scheduler=None,
                 peered=False,
                 **kw):
        self.throttler = None

        self.ipfsCtx = ipfsCtx
        self.topicBase = topic
        self.inQueue = asyncio.Queue(maxsize=256)
        self.errorsQueue = asyncio.Queue()
        self.lock = asyncio.Lock()
        self.peered = peered
        self.scheduler = scheduler

        self._filterSelfLegacy = filterSelfMessages
        self._receivedCount = 0
        self._errorsCount = 0
        self._runPeriodic = runPeriodic
        self._filters = []
        self._peerActivity = {}

        GService.__init__(self, **kw)
        Configurable.__init__(self, applyNow=True)

        self._shuttingDown = False

        # Async sigs
        self.rawMessageReceived = AsyncSignal(str, str, bytes)
        self.rawMessageSent = AsyncSignal(str, str, str)

        self.tskServe = None
        self.tskProcess = None
        self.tskPeriodic = None

        self.addMessageFilter(self.filterMessageSize)
        self.addMessageFilter(self.filterPeerActivity)

        configModRegCallback(self.onConfigChanged, mod='galacteek.ipfs.pubsub')
Beispiel #14
0
class HashmarksSynchronizer:
    syncing = AsyncSignal(bool)

    async def syncTask(self):
        try:
            while True:
                await asyncio.sleep(60)

                sources = await database.hashmarkSourcesNeedSync(
                    minutes=60 * 3)
                count = len(sources)

                if count > 0:
                    log.debug(
                        'Unsynced sources: {}, syncing now'.format(count))
                    await self.sync()
        except asyncio.CancelledError:
            log.debug('Sync task cancelled')
        except Exception as err:
            log.debug(f'Sync task error: {err}')

    async def sync(self):
        _count, _scount = 0, 0

        await self.syncing.emit(True)

        log.info('Synchronizing hashmarks database ...')

        sources = await database.hashmarkSourceAll()

        for source in sources:
            if source.type == HashmarkSource.TYPE_PYMODULE:
                loader = ModuleCatalogLoader()
            elif source.type == HashmarkSource.TYPE_GITREPOS:
                loader = GitCatalogLoader()
            elif source.type == HashmarkSource.TYPE_IPFSMARKS_LEGACY:
                loader = IPFSMarksCatalogLoader()
            else:
                continue

            log.info('Synchronizing: {}'.format(source))
            _count += await loader.load(source)

            if _count >= 0:
                _scount += 1
                source.syncedlast = datetime.now()
                await source.save()

        shistory = database.HashmarkSyncHistory(
            hashmarkstotal=await database.hashmarksCount(),
            hashmarksadded=_count,
            srcsynccount=_scount
        )
        await shistory.save()

        await self.syncing.emit(False)
Beispiel #15
0
    def __init__(self, did, localId=False, ldCache=None):
        self._did = did
        self._p2pServices = weakref.WeakValueDictionary()
        self._document = {}
        self._docCid = None
        self._localId = localId
        self._lastResolve = None
        self._latestModified = None
        self._unlocked = False
        self.rsaAgent = None

        # JSON-LD expanded cache
        self.cache = ldCache if ldCache else LRUCache(4)

        # Async sigs
        self.sChanged = AsyncSignal(str)
        self.sServicesChanged = AsyncSignal()
        self.sServiceAvailable = AsyncSignal(IPIdentifier, IPService)
        self.sChanged.connectTo(self.onDidChanged)
Beispiel #16
0
    def __init__(self, mailDirPath: Path, mailBoxesPath: Path):
        super().__init__()
        self.clearMailDirPath = mailDirPath
        self.clearMailDirPath.mkdir(parents=True, exist_ok=True)
        self.clearMailDir = Maildir(str(self.clearMailDirPath))
        self.mailBoxesPath = mailBoxesPath
        self.mailBoxes = {}

        # Signals
        self.sNewMessage = AsyncSignal(str)
Beispiel #17
0
    def __init__(self, seed, objidx, oname, odescr, parent=None):
        super().__init__(seed, objidx, oname, odescr, type=seedingObjItemType)

        self.stat = None
        self.oController = StoredSeedObjectActionsWidget(self, self.objdescr)
        self.oController.fLayout()
        self.oController.sDownload.connectTo(self.onDownload)
        self.oController.sCancel.connectTo(self.onCancel)
        self.oController.sRestart.connectTo(self.onRestart)
        self._job = None

        self.sJobActive = AsyncSignal(SeedingObjectTreeItem, bool)
Beispiel #18
0
    def __init__(self, treeItem, obj, parent=None):
        super().__init__(treeItem, obj, parent=parent)

        self.sCancel = AsyncSignal()
        self.sRestart = AsyncSignal()

        self.btnCancel = QPushButton(self)
        self.btnCancel.setText('Cancel')
        self.btnCancel.setIcon(getIcon('cancel.png'))
        self.btnCancel.clicked.connect(
            lambda: ensure(self.sCancel.emit()))
        self.btnCancel.hide()

        self.btnRestart = QPushButton(self)
        self.btnRestart.setText('Restart')
        self.btnRestart.clicked.connect(
            lambda: ensure(self.sRestart.emit()))
        self.btnRestart.hide()

        self.hlayout.addWidget(self.btnCancel)
        self.hlayout.addWidget(self.btnRestart)
Beispiel #19
0
    def __init__(self,
                 ipfsCtx,
                 client,
                 topic='galacteek.default',
                 runPeriodic=False,
                 filterSelfMessages=True,
                 maxMsgTsDiff=None,
                 minMsgTsDiff=None,
                 maxMessageSize=32768,
                 scheduler=None):
        self.client = client
        self.ipfsCtx = ipfsCtx
        self.topic = topic
        self.inQueue = asyncio.Queue()
        self.errorsQueue = asyncio.Queue()
        self.lock = asyncio.Lock()
        self.scheduler = scheduler

        self._receivedCount = 0
        self._errorsCount = 0
        self._runPeriodic = runPeriodic
        self._filters = []
        self._peerActivity = {}
        self._maxMsgTsDiff = maxMsgTsDiff
        self._minMsgTsDiff = minMsgTsDiff
        self._maxMessageSize = maxMessageSize

        # Async sigs
        self.rawMessageReceived = AsyncSignal(str, str, bytes)
        self.rawMessageSent = AsyncSignal(str, str, str)

        self.tskServe = None
        self.tskProcess = None
        self.tskPeriodic = None

        self.addMessageFilter(self.filterMessageSize)
        self.addMessageFilter(self.filterPeerActivity)

        if filterSelfMessages is True:
            self.addMessageFilter(self.filterSelf)
Beispiel #20
0
    def __init__(self, name, rPath: Path, store, **kw):
        super(IGraph, self).__init__(store, **kw)

        self.name = name
        self.rPath = rPath
        self.exportsPath = self.rPath.joinpath('exports')
        self.exportsPath.mkdir(parents=True, exist_ok=True)
        self.xmlExportPath = self.exportsPath.joinpath('graph.xml')

        self.dbPath = str(self.rPath.joinpath('g_rdf.db'))
        self.dbUri = Literal(f"sqlite:///{self.dbPath}")
        self.cid = None

        self.sCidChanged = AsyncSignal(str, str)
Beispiel #21
0
class JSONPubsubService(PubsubService):
    """
    JSON pubsub listener, handling incoming messages as JSON objects
    """

    jsonMessageReceived = AsyncSignal(str, str, bytes)

    def msgDataToJson(self, msg):
        """
        Decode JSON data contained in a pubsub message
        """
        try:
            return json.loads(msg['data'].decode())
        except Exception:
            logger.debug('Could not decode JSON message data')
            return None

    async def processMessages(self):
        try:
            while True:
                data = await self.inQueue.get()

                if data is None:
                    continue

                msg = self.msgDataToJson(data)
                if msg is None:
                    self.debug('Invalid JSON message')
                    continue

                sender = data['from'] if isinstance(data['from'], str) else \
                    data['from'].decode()

                try:
                    gHub.publish(keyPsJson, (sender, self.topic, msg))
                    await self.processJsonMessage(sender, msg)
                except Exception as exc:
                    self.debug('processJsonMessage error: {}'.format(str(exc)))
                    traceback.print_exc()
                    await self.errorsQueue.put((msg, exc))
                    self._errorsCount += 1
        except asyncio.CancelledError:
            self.debug('JSON process cancelled')
        except Exception as err:
            self.debug('JSON process exception: {}'.format(str(err)))

    async def processJsonMessage(self, sender, msg):
        """ Implement this method to process incoming JSON messages"""
        return True
Beispiel #22
0
class MockEthereumController:
    ethConnected = AsyncSignal(bool)
    ethNewBlock = AsyncSignal(str)

    def changeParams(self, params):
        self._params = params

    def getWeb3(self):
        return None

    async def stop(self):
        pass

    async def start(self):
        pass

    async def latestBlock(self):
        pass

    async def getBlock(self, block='latest'):
        pass

    async def connected(self):
        return False
Beispiel #23
0
class ObjectActionsWidget(QWidget):
    def __init__(self, treeItem, obj, parent=None):
        super().__init__(parent)

        self.sDownload = AsyncSignal()

        self.treeItem = treeItem
        self.obj = obj
        self.ipfsPath = IPFSPath(self.obj.get('path'))

        self.hlayout = QHBoxLayout(self)
        self.setLayout(self.hlayout)

        self.btnOpen = QPushButton(iOpen())
        self.btnOpen.setText(iOpen())
        self.btnOpen.setIcon(getIcon('open.png'))
        self.btnOpen.clicked.connect(partialEnsure(self.onOpen))

        self.btnHashmark = HashmarkThisButton(self.ipfsPath)
        self.btnClipboard = IPFSPathClipboardButton(self.ipfsPath)

        self.btnDownload = QToolButton(self)
        self.btnDownload.setText('Download')
        self.btnDownload.clicked.connect(self.onDl)
        self.btnDownload.hide()

        self.hlayout.addWidget(self.btnClipboard)
        self.hlayout.addWidget(self.btnHashmark)
        self.hlayout.addWidget(self.btnOpen)
        self.hlayout.addWidget(self.btnDownload)

    def fLayout(self):
        self.hlayout.addItem(
            QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Minimum))

    def onDl(self):
        ensure(self.sDownload.emit())

    def showControls(self, download=False):
        self.btnDownload.setVisible(download)

    async def onOpen(self, *a):
        app = QApplication.instance()
        ensure(app.resourceOpener.open(self.obj['path']))
Beispiel #24
0
class PeersCountGraphView(BaseTimedGraph):
    peersCountFetched = AsyncSignal(int)

    def __init__(self, parent=None):
        super(PeersCountGraphView, self).__init__(parent=parent)

        self.setObjectName('PeersCountGraph')
        self.peersCountFetched.connectTo(self.onPeersCount)
        self.axisY.setTitleText(iPeers())

        pen = QPen(QColor('#60cccf'))
        pen.setWidth(2)

        self.seriesPeers = QLineSeries()
        self.seriesPeers.setName('Peers')
        self.seriesPeers.setPen(pen)
        self.chart.addSeries(self.seriesPeers)
        self.seriesPeers.attachAxis(self.axisX)
        self.seriesPeers.attachAxis(self.axisY)

        self.setChart(self.chart)

    async def onPeersCount(self, count):
        dtNow = QDateTime.currentDateTime()
        dtMsecs = dtNow.toMSecsSinceEpoch()

        delta = 120 * 1000
        if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta:
            self.removeOldSeries(self.seriesPeers, dtMsecs - delta)
            self.seriesPurgedT = dtMsecs

        peersVector = self.seriesPeers.pointsVector()
        values = [p.y() for p in peersVector]

        if values:
            self.axisY.setMax(max(values) + 10)
            self.axisY.setMin(min(values) - 10)

        dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000))
        dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000)

        self.seriesPeers.append(dtMsecs, count)
        self.axisX.setRange(dateMin, dateMax)
Beispiel #25
0
    def __init__(self, ipfsCtx, peerId, identMsg,
                 ipIdentifier: IPIdentifier,
                 pinglast=0, pingavg=0,
                 validated=False,
                 authenticated=False):
        self._ipfsCtx = ipfsCtx
        self._ipid = ipIdentifier
        self.peerId = peerId
        self.ident = identMsg
        self.pinglast = pinglast
        self.pingavg = pingavg
        self._identLast = int(time.time())
        self._validated = validated
        self._authenticated = authenticated

        self._avatarPath = None
        self._avatarImage = self.defaultAvatarImage()

        self.sInactive = AsyncSignal(str)
Beispiel #26
0
class TorProtocol(asyncio.SubprocessProtocol):
    def __init__(self, loop, exitFuture, startedFuture, debug=False):
        self._loop = loop
        self.debug = debug
        self.eventStarted = asyncio.Event()
        self.exitFuture = exitFuture
        self.startedFuture = startedFuture
        self.errAlreadyRunning = False

        self.bstrapRe = re.compile(
            r'\s*\d+:\d+:\d+\.\d*\s\[\w*\] Bootstrapped (\d+)\%')

        self.sTorBootstrapStatus = AsyncSignal(int, str)

    @property
    def loop(self):
        return self._loop

    def pipe_data_received(self, fd, data):
        try:
            msg = data.decode().strip()
        except BaseException:
            return

        for line in msg.split('\n'):
            if self.debug:
                log.debug(f'TOR: {line}')

            ma = self.bstrapRe.search(line)
            if ma:
                try:
                    pc = ma.group(1)
                    ensure(
                        self.sTorBootstrapStatus.emit(int(pc), 'bootstrapped'))
                except Exception:
                    continue

    def process_exited(self):
        if not self.exitFuture.done():
            self.exitFuture.set_result(True)
Beispiel #27
0
    def __init__(self, maildir: BitMessageMailDir, parent=None):
        super().__init__(parent)

        self.app = QApplication.instance()
        self.maildir = maildir
        self.currentItemChanged.connect(partialEnsure(
            self.onCurMessageChanged))
        self.setColumnCount(2)
        self.setHeaderLabels(['Subject', 'Date'])
        self.setHeaderHidden(True)
        self.setSortingEnabled(True)

        self.sMessageNeedsDisplay = AsyncSignal(MaildirMessage)

        self.viewActive.connect(self.onViewSwitched)

        self.fontNoBold = QFont(self.font())
        self.fontNoBold.setBold(False)
        self.fontBold = QFont(self.font())
        self.fontBold.setBold(True)

        self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.hideColumn(1)
Beispiel #28
0
class BandwidthGraphView(BaseTimedGraph):
    bwStatsFetched = AsyncSignal(dict)

    def __init__(self, parent=None):
        super(BandwidthGraphView, self).__init__(parent=parent)

        self.setObjectName('BandwidthGraph')
        self.chart.setTitle(iBandwidthUsage())
        self.axisY.setTitleText(iRateKbPerSecond())

        self.bwStatsFetched.connectTo(self.onBwStats)

        penIn = QPen(QColor('red'))
        penIn.setWidth(2)
        penOut = QPen(QColor('blue'))
        penOut.setWidth(2)

        self.seriesKbIn = QLineSeries()
        self.seriesKbIn.setName(iRateInput())
        self.seriesKbIn.setPen(penIn)
        self.chart.addSeries(self.seriesKbIn)
        self.seriesKbIn.attachAxis(self.axisX)
        self.seriesKbIn.attachAxis(self.axisY)

        self.seriesKbOut = QLineSeries()
        self.seriesKbOut.setName(iRateOutput())
        self.seriesKbOut.setPen(penOut)
        self.chart.addSeries(self.seriesKbOut)
        self.seriesKbOut.attachAxis(self.axisX)
        self.seriesKbOut.attachAxis(self.axisY)

        self.setChart(self.chart)

    async def onBwStats(self, stats):
        dtNow = QDateTime.currentDateTime()
        dtMsecs = dtNow.toMSecsSinceEpoch()

        delta = 120 * 1000
        if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta:
            self.removeOldSeries(self.seriesKbIn, dtMsecs - delta)
            self.removeOldSeries(self.seriesKbOut, dtMsecs - delta)
            self.seriesPurgedT = dtMsecs

        rateIn = stats.get('RateIn', 0)
        rateOut = stats.get('RateOut', 0)
        rateInKb = int(rateIn / 1024)
        rateOutKb = int(rateOut / 1024)

        inVector = self.seriesKbIn.pointsVector()
        outVector = self.seriesKbIn.pointsVector()

        inValues = [p.y() for p in inVector]
        outValues = [p.y() for p in outVector]

        if inValues or outValues:
            self.axisY.setMax(max(max(inValues), max(outValues)))

        dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000))
        dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000)

        self.seriesKbIn.append(dtMsecs, rateInKb)
        self.seriesKbOut.append(dtMsecs, rateOutKb)
        self.axisX.setRange(dateMin, dateMax)
Beispiel #29
0
class GalacteekApplication(QApplication):
    """
    Galacteek application class

    :param bool debug: enable debugging
    :param str profile: application profile
    """

    manualAvailable = pyqtSignal(str, dict)
    messageDisplayRequest = pyqtSignal(str, str)
    appImageImported = pyqtSignal(str)

    dbConfigured = AsyncSignal(bool)

    def __init__(self,
                 debug=False,
                 profile='main',
                 sslverify=True,
                 enableOrbital=False,
                 progName=None,
                 cmdArgs={}):
        QApplication.__init__(self, sys.argv)

        QCoreApplication.setApplicationName(GALACTEEK_NAME)

        self.dbConfigured.connectTo(self.onDbConfigured)

        self.setQuitOnLastWindowClosed(False)

        self._cmdArgs = cmdArgs
        self._debugEnabled = debug
        self._appProfile = profile
        self._loop = None
        self._executor = None
        self._ipfsClient = None
        self._ipfsOpMain = None
        self._ipfsd = None
        self._sslverify = sslverify
        self._progName = progName
        self._progCid = None
        self._system = platform.system()
        self._urlSchemes = {}
        self._shuttingDown = False

        self._icons = {}
        self._ipfsIconsCache = {}
        self._ipfsIconsCacheMax = 32

        self.enableOrbital = enableOrbital
        self.orbitConnector = None

        self.translator = None
        self.mainWindow = None
        self.feedFollowerTask = None

        self.webProfiles = {}
        self.ipfsCtx = IPFSContext(self)
        self.peersTracker = peers.PeersTracker(self.ipfsCtx)

        self.desktopWidget = QDesktopWidget()
        self.desktopGeometry = self.desktopWidget.screenGeometry()

        self.setWindowIcon(getIcon('galacteek.png'))

        self.setupAsyncLoop()
        self.setupPaths()

    @property
    def cmdArgs(self):
        return self._cmdArgs

    @property
    def shuttingDown(self):
        return self._shuttingDown

    @property
    def offline(self):
        return self.cmdArgs.offline

    @property
    def system(self):
        return self._system

    @property
    def debugEnabled(self):
        return self._debugEnabled

    @property
    def ipfsIconsCacheMax(self):
        return self._ipfsIconsCacheMax

    @property
    def ipfsIconsCache(self):
        return self._ipfsIconsCache

    @property
    def progName(self):
        return self._progName

    @property
    def progCid(self):
        return self._progCid

    @property
    def sslverify(self):
        return self._sslverify

    @property
    def appProfile(self):
        return self._appProfile

    @property
    def ipfsd(self):
        return self._ipfsd

    @property
    def loop(self):
        return self._loop

    @property
    def executor(self):
        return self._executor

    @loop.setter
    def loop(self, newLoop):
        self._loop = newLoop

    @property
    def allTasks(self):
        return asyncio.Task.all_tasks(loop=self.loop)

    @property
    def pendingTasks(self):
        return [task for task in self.allTasks if not task.done()]

    @property
    def ipfsClient(self):
        return self._ipfsClient

    @ipfsClient.setter
    def ipfsClient(self, client):
        self.debug('IPFS client changed: {}'.format(client))
        self._ipfsClient = client

    @property
    def ipfsOpMain(self):
        return self._ipfsOpMain

    @ipfsOpMain.setter
    def ipfsOpMain(self, op):
        """ The main IPFS operator, used by @ipfsOp """
        self.debug('Main IPFS operator upgrade: ID {}'.format(op.uid))
        self._ipfsOpMain = op

    @property
    def gatewayAuthority(self):
        params = self.getIpfsConnectionParams()
        return '{0}:{1}'.format(params.host, params.gatewayPort)

    @property
    def gatewayUrl(self):
        params = self.getIpfsConnectionParams()
        return params.gatewayUrl

    @property
    def dataLocation(self):
        return self._dataLocation

    @property
    def ipfsBinLocation(self):
        return self._ipfsBinLocation

    @property
    def ipfsDataLocation(self):
        return self._ipfsDataLocation

    @property
    def nsCacheLocation(self):
        return self._nsCacheLocation

    @property
    def orbitDataLocation(self):
        return self._orbitDataLocation

    def applyStyle(self, theme='default'):
        qssPath = ":/share/static/qss/{theme}/galacteek.qss".format(
            theme=theme)
        qssFile = QFile(qssPath)

        try:
            qssFile.open(QFile.ReadOnly)
            styleSheetBa = qssFile.readAll()
            styleSheetStr = styleSheetBa.data().decode('utf-8')
            self.setStyleSheet(styleSheetStr)
        except BaseException:
            # that would probably occur if the QSS is not
            # in the resources file..  set some default stylesheet here?
            pass

        self.gStyle = GalacteekStyle()
        self.setStyle(self.gStyle)

    def debug(self, msg):
        if self.debugEnabled:
            log.debug(msg)

    def initSystemTray(self):
        self.systemTray = QSystemTrayIcon(self)
        self.systemTray.setIcon(getIcon('galacteek-incandescent.png'))
        self.systemTray.show()
        self.systemTray.activated.connect(self.onSystemTrayIconClicked)

        systemTrayMenu = QMenu(self.mainWindow)

        actionShow = systemTrayMenu.addAction('Show')
        actionShow.setIcon(getIcon('galacteek-incandescent.png'))
        actionShow.triggered.connect(self.onShowWindow)

        systemTrayMenu.addSeparator()

        actionQuit = systemTrayMenu.addAction('Quit')
        actionQuit.setIcon(getIcon('quit.png'))
        actionQuit.triggered.connect(self.onExit)

        self.systemTray.setContextMenu(systemTrayMenu)

    def initMisc(self):
        self.multihashDb = IPFSObjectMetadataDatabase(self._mHashDbLocation,
                                                      loop=self.loop)

        self.resourceOpener = IPFSResourceOpener(parent=self)

        self.downloadsManager = downloads.DownloadsManager(self)
        self.marksLocal = IPFSMarks(self.localMarksFileLocation, backup=True)

        self.marksLocal.addCategory('general')
        self.marksLocal.addCategory('uncategorized')
        self.marksNetwork = IPFSMarks(self.networkMarksFileLocation,
                                      autosave=False)

        self.tempDir = QTemporaryDir()
        self.tempDirWeb = self.tempDirCreate(self.tempDir.path(),
                                             'webdownloads')

    def tempDirCreate(self, basedir, name=None):
        tmpdir = QDir(basedir)

        if not tmpdir.exists():
            return

        uid = name if name else str(uuid.uuid4())

        path = tmpdir.absoluteFilePath(uid)
        if tmpdir.mkpath(path):
            return path

    async def setupHashmarks(self):
        pkg = 'galacteek.hashmarks.default'

        res = await database.hashmarkSourceSearch(
            name='core', url=pkg, type=models.HashmarkSource.TYPE_PYMODULE)

        if not res:
            await database.hashmarkSourceAdd(
                type=models.HashmarkSource.TYPE_PYMODULE, url=pkg, name='core')
            await self.hmSynchronizer.sync()

        await database.hashmarkSourceAdd(
            type=models.HashmarkSource.TYPE_GITREPOS,
            url='https://github.com/galacteek/hashmarks-dwebland')

        await self.scheduler.spawn(self.hmSynchronizer.syncTask())

    def setupTranslator(self):
        if self.translator:
            QApplication.removeTranslator(self.translator)

        self.translator = QTranslator()
        QApplication.installTranslator(self.translator)
        lang = self.settingsMgr.getSetting(CFG_SECTION_UI, CFG_KEY_LANG)
        self.translator.load(
            ':/share/translations/galacteek_{0}.qm'.format(lang))

    def createMainWindow(self, show=True):
        self.mainWindow = mainui.MainWindow(self)
        if show is True:
            self.mainWindow.show()

    def onSystemTrayIconClicked(self, reason):
        if reason == QSystemTrayIcon.Unknown:
            pass
        elif reason == QSystemTrayIcon.Context:
            pass
        elif reason == QSystemTrayIcon.DoubleClick:
            self.mainWindow.showMaximized()
        else:
            pass

    def systemTrayMessage(self,
                          title,
                          message,
                          timeout=2000,
                          messageIcon=QSystemTrayIcon.Information):
        self.systemTray.showMessage(title, message, messageIcon, timeout)

    @ipfsOp
    async def setupRepository(self, op):
        pubsubEnabled = True  # mandatory now ..
        hExchEnabled = self.settingsMgr.isTrue(CFG_SECTION_IPFS,
                                               CFG_KEY_HASHMARKSEXCH)

        self.ipfsCtx.resources['ipfs-logo-ice'] = await self.importQtResource(
            '/share/icons/ipfs-logo-128-ice.png')
        self.ipfsCtx.resources['ipfs-cube-64'] = await self.importQtResource(
            '/share/icons/ipfs-cube-64.png')
        self.ipfsCtx.resources['atom-feed'] = await self.importQtResource(
            '/share/icons/atom-feed.png')
        self.ipfsCtx.resources['markdown-reference'] = \
            await self.importQtResource(
                '/share/static/docs/markdown-reference.html')

        await self.ipfsCtx.setup(pubsubEnable=pubsubEnabled,
                                 pubsubHashmarksExch=hExchEnabled,
                                 offline=self.offline)
        await self.ipfsCtx.profilesInit()
        await self.qSchemeHandler.start()
        await self.importLdContexts()

        self.feedFollower = FeedFollower(self)
        self.feedFollowerTask = await self.scheduler.spawn(
            self.feedFollower.process())

        await self.ipfsCtx.ipfsRepositoryReady.emit()
        self.ipfsCtx._ipfsRepositoryReady.emit()

        #
        # If the application's binary name is a valid CID, pin it!
        # This happens when running the AppImage and ensures
        # self-seeding of the image!
        #

        if isinstance(self.progName, str):
            progNameClean = re.sub(r'[\.\/]*', '', self.progName)
            if cidhelpers.cidValid(progNameClean):
                self._progCid = progNameClean
                log.debug("Auto pinning program's CID: {0}".format(
                    self.progCid))
                await self.ipfsCtx.pin(joinIpfs(self.progCid),
                                       False,
                                       self.onAppReplication,
                                       qname='self-seeding')

        if self.cmdArgs.seed and self.cmdArgs.appimage:
            await self.seedAppImage()

    @ipfsOp
    async def seedAppImage(self, ipfsop):
        # Automatic AppImage seeding

        if os.path.isfile(self.cmdArgs.binarypath):
            log.info(
                'AppImage seeding: {img}'.format(img=self.cmdArgs.binarypath))

            ensure(ipfsop.addPath(self.cmdArgs.binarypath, wrap=True),
                   futcallback=self.onAppSeed)

    def onAppSeed(self, future):
        try:
            replResult = future.result()
        except Exception as err:
            log.debug('AppImage seed: failed', exc_info=err)
        else:
            if isinstance(replResult, dict):
                cid = replResult.get('Hash')

                self.appImageImported.emit(cid)

                log.info('AppImage seed OK: CID {cid}'.format(cid=cid))

    def onAppReplication(self, future):
        try:
            replResult = future.result()
        except Exception as err:
            log.debug('App replication: failed', exc_info=err)
        else:
            log.debug('App replication: success ({result})'.format(
                result=replResult))

    @ipfsOp
    async def importLdContexts(self, ipfsop):
        """
        Import the JSON-LD contexts and associate the
        directory entry with the 'galacteek.ld.contexts' key
        """

        contextsPath = ipfsop.ldContextsRootPath()

        if not os.path.isdir(contextsPath):
            log.debug('LD contexts not found')
            return

        entry = await ipfsop.addPath(contextsPath,
                                     recursive=True,
                                     hidden=False)
        if entry:
            ldKeyName = 'galacteek.ld.contexts'
            log.debug('LD contexts sitting at: {}'.format(entry.get('Hash')))
            await ipfsop.keyGen(ldKeyName, checkExisting=True)
            ensure(
                ipfsop.publish(entry['Hash'],
                               key=ldKeyName,
                               allow_offline=True))

    @ipfsOp
    async def importQtResource(self, op, path):
        rscFile = QFile(':{0}'.format(path))

        try:
            rscFile.open(QFile.ReadOnly)
            data = rscFile.readAll().data()
            entry = await op.addBytes(data)
        except Exception as e:
            log.debug('importQtResource: {}'.format(str(e)))
        else:
            return entry

    def ipfsTask(self, fn, *args, **kw):
        """ Schedule an async IPFS task """
        return self.loop.create_task(fn(self.ipfsClient, *args, **kw))

    def ipfsTaskOp(self, fn, *args, **kw):
        """ Schedule an async IPFS task using an IPFSOperator instance """
        client = self.ipfsClient
        if client:
            return self.loop.create_task(
                fn(self.getIpfsOperator(), *args, **kw))

    def getIpfsOperator(self):
        """ Returns a new IPFSOperator with the currently active IPFS client"""
        return IPFSOperator(self.ipfsClient,
                            ctx=self.ipfsCtx,
                            debug=self.debugEnabled,
                            nsCachePath=self.nsCacheLocation)

    def getIpfsConnectionParams(self):
        mgr = self.settingsMgr

        section = CFG_SECTION_IPFSD
        if mgr.isTrue(section, CFG_KEY_ENABLED):
            return IPFSConnectionParams(
                '127.0.0.1', mgr.getSetting(section, CFG_KEY_APIPORT),
                mgr.getSetting(section, CFG_KEY_HTTPGWPORT))
        else:
            section = CFG_SECTION_IPFSCONN1
            return IPFSConnectionParams(
                mgr.getSetting(section, CFG_KEY_HOST),
                mgr.getSetting(section, CFG_KEY_APIPORT),
                mgr.getSetting(section, CFG_KEY_HTTPGWPORT))

    def getEthParams(self):
        mgr = self.settingsMgr
        provType = mgr.getSetting(CFG_SECTION_ETHEREUM, CFG_KEY_PROVIDERTYPE)
        rpcUrl = mgr.getSetting(CFG_SECTION_ETHEREUM, CFG_KEY_RPCURL)
        return EthereumConnectionParams(rpcUrl, provType=provType)

    async def updateIpfsClient(self):
        from galacteek.ipfs import ConnectionError

        connParams = self.getIpfsConnectionParams()
        client = aioipfs.AsyncIPFS(host=connParams.host,
                                   port=connParams.apiPort,
                                   loop=self.loop)
        self.ipfsClient = client
        self.ipfsCtx.ipfsClient = client
        self.ipfsOpMain = self.getIpfsOperator()
        self.ipfsOpMain.ipidManager = self.ipidManager

        IPFSOpRegistry.regDefault(self.ipfsOpMain)

        try:
            await self.setupRepository()
        except ConnectionError:
            await messageBoxAsync(
                'IPFS connection error (is your daemon running ?)')

        await self.ipfsCtx.ipfsConnectionReady.emit()

    async def stopIpfsServices(self):
        try:
            await self.ipfsCtx.shutdown()
        except BaseException as err:
            log.debug(
                'Error shutting down context: {err}'.format(err=str(err)))

        if self.feedFollowerTask is not None:
            await self.feedFollowerTask.close()

    def setupDb(self):
        ensure(self.setupOrmDb(self._mainDbLocation))

    def jobsExceptionHandler(self, scheduler, context):
        pass

    async def setupOrmDb(self, dbpath):
        self.scheduler = await aiojobs.create_scheduler(close_timeout=1.0,
                                                        limit=150,
                                                        pending_limit=1000)

        # Old database, just for Atom feeds right now

        self.sqliteDb = SqliteDatabase(self._sqliteDbLocation)
        ensure(self.sqliteDb.setup())
        self.modelAtomFeeds = AtomFeedsModel(self.sqliteDb.feeds, parent=self)

        self.urlHistory = history.URLHistory(
            self.sqliteDb,
            enabled=self.settingsMgr.urlHistoryEnabled,
            parent=self)

        if not await database.initOrm(self._mainDbLocation):
            await self.dbConfigured.emit(False)
            return

        await self.setupHashmarks()
        await self.dbConfigured.emit(True)

    async def onDbConfigured(self, configured):
        if not configured:
            return

        self.debug('Database ready')

        self.setupClipboard()
        self.setupTranslator()
        self.initSystemTray()
        self.initMisc()
        self.initEthereum()
        self.initWebProfiles()
        self.initDapps()
        self.createMainWindow()
        self.clipboardInit()

        await self.setupIpfsConnection()

    async def fetchGoIpfs(self):
        ipfsPath = self.which('ipfs')
        fsMigratePath = self.which('fs-repo-migrations')

        if fsMigratePath is None or self.cmdArgs.forcegoipfsdl:
            await fetchFsMigrateWrapper(self)

        if ipfsPath is None or self.cmdArgs.forcegoipfsdl:
            path = await fetchGoIpfsWrapper(self)
            if path is None:
                self.systemTrayMessage('Galacteek', iGoIpfsFetchError())

    async def setupIpfsConnection(self):
        sManager = self.settingsMgr

        await self.fetchGoIpfs()

        if sManager.isTrue(CFG_SECTION_IPFSD, CFG_KEY_ENABLED):
            fsMigratePath = shutil.which('fs-repo-migrations')
            hasFsMigrate = fsMigratePath is not None

            if hasFsMigrate is False and self.cmdArgs.migrate is True:
                self.systemTrayMessage('Galacteek', iFsRepoMigrateNotFound())

            enableMigrate = hasFsMigrate is True and \
                self.cmdArgs.migrate is True

            ipfsPath = self.which('ipfs')

            await self.startIpfsDaemon(goIpfsPath=ipfsPath,
                                       migrateRepo=enableMigrate)
        else:
            await self.updateIpfsClient()

    def setupMainObjects(self):
        self.manuals = ManualsManager(self)
        self.mimeDb = QMimeDatabase()
        self.jinjaEnv = defaultJinjaEnv()
        self.solarSystem = SolarSystem()
        self.mimeTypeIcons = preloadMimeIcons()
        self.hmSynchronizer = HashmarksSynchronizer()
        self.ipidManager = IPIDManager(
            resolveTimeout=self.settingsMgr.ipidIpnsTimeout)

        self.towers = {
            'dags': DAGSignalsTower(self),
            'schemes': URLSchemesTower(self),
            'did': DIDTower()
        }

        self.rscAnalyzer = ResourceAnalyzer(parent=self)

        self.messageDisplayRequest.connect(
            lambda msg, title: ensure(messageBoxAsync(msg, title=title)))
        self.appImageImported.connect(lambda cid: ensure(
            messageBoxAsync('AppImage was imported in IPFS!')))

    def setupAsyncLoop(self):
        """
        Install the quamash event loop and enable debugging
        """

        loop = QEventLoop(self)
        asyncio.set_event_loop(loop)
        logging.getLogger('quamash').setLevel(logging.INFO)

        if self.debugEnabled:
            logging.getLogger('asyncio').setLevel(logging.DEBUG)
            loop.set_debug(True)
            warnings.simplefilter('always', ResourceWarning)
            warnings.simplefilter('always', BytesWarning)
            warnings.simplefilter('always', ImportWarning)

        self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)

        self.loop = loop
        return loop

    def task(self, fn, *args, **kw):
        return self.loop.create_task(fn(*args, **kw))

    def configure(self):
        self.initSettings()
        self.setupMainObjects()
        self.setupSchemeHandlers()
        self.applyStyle()
        self.setupDb()

    def acquireLock(self):
        lpath = Path(self._pLockLocation)

        if not lpath.exists():
            lpath.touch()

        self.lock = FileLock(lpath, timeout=2)

        try:
            self.lock.acquire()
        except Exception:
            return questionBox(
                'Lock', 'The profile lock could not be acquired '
                '(another instance could be running). Ignore ?')

        return True

    def setupPaths(self):
        qtDataLocation = QStandardPaths.writableLocation(
            QStandardPaths.DataLocation)

        if not qtDataLocation:
            raise Exception('No writable data location found')

        self._dataLocation = os.path.join(qtDataLocation, self._appProfile)

        self._ipfsBinLocation = os.path.join(qtDataLocation, 'ipfs-bin')
        self._ipfsDataLocation = os.path.join(self.dataLocation, 'ipfs')
        self._orbitDataLocation = os.path.join(self.dataLocation, 'orbitdb')
        self._mHashDbLocation = os.path.join(self.dataLocation, 'mhashmetadb')
        self._sqliteDbLocation = os.path.join(self.dataLocation, 'db.sqlite')
        self._pLockLocation = os.path.join(self.dataLocation, 'profile.lock')
        self._mainDbLocation = os.path.join(self.dataLocation,
                                            'db_main.sqlite3')
        self.marksDataLocation = os.path.join(self.dataLocation, 'marks')
        self.uiDataLocation = os.path.join(self.dataLocation, 'ui')
        self.cryptoDataLocation = os.path.join(self.dataLocation, 'crypto')
        self.gpgDataLocation = os.path.join(self.cryptoDataLocation, 'gpg')
        self.localMarksFileLocation = os.path.join(self.marksDataLocation,
                                                   'ipfsmarks.local.json')
        self.networkMarksFileLocation = os.path.join(self.marksDataLocation,
                                                     'ipfsmarks.network.json')
        self.pinStatusLocation = os.path.join(self.dataLocation,
                                              'pinstatus.json')
        self._nsCacheLocation = os.path.join(self.dataLocation, 'nscache.json')

        qtConfigLocation = QStandardPaths.writableLocation(
            QStandardPaths.ConfigLocation)
        self.configDirLocation = os.path.join(qtConfigLocation, GALACTEEK_NAME,
                                              self._appProfile)
        self.settingsFileLocation = os.path.join(
            self.configDirLocation, '{}.conf'.format(GALACTEEK_NAME))

        for dir in [
                self._ipfsDataLocation, self._mHashDbLocation,
                self.ipfsBinLocation, self.marksDataLocation,
                self.cryptoDataLocation, self.gpgDataLocation,
                self.uiDataLocation, self.configDirLocation
        ]:
            if not os.path.exists(dir):
                os.makedirs(dir)

        self.defaultDownloadsLocation = QStandardPaths.writableLocation(
            QStandardPaths.DownloadLocation)

        self.debug('Datapath: {0}, config: {1}, configfile: {2}'.format(
            self._dataLocation, self.configDirLocation,
            self.settingsFileLocation))

        os.environ['PATH'] += os.pathsep + self.ipfsBinLocation

    def which(self, prog='ipfs'):
        path = self.ipfsBinLocation + os.pathsep + os.environ['PATH']
        result = shutil.which(prog, path=path)
        self.debug('Program {0} found at {1}'.format(prog, result))
        return result

    def initSettings(self):
        self.settingsMgr = SettingsManager(path=self.settingsFileLocation)
        setDefaultSettings(self)
        self.settingsMgr.sync()

    async def startIpfsDaemon(self, goIpfsPath='ipfs', migrateRepo=False):
        if self.ipfsd is not None:  # we only support one daemon for now
            return

        pubsubEnabled = True  # mandatory now ..
        corsEnabled = self.settingsMgr.isTrue(CFG_SECTION_IPFSD, CFG_KEY_CORS)

        sManager = self.settingsMgr
        section = CFG_SECTION_IPFSD

        # Instantiate an IPFS daemon using asyncipfsd and
        # start it in a task, monitoring the initialization process

        self._ipfsd = asyncipfsd.AsyncIPFSDaemon(
            self.ipfsDataLocation,
            goIpfsPath=goIpfsPath,
            apiport=sManager.getInt(section, CFG_KEY_APIPORT),
            swarmport=sManager.getInt(section, CFG_KEY_SWARMPORT),
            swarmportQuic=sManager.getInt(section, CFG_KEY_SWARMPORT_QUIC),
            swarmProtos=sManager.swarmProtosList,
            gatewayport=sManager.getInt(section, CFG_KEY_HTTPGWPORT),
            swarmLowWater=sManager.getInt(section, CFG_KEY_SWARMLOWWATER),
            swarmHighWater=sManager.getInt(section, CFG_KEY_SWARMHIGHWATER),
            storageMax=sManager.getInt(section, CFG_KEY_STORAGEMAX),
            gwWritable=sManager.isTrue(section, CFG_KEY_HTTPGWWRITABLE),
            routingMode=sManager.getSetting(section, CFG_KEY_ROUTINGMODE),
            pubsubRouter=sManager.getSetting(section, CFG_KEY_PUBSUB_ROUTER),
            namesysPubsub=sManager.isTrue(section, CFG_KEY_NAMESYS_PUBSUB),
            pubsubSigning=sManager.isTrue(section, CFG_KEY_PUBSUB_USESIGNING),
            fileStore=sManager.isTrue(section, CFG_KEY_FILESTORE),
            nice=sManager.getInt(section, CFG_KEY_NICE),
            pubsubEnable=pubsubEnabled,
            corsEnable=corsEnabled,
            migrateRepo=migrateRepo,
            debug=self.cmdArgs.goipfsdebug,
            offline=self.cmdArgs.offline,
            loop=self.loop)

        await self.scheduler.spawn(self.startIpfsdTask(self.ipfsd))

    async def startIpfsdTask(self, ipfsd):
        started = await ipfsd.start()

        if started is False:
            return self.systemTrayMessage('IPFS', iIpfsDaemonProblem())

        running = False

        logUser.info(iIpfsDaemonStarted())

        # Use asyncio.wait_for to wait for the proto.eventStarted
        # event to be fired.

        for attempt in range(1, 32):
            logUser.info(iIpfsDaemonWaiting(attempt))

            with async_timeout.timeout(1):
                try:
                    await ipfsd.proto.eventStarted.wait()
                except asyncio.CancelledError:
                    continue
                except asyncio.TimeoutError:
                    # Event not set yet, wait again
                    log.debug('IPFSD: timeout occured while waiting for '
                              'daemon to start (attempt: {0})'.format(attempt))
                    continue
                else:
                    # Event was set, good to go
                    logUser.info(iIpfsDaemonReady())
                    running = True
                    break

        if running is True:
            ensure(self.updateIpfsClient())
        else:
            logUser.info(iIpfsDaemonInitProblem())

    def initEthereum(self):
        try:
            from galacteek.dweb.ethereum.ctrl import EthereumController

            self.ethereum = EthereumController(self.getEthParams(),
                                               loop=self.loop,
                                               parent=self,
                                               executor=self.executor)
            if self.settingsMgr.ethereumEnabled:
                ensure(self.ethereum.start())
        except ImportError:
            self.ethereum = MockEthereumController()

    def normalCursor(self):
        cursor = QCursor(Qt.ArrowCursor)
        QApplication.setOverrideCursor(cursor)
        QApplication.changeOverrideCursor(cursor)

    def setupClipboard(self):
        self.appClipboard = self.clipboard()
        self.clipTracker = ClipboardTracker(self, self.appClipboard)

    def clipboardInit(self):
        self.clipTracker.clipboardInit()

    def setClipboardText(self, text):
        self.clipTracker.setText(text)

    def getClipboardText(self):
        return self.clipTracker.getText()

    def initWebProfiles(self):
        self.scriptsIpfs = ipfsClientScripts(self.getIpfsConnectionParams())

        self.webProfiles = {
            'minimal': MinimalProfile(parent=self),
            'ipfs': IPFSProfile(parent=self),
            'web3': Web3Profile(parent=self)
        }

    def availableWebProfilesNames(self):
        return [p.profileName for n, p in self.webProfiles.items()]

    def initDapps(self):
        self.dappsRegistry = DappsRegistry(self.ethereum, parent=self)

    def setupSchemeHandlers(self):
        self.dwebSchemeHandler = DWebSchemeHandlerGateway(self)
        self.ensSchemeHandler = EthDNSSchemeHandler(self)
        self.ensProxySchemeHandler = EthDNSProxySchemeHandler(self)
        self.nativeIpfsSchemeHandler = NativeIPFSSchemeHandler(
            self, noMutexes=self.cmdArgs.noipfsmutexlock)
        self.qSchemeHandler = MultiObjectHostSchemeHandler(self)

    def subUrl(self, path):
        """ Joins the gatewayUrl and path to form a new URL """
        sub = QUrl(str(self.gatewayUrl))
        sub.setPath(path)
        return sub

    def getJinjaTemplate(self, name):
        try:
            tmpl = self.jinjaEnv.get_template(name)
        except jinja2.exceptions.TemplateNotFound:
            return None
        else:
            return tmpl

    @asyncify
    async def checkReleases(self):
        self.debug('Checking for new releases')
        newR = await pypicheck.newReleaseAvailable()
        if newR:
            self.systemTrayMessage('Galacteek',
                                   iNewReleaseAvailable(),
                                   timeout=8000)

    def showTasks(self):
        for task in self.pendingTasks:
            self.debug('Pending task: {}'.format(task))

    def onShowWindow(self):
        self.mainWindow.showMaximized()

    def restart(self):
        ensure(self.restartApp())

    async def restartApp(self):
        from galacteek.guientrypoint import appStarter
        pArgs = self.arguments()

        await self.exitApp()
        time.sleep(1)
        appStarter.startProcess(pArgs)

    def onExit(self):
        ensure(self.exitApp())

    async def shutdownScheduler(self):
        # It ain't that bad. STFS with dignity

        for stry in range(0, 12):
            try:
                async with async_timeout.timeout(2):
                    await self.scheduler.close()
            except asyncio.TimeoutError:
                log.warning('Timeout shutting down the scheduler (not fooled)')
                continue
            else:
                log.debug(f'Scheduler went down (try: {stry})')
                return

    async def exitApp(self):
        self._shuttingDown = True

        self.lock.release()

        self.mainWindow.stopTimers()

        try:
            self.systemTray.hide()
        except:
            pass

        try:
            await self.sqliteDb.close()
            await database.closeOrm()
        except:
            pass

        await self.stopIpfsServices()

        await self.loop.shutdown_asyncgens()
        await self.shutdownScheduler()
        await cancelAllTasks()

        await self.ethereum.stop()

        if self.ipfsClient:
            await self.ipfsClient.close()

        if self.ipfsd:
            self.ipfsd.stop()

        self.mainWindow.close()

        if self.debug:
            self.showTasks()

        self.tempDir.remove()
        self.quit()
Beispiel #30
0
class EthereumController:
    ethConnected = AsyncSignal(bool)
    ethNewBlock = AsyncSignal(str)

    def __init__(self, connParams, web3=None, loop=None,
                 executor=None, parent=None):
        self.app = QApplication.instance()

        self.loop = loop if loop else asyncio.get_event_loop()
        self.executor = executor if executor else \
            concurrent.futures.ThreadPoolExecutor(max_workers=4)

        self._params = connParams
        self._web3 = web3
        self._watchTask = None
        self._blockLatest = None
        self._stop = False
        self._loadedContracts = {}

    @property
    def web3(self):
        return self._web3

    @property
    def params(self):
        return self._params

    def changeParams(self, params):
        self._params = params

    def getWeb3(self):
        try:
            if self._params.provType == 'http':
                web3 = Web3(Web3.HTTPProvider(self.params.rpcUrl))

            elif self._params.provType == 'websocket':
                web3 = Web3(WebsocketProvider(self.params.rpcUrl))
                return web3

            web3.eth.defaultAccount = web3.eth.accounts[0]
            self._ns = ENS.fromWeb3(web3)
            return web3
        except Exception as err:
            log.debug('Cannot get Web3 connector: {}'.format(str(err)))
            return None

    async def _e(self, fn, *args, **kw):
        timeout = kw.pop('timeout', None)
        try:
            if isinstance(timeout, int):
                with async_timeout.timeout(timeout):
                    return await self.loop.run_in_executor(
                        self.executor, functools.partial(fn, *args, **kw))
            else:
                return await self.loop.run_in_executor(
                    self.executor, functools.partial(fn, *args, **kw))
        except asyncio.TimeoutError:
            log.debug('ETH Timeout')
        except ConnectionError:
            log.debug(
                'ETH Connection error while running fn: {0}'.format(fn))
            return None

    # API coroutines

    async def stop(self):
        self._stop = True

    async def start(self):
        if not self._web3:
            self._web3 = self.getWeb3()

        if self.web3 is None:
            log.debug('Web3 invalid')
            return

        if await self.connected():
            logUser.info('Ethereum: connected to {}'.format(
                self.params.rpcUrl))
            await self.ethConnected.emit(True)

            self._watchTask = ensure(self._e(self.watchTask))
            ensure(self.loadAutoDeploy())
            # await self.loadContracts()
        else:
            await self.ethConnected.emit(False)

    async def latestBlock(self):
        return await self._e(self.web3.eth.getBlock, 'latest')

    async def getBlock(self, block='latest'):
        return EthBlock(await self._e(self.web3.eth.getBlock, block))

    async def connected(self):
        return self.web3 and await self._e(self.web3.isConnected)

    async def ensAddress(self, name):
        return await self._e(self._ns.address, name)

    def watchTask(self):
        try:
            blkfilter = self.web3.eth.filter('latest')
        except Exception:
            # Node does not support filters ?
            return

        while not self._stop:
            time.sleep(5)

            for event in blkfilter.get_new_entries():
                self.eventLatestBlock(event)

    def eventLatestBlock(self, event):
        if isinstance(event, bytes):
            blockHex = w3.toHex(event)
            ensure(self.ethNewBlock.emit(blockHex))

    async def loadAutoDeploy(self):
        # Load contracts with autoload=True

        for lContract in listContracts():
            dList = getContractDeployments(lContract.name, autoload=True)

            for depl in dList:
                log.debug('Loading auto-deployment for contract {}'.format(
                    lContract.name))
                contract, cOperator = await self.loadLocalContractFromAddr(
                    lContract.name, depl['address'])

    async def loadContractFromAddress(self, address: str, abi):
        def load(addr, abi):
            contract = self.web3.eth.contract(
                address=addr,
                abi=abi
            )
            return ContractOperator(self, contract)
        return await self._e(load, address, abi)

    async def loadLocalContractFromDeployment(self, contractName: str):
        if not await self.connected():
            raise Exception('Not connected!')

        lContract = getContractByName(contractName)
        if not lContract:
            return

        deployments = getContractDeployments(contractName)
        if len(deployments) > 0:
            depl = deployments.pop()
            return await self.loadLocalContractFromAddr(
                contractName,
                depl['address']
            )

        return None, None

    async def loadLocalContractFromAddr(self, contractName: str,
                                        address: str):
        if not await self.connected():
            raise Exception('Not connected!')

        lContract = getContractByName(contractName)
        if not lContract:
            return None, None

        def load(_lContract, address):
            # Blocking call
            w3Contract = _lContract.web3Contract(self.web3)
            return w3Contract(address)

        contract = await self._e(load, lContract, address)
        if contract:
            operator = lContract.module.contractOperator(self, contract,
                                                         address, parent=self)
            self._loadedContracts.setdefault(contractName, {'default': None})
            self._loadedContracts[contractName][address] = operator

            if self._loadedContracts[contractName]['default'] is None:
                self._loadedContracts[contractName]['default'] = operator

            log.debug('Contract {0} loaded at {1}'.format(
                contractName, address))
            return lContract, operator
        else:
            log.debug('Contract invalid: {}'.format(address))
            return None, None

    def getDefaultLoadedOperator(self, contractName: str):
        loaded = self._loadedContracts.get(contractName)
        if loaded:
            return loaded.get('default')