Esempio n. 1
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)
Esempio n. 2
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()))
Esempio n. 3
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()
Esempio n. 4
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)
Esempio n. 5
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)
Esempio n. 6
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)
Esempio n. 7
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)
Esempio n. 8
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()
Esempio n. 9
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)
Esempio n. 10
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')
Esempio n. 11
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)
Esempio n. 12
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)
Esempio n. 13
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)
Esempio n. 14
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)
Esempio n. 15
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)
Esempio n. 16
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)
Esempio n. 17
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)
Esempio n. 18
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)
Esempio n. 19
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
Esempio n. 20
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
Esempio n. 21
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)
Esempio n. 22
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)
Esempio n. 23
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)
Esempio n. 24
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)
Esempio n. 25
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()
Esempio n. 26
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')
Esempio n. 27
0
class Peers(GService):
    changed = AsyncSignal()
    peerAdded = AsyncSignal(PeerIdentityCtx)
    peerNew = AsyncSignal(PeerIdentityCtx)
    peerAuthenticated = AsyncSignal(PeerIdentityCtx)
    peerModified = AsyncSignal(PeerIdentityCtx)
    peerDidModified = AsyncSignal(PeerIdentityCtx, bool)

    # Unused now
    peerLogout = AsyncSignal(str)

    def __init__(self, ctx):
        super().__init__()

        self.app = QApplication.instance()
        self.ctx = ctx
        self.lock = aiorwlock.RWLock()
        self.evStopWatcher = asyncio.Event()
        self._byPeerId = collections.OrderedDict()
        self._byHandle = collections.OrderedDict()
        self._didGraphLStatus = []
        self._didAuthInp = {}
        self._pgScanCount = 0

        self.peerAuthenticated.connectTo(self.onPeerAuthenticated)

    @property
    def pgScanCount(self):
        return self._pgScanCount

    @property
    def byPeerId(self):
        return self._byPeerId

    @property
    def byHandle(self):
        return self._byHandle

    @property
    def peersIds(self):
        return self.byPeerId.keys()

    @property
    def peersCount(self):
        return len(self.peersHandles)

    @property
    def peersHandles(self):
        return [handle for handle in self.byHandle.keys()]

    async def onPeerLogout(self, peerId):
        # Not used anymore
        pass

    @ipfsOp
    async def watchNetworkGraph(self, ipfsop):
        profile = ipfsop.ctx.currentProfile

        # First scan
        await self.scanNetworkGraph()

        if profile.dagNetwork:
            profile.dagNetwork.dagUpdated.connectTo(self.networkGraphChanged)

    async def networkGraphChanged(self, dagCid):
        log.debug(
            f'Network graph moved to CID: {dagCid} '
            f'(graph scan run: {self.pgScanCount})'
        )

        await self.scanNetworkGraph()

    @ipfsOp
    async def scanNetworkGraph(self, ipfsop):
        profile = ipfsop.ctx.currentProfile

        async with profile.dagNetwork.read() as ng:
            for peerId, peerHandles in ng.d['peers'].items():
                for handle, hData in peerHandles.items():
                    sHandle = SpaceHandle(handle)
                    did = hData.get('did')

                    if peerId == ipfsop.ctx.node.id:
                        if did != profile.userInfo.personDid:
                            # Don't load inactive IPIDs
                            continue

                    log.debug(
                        f'scanNetworkGraph: processing {handle} ({did})')

                    if not sHandle.valid:
                        continue

                    # Is it in the model already ?
                    if self.app.peersTracker.model.didRegistered(did):
                        continue

                    if did in self._didGraphLStatus:
                        await ipfsop.sleep()
                        continue

                    ensure(
                        self.loadDidFromGraph(ipfsop, peerId, did, sHandle))
                    self._didGraphLStatus.append(did)

                    await ipfsop.sleep(0.1)

                await ipfsop.sleep(0.05)

        self._pgScanCount += 1

    async def loadDidFromGraph(self, ipfsop, peerId: str, did: str,
                               sHandle: str):
        loadTimeout = cGet('peers.didLoadTimeout')
        loadAttempts = cGet('peers.didLoadAttempts')

        peersService = ipfsop.ctx.pubsub.byTopic(TOPIC_PEERS)

        for attempt in range(0, max(2, loadAttempts)):
            ipid = await self.app.ipidManager.load(
                did,
                track=True,
                timeout=loadTimeout,
                localIdentifier=(peerId == ipfsop.ctx.node.id)
            )

            if not ipid:
                log.debug(f'Cannot load IPID: {did}, attempt {attempt}')
                await ipfsop.sleep(cGet('peers.didFail.sleepInterval'))
                continue
            else:
                break

        if not ipid:
            log.debug(f'Cannot load IPID: {did}, bailing out')
            self._didGraphLStatus.remove(did)
            return False

        piCtx = PeerIdentityCtx(
            self.ctx,
            sHandle.peer,
            str(sHandle),
            ipid,
            validated=True,
            authenticated=True
        )

        ipid.sChanged.connectTo(partialEnsure(
            self.onPeerDidModified, piCtx))
        piCtx.sStatusChanged.connectTo(partialEnsure(
            self.peerModified.emit, piCtx))

        async with self.lock.writer_lock:
            self._byHandle[str(sHandle)] = piCtx

            if piCtx.peerId not in self._byPeerId:
                self._byPeerId[piCtx.peerId] = piCtx

        log.debug(f'Loaded IPID from graph: {did}')

        await self.peerAdded.emit(piCtx)
        await ipfsop.sleep(1)

        await peersService.sendIdentReq(piCtx.peerId)

        self._didGraphLStatus.remove(did)

        return True

    @ipfsOp
    async def registerFromIdent(self, ipfsop, sender, iMsg):
        profile = ipfsop.ctx.currentProfile

        log.debug(f'registerFromIdent ({iMsg.peer}): '
                  f'DID: {iMsg.personDid}, handle: {iMsg.iphandle}')

        try:
            inGraph = await profile.dagNetwork.byDid(iMsg.personDid)
        except Exception:
            # network dag not ready ..
            log.debug(f'registerFromIdent {iMsg.personDid}: '
                      'network DAG not loaded yet ?')
            return

        if not inGraph:
            if isinstance(iMsg, PeerIdentMessageV4) and \
                    sender != ipfsop.ctx.node.id:
                pubKeyPem = await ipfsop.rsaPubKeyCheckImport(
                    iMsg.defaultRsaPubKeyCid)

                if not pubKeyPem:
                    log.debug(
                        f'Invalid RSA pub key .. {iMsg.defaultRsaPubKeyCid}')
                    return

                sigBlob = await ipfsop.catObject(iMsg.pssSigCurDid)
                if sigBlob is None:
                    log.debug(
                        f'Cannot get pss SIG {iMsg.pssSigCurDid}')
                    return

                if not await ipfsop.ctx.rsaExec.pssVerif(
                    iMsg.personDid.encode(),
                    sigBlob,
                    pubKeyPem
                ):
                    log.debug(f'Invalid PSS sig for peer {sender}')
                    return
                else:
                    log.debug(f'Valid PSS sig for {sender} !')

            peerValidated = False
            personDid = iMsg.personDid

            if not ipidFormatValid(personDid):
                log.debug('Invalid DID: {}'.format(personDid))
                return

            inProgress = self._didAuthInp.get(personDid, False)

            if inProgress is True:
                log.debug(f'registerFromIdent {iMsg.personDid}: '
                          f'authentication in progress')
                return

            self._didAuthInp[personDid] = True

            try:
                mType, stat = await self.app.rscAnalyzer(iMsg.iphandleqrpngcid)
            except Exception:
                log.debug('Cannot stat QR: {}'.format(iMsg.iphandleqrpngcid))
                self._didAuthInp[personDid] = False
                return
            else:
                statInfo = StatInfo(stat)

                if not statInfo.valid or statInfo.dataLargerThan(
                        kilobytes(512)) or not mType or not mType.isImage:
                    log.debug('Invalid stat for QR: {}'.format(
                        iMsg.iphandleqrpngcid))
                    self._didAuthInp[personDid] = False
                    return

                if not await self.validateQr(
                        iMsg.iphandleqrpngcid, iMsg) is True:
                    log.debug('Invalid QR: {}'.format(iMsg.iphandleqrpngcid))
                    peerValidated = False
                    self._didAuthInp[personDid] = False
                    return
                else:
                    log.debug('Ident QR {qr} for {peer} seems valid'.format(
                        qr=iMsg.iphandleqrpngcid, peer=iMsg.peer))
                    peerValidated = True

                await ipfsop.ctx.pin(iMsg.iphandleqrpngcid)

            # Load the IPID

            loadAttempts = cGet('peers.didLoadAttempts')

            for attempt in range(0, loadAttempts):
                ipid = await self.app.ipidManager.load(
                    personDid,
                    localIdentifier=(iMsg.peer == ipfsop.ctx.node.id)
                )

                if ipid:
                    break

            if not ipid:
                log.debug(f'Cannot load DID: {personDid}')
                self._didAuthInp[personDid] = False
                return

            async with self.lock.writer_lock:
                piCtx = self.getByHandle(iMsg.iphandle)

                if not piCtx:
                    log.debug(f'Creating new PeerIdentityCtx for '
                              f'{iMsg.iphandle} ({personDid})')

                    piCtx = PeerIdentityCtx(
                        self.ctx, iMsg.peer, iMsg.iphandle,
                        ipid,
                        validated=peerValidated
                    )

                    ipid.sChanged.connectTo(partialEnsure(
                        self.onPeerDidModified, piCtx))
                    piCtx.sStatusChanged.connectTo(partialEnsure(
                        self.peerModified.emit, piCtx))

                piCtx.ident = iMsg

                if not piCtx.authFailedRecently:
                    ensure(self.didPerformAuth(piCtx, iMsg))

                self._byHandle[iMsg.iphandle] = piCtx
        else:
            # This peer is already registered in the network graph
            # What we ought to do here is just to refresh the DID document

            async with self.lock.writer_lock:
                piCtx = self.getByHandle(iMsg.iphandle)

                if piCtx:
                    self._byPeerId[piCtx.peerId] = piCtx

                    piCtx.ident = iMsg
                    await piCtx.ipid.refresh()

                    await self.peerModified.emit(piCtx)

        await self.changed.emit()

    @ipfsOp
    async def didPerformAuth(self, ipfsop, piCtx, identMsg):
        success = False
        ipid = piCtx.ipid
        idToken = None

        if isinstance(identMsg, PeerIdentMessageV4):
            idToken = identMsg.identToken

        if not ipid.local:
            # DID Auth
            for attempt in range(0, 1):
                log.debug('DID auth: {did} (attempt: {a})'.format(
                    did=ipid.did, a=attempt))

                if not await self.app.ipidManager.didAuthenticate(
                        ipid, piCtx.peerId, token=idToken):
                    log.debug('DID auth failed for DID: {}'.format(ipid.did))
                    break
                else:
                    success = True
                    log.debug('DID auth success for DID: {}'.format(ipid.did))
                    piCtx._authenticated = True

                    # Authenticated
                    await self.peerAuthenticated.emit(piCtx)

                    # Peering
                    await self.peeringAdd(piCtx)
                    break
        else:
            # We control this DID
            piCtx._authenticated = True
            await self.peerAuthenticated.emit(piCtx)

        if not success:
            piCtx.failedAuthAttempt()

        self._didAuthInp[ipid.did] = False

    @ipfsOp
    async def onPeerAuthenticated(self, ipfsop, piCtx: PeerIdentityCtx):
        """
        DID auth was successfull for a peer, store the handle
        and DID in the graph, and create the initial IPLD
        link for the DID document (diddoc)
        """

        profile = ipfsop.ctx.currentProfile
        did = piCtx.ipid.did
        handle = str(piCtx.spaceHandle)

        async with profile.dagNetwork as g:
            pData = g.root['peers'].setdefault(piCtx.peerId, {})

            if handle in pData:
                # This handle is already registered for this peer
                # This can happen if a peer created 2 IPIDs with
                # the same handle after losing the profile's data

                log.debug(f'{handle} already in the graph, overwriting')

            pData[handle] = {
                'did': did,
                'diddoc': {},
                'dateregistered': utcDatetimeIso(),
                'datedidauth': utcDatetimeIso(),
                'datelastseen': utcDatetimeIso(),
                'flags': 0
            }

            # Link the DID document in the graph
            if piCtx.ipid.docCid:
                pData[handle]['diddoc'] = g.ipld(piCtx.ipid.docCid)

            log.debug(f'Authenticated {handle} ({did}) in the peers graph')

    async def peeringAdd(self, piCtx: PeerIdentityCtx):
        """
        Register the peer in go-ipfs Peering.Peers
        This way we make sure galacteek peers are always peered well
        """

        if self.app.ipfsd:
            await self.app.ipfsd.ipfsConfigPeeringAdd(piCtx.peerId)

    @ipfsOp
    async def validateQr(self, ipfsop, qrCid, iMsg):
        validCodes = 0
        try:
            codes = await self.app.rscAnalyzer.decodeQrCodes(qrCid)

            if not codes:
                # QR decoder not available, or invalid QR
                log.debug(f'validateQr({qrCid}): no codes found')
                return False

            if len(codes) not in range(2, 4):
                log.debug(f'validateQr({qrCid}): invalid code range')
                return False

            for code in codes:
                if isinstance(code, IPFSPath):
                    if code.isIpns:
                        peerKey = IPFSPath(joinIpns(iMsg.peer))

                        if str(code) == joinIpns(iMsg.peer):
                            validCodes += 1
                        elif code == peerKey:  # IPFSPath comp
                            validCodes += 1

                    if code.isIpfs:
                        computed = await ipfsop.hashComputeString(
                            iMsg.iphandle)

                        if not computed:
                            log.debug(f'validateQr({qrCid}): compute failed')
                            continue

                        if computed['Hash'] == stripIpfs(code.objPath):
                            validCodes += 1
                elif isinstance(code, str) and ipidFormatValid(code) and \
                        code == iMsg.personDid:
                    validCodes += 1
        except Exception as e:
            log.debug('QR decode error: {}'.format(str(e)))
            return False
        else:
            # Just use 3 min codes here when we want the DID to be required
            log.debug(f'validateQr({qrCid}): found {validCodes} codes')
            return validCodes >= 2

        log.debug(f'validateQr({qrCid}): invalid (out)')
        return False

    @ipfsOp
    async def onPeerDidModified(self, ipfsop, piCtx: PeerIdentityCtx,
                                didCid: str):
        log.debug('DID modified for peer: {}'.format(piCtx.peerId))

        profile = ipfsop.ctx.currentProfile
        await profile.dagNetwork.didUpdateObj(piCtx.ipid.did, didCid)

        await self.peerDidModified.emit(piCtx, True)

    async def onUnresponsivePeer(self, peerId):
        # Unused
        piCtx = self.getByPeerId(peerId)
        if piCtx:
            log.debug('{} unresponsive ..'.format(peerId))
            await self.unregister(peerId)

    async def init(self):
        pass

    async def stop(self):
        pass

    async def on_start(self):
        pass

    @GService.task
    async def peersWatcherTask(self):
        interval = cGet('peers.watcherTask.sleepInterval')

        while not self.should_stop:
            await asyncio.sleep(interval)

            await self.peersWatcherRun()

    @ipfsOp
    async def peersWatcherRun(self, ipfsop):
        if ipfsop.noPeers:
            # No need
            log.debug('Peers watch: no peers, skipping')
            return

        async with self.lock.reader_lock:
            for peerId, piCtx in self._byPeerId.items():
                if not piCtx.identLast:
                    continue

                if not piCtx.pingedRecently:
                    await piCtx.watch(ipfsop)

    def getByPeerId(self, peerId):
        return self._byPeerId.get(peerId, None)

    def getByHandle(self, handle):
        return self._byHandle.get(handle, None)

    def peerRegistered(self, peerId):
        return peerId in self.peersIds

    def __str__(self):
        return 'Galacteek peers registered: {0}'.format(self.peersCount)
Esempio n. 28
0
class DIDTower:
    didServiceOpenRequest = AsyncSignal(str, str, dict)
    didServiceObjectOpenRequest = AsyncSignal(str,
                                              str,
                                              str,
                                              _id='didObjectOpen')
Esempio n. 29
0
class URLSchemesTower(GObject):
    qMappingsChanged = AsyncSignal()
Esempio n. 30
0
    def __init__(self, bmAddr: str, mailDirPath: Path):
        self.bmAddress = bmAddr
        self.path = mailDirPath
        self.maildir = Maildir(str(self.path))

        self.sNewMessage = AsyncSignal(str, EmailMessage)