async def createPyramid(self, ipfsop, pyramidName, category, ipnsKeyName, description, ipnsLifetime, extra): try: logUser.info( 'Multihash pyramid {pyr}: generating IPNS key ...'.format( pyr=pyramidName)) ipnsKey = await ipfsop.keyGen(ipnsKeyName) if ipnsKey: self.marks.pyramidNew(pyramidName, category, self.iconCid, ipnskey=ipnsKey['Id'], lifetime=ipnsLifetime, type=self.pyramidType, description=description, extra=extra) logUser.info( 'Multihash pyramid {pyr}: created'.format(pyr=pyramidName)) else: raise Exception('Could not generate IPNS key') except aioipfs.APIError as err: messageBox('IPFS error while creating pyramid: {}'.format( err.message)) except Exception as err: messageBox('Error creating pyramid: {}'.format(str(err)))
async def scan(self, ipfsop): log.debug('Scanning hashmarks MFS library') listing = await ipfsop.filesList(self.profile.pathHMarksLibrary) for file in listing: await ipfsop.sleep() uid = file['Name'] marksHash = file['Hash'] if marksHash in self._loaded: continue fPath = os.path.join(self.profile.pathHMarksLibrary, uid) try: marksCiphered = await self.loadFromPath(fPath) marks = IPFSMarks(None, data=marksCiphered.root) self.hashmarksLoaded.emit(uid, marks) self._loaded.append(marksHash) except BaseException as err: log.debug('Could not load hashmarks from CID {0}: {1}'.format( marksHash, str(err))) else: logUser.info('Loaded hashmarks from peer {0}'.format(uid)) await asyncio.sleep(1)
async def encodeClipboardItems(self, ipfsop, encrypt=False): logUser.info('QR: encoding clipboard stack (encrypted: {enc})'.format( enc=encrypt)) encoder = IPFSQrEncoder() for item in self.tracker.items: if item.ipfsPath.valid: logUser.info('QR: adding item: {item}'.format( item=str(item.ipfsPath))) encoder.add(str(item.ipfsPath)) logUser.info('QR: encoding ..') qrName = 'ipfsqr.{}.png'.format(int(time.time())) imgPath = self.app.tempDir.filePath(qrName) try: image = await encoder.encodeAll(loop=self.app.loop, executor=self.app.executor) image.save(imgPath) except: # :-/ logUser.info('QR: encoding error ..') return else: logUser.info('QR: encoding successfull!') if ipfsop.ctx.currentProfile: await ipfsop.ctx.currentProfile.qrImageEncoded.emit( encrypt, imgPath)
async def dropEventLocalFile(self, ipfsop, url): """ Handle a drop event with a file:// URL """ maxFileSize = megabytes(64) try: path = url.toLocalFile() fileInfo = QFileInfo(path) if fileInfo.isFile(): file = QFile(path) if file.open(QIODevice.ReadOnly): size = file.size() if size and size < maxFileSize: logUser.info('Importing file: {0}'.format(path)) entry = await ipfsop.addPath(path) if entry: self.tracker.clipboardProcess(entry['Hash']) file.close() if fileInfo.isDir(): # Don't check for directory size async def entryAdded(entry): logUser.info('{path}: imported'.format( path=entry.get('Name'))) entry = await ipfsop.addPath(path, callback=entryAdded) if entry: self.tracker.clipboardProcess(entry['Hash']) except Exception: pass
async def openBlock(self, ipfsop, pathRef, mimeType=None): """ Open the raw block referenced by pathRef XXX: needs improvements, this only works for images for now """ ipfsPath = None if isinstance(pathRef, IPFSPath): ipfsPath = pathRef elif isinstance(pathRef, str): ipfsPath = IPFSPath(pathRef) else: raise ValueError('Invalid input value') if not ipfsPath.valid: return False blockStat = await ipfsop.waitFor(ipfsop.client.block.stat(pathRef), 5) if not blockStat or not isinstance(blockStat, dict): messageBox('Block is bad') blockSize = blockStat.get('Size') if blockSize > megabytes(16): return logUser.info('Block {path}: Opening'.format(path=pathRef)) blockData = await ipfsop.client.block.get(pathRef) rscPath = ipfsPath.objPath rscShortName = rscPath mimeType = await detectMimeTypeFromBuffer(blockData[:1024]) if mimeType and mimeType.valid: logUser.info('Block: {path} ({type}): opening'.format( path=rscPath, type=str(mimeType))) else: return messageBox(iResourceCannotOpen(rscPath)) if mimeType.isImage: tab = ImageViewerTab(self.app.mainWindow) ensure(tab.view.showImage(rscPath, fromBlock=True)) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('image/x-generic'), tooltip=rscPath, current=True)
async def handleRequest(self, ipfsop, request, uid): rUrl = request.requestUrl() domain = rUrl.host() if not domain or len(domain) > 512 or not domainValid(domain): logUser.info('EthDNS: invalid domain request') return self.urlInvalid(request) path = await self.urlProxiedPath(rUrl) if path and path.valid: return await self.fetchFromPath(ipfsop, request, path, uid) else: logUser.info( 'EthDNS: {domain} resolve failed'.format(domain=domain)) return self.urlNotFound(request)
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 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())
async def handleRequest(self, request): rUrl = request.requestUrl() if not rUrl.isValid(): return self.urlInvalid(request) domain = rUrl.host() uPath = rUrl.path() if not domain or len(domain) > 512: logUser.info('ENS: invalid domain request') return self.urlInvalid(request) logUser.info('ENS: resolving {0}'.format(domain)) path = await ensContentHash(domain, sslverify=self.app.sslverify) if path and path.valid: await ensDomainResolved.emit(domain, path) sPath = path.child(uPath) if uPath else path logUser.info('ENS: {domain} resolved to {res}'.format( domain=domain, res=sPath.ipfsUrl)) return request.redirect(QUrl(sPath.ipfsUrl)) else: logUser.info('ENS: {domain} resolve failed'.format(domain=domain)) request.fail(QWebEngineUrlRequestJob.UrlInvalid)
def onMovieError(self, err): logUser.info(f'{self.clip.fileName()}: {self.clip.lastErrorString()}')
def userLogInfo(self, msg): logUser.info('profile ({0}): {1}'.format(self.name, msg))
async def blogPost(self, ipfsop, title, msg, category=None, tags=None, author=None): async with self.edag: sHandle = self.profile.userInfo.spaceHandle username = sHandle.human uid = str(uuid.uuid4()) postName = titleToPostName(title.strip().lower()) exEntries = await self.blogEntries() if not postName or postName in exEntries: raise Exception('A blog post with this name already exists') now = datetime.now(timezone.utc) postObject = { 'blogpost': { 'authordid': self.profile.userInfo.personDid, 'body': msg, 'title': title, 'uuid': uid, 'postname': postName, # name of the post's DAG node 'tags': tags if tags else [], 'category': None, 'author': author if author else username, 'date_published': isoformat(now), 'date_modified': isoformat(now) } } # Create the DAG node for this post # Its view will be rendered later on self.dagBlog[postName] = {POST_NODEKEY: postObject} await ipfsop.sleep(2) await self.update() await ipfsop.sleep(1) result = await self.dagUser.resolve( posixIpfsPath.join(BLOG_NODEKEY, postName, 'view')) resolved = IPFSPath(result, autoCidConv=True) if isinstance(tags, list) and resolved.isIpfsRoot and resolved.valid: # Register the post by tag async with self.edag as dag: byTags = dag.root[BLOG_NODEKEY][TAGS_NODEKEY] for tag in tags: if tag is None: continue planet, ptag = tag.split('#') if not planet or not ptag: continue planet = planet.replace('@', '') byTags.setdefault(planet, {}) byTags[planet].setdefault(ptag, {'_posts': []}) byTags[planet][ptag]['_posts'].append({ 'name': postName, 'title': title, 'view': { '/': stripIpfs(str(resolved)) } }) byTags[planet][ptag]['index.html'] = await self.renderLink( 'usersite/bytag.html', contained=True, tag=tag, tagposts=byTags[planet][ptag]['_posts']) logUser.info('Your blog post is online') return True
async def open(self, ipfsop, pathRef, mimeType=None, openingFrom=None, pyramidOrigin=None, minWebProfile='ipfs', schemePreferred=None, tryDecrypt=False, fromEncrypted=False, editObject=False, pin=False, burnAfterReading=False, useWorkspace=None): """ Open the resource referenced by rscPath according to its MIME type :param pathRef: IPFS object's path (can be str or IPFSPath) :param openingFrom str: UI component making the open request :param minWebProfile str: Minimum Web profile to use :param tryDecrypt bool: Try to decrypt the object or not :param editObject bool: Set Text Editor in edit mode for text files :param MIMEType mimeType: MIME type """ ipfsPath = None statInfo = None if isinstance(pathRef, IPFSPath): ipfsPath = pathRef elif isinstance(pathRef, str): url = QUrl(pathRef) if isEnsUrl(url): return self.openEnsUrl(url, pin=pin) if isUrlSupported(url): return self.openUrl(url) ipfsPath = IPFSPath(pathRef, autoCidConv=True) else: raise ValueError(f'Invalid input value: {pathRef}') if not ipfsPath.valid: return False rscPath = ipfsPath.objPath if self.app.mainWindow.pinAllGlobalChecked: ensure( ipfsop.ctx.pinner.queue(rscPath, False, None, qname='default')) rscShortName = ipfsPath.shortRepr() if ipfsPath.isIpfs: # Try to reuse metadata from the multihash store rscMeta = await self.app.multihashDb.get(rscPath) if rscMeta: cachedMime = rscMeta.get('mimetype') cachedStat = rscMeta.get('stat') if cachedMime: mimeType = MIMEType(cachedMime) if cachedStat: statInfo = StatInfo(cachedStat) wsSwitch = True mtConfig = self.cObjectOpener.mimeTypes.default if mimeType is None: ltBefore = loopTime() mimeType = await detectMimeType(rscPath) spent = loopTime() - ltBefore if spent > mtConfig.slowObjectTimer: # We won't switch workspaces wsSwitch = False if mimeType and mimeType.valid: logUser.info('{path} ({type}): opening'.format(path=rscPath, type=str(mimeType))) else: logUser.info(iResourceCannotOpen(rscPath)) return hashmark = await hashmarksByPath(rscPath) if hashmark and not useWorkspace: await hashmark._fetch_all() useWorkspace = self.app.mainWindow.stack.wsHashmarkTagRulesRun( hashmark) if mimeType == mimeTypeDagUnknown: indexPath = ipfsPath.child('index.html') stat = await ipfsop.objStat(indexPath.objPath, timeout=8) if stat: # Browse the index return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin).browseFsPath( indexPath, schemePreferred=schemePreferred) # Otherwise view the DAG view = DAGViewer(rscPath, self.app.mainWindow) self.app.mainWindow.registerTab(view, iDagViewer(), current=True, icon=getIcon('ipld.png'), tooltip=rscPath) return if mimeType.type == 'application/octet-stream' and not fromEncrypted: # Try to decode it with our key if it's a small file if statInfo is None: statInfo = StatInfo(await ipfsop.objStat(rscPath, timeout=5)) profile = ipfsop.ctx.currentProfile if profile and statInfo.valid and \ (statInfo.dataSmallerThan(megabytes(8)) or tryDecrypt): data = await ipfsop.catObject(ipfsPath.objPath, timeout=30) if not data: # XXX return decrypted = await profile.rsaAgent.decrypt(data) if decrypted: # # "Good evening, 007" # # Create a short-lived IPFS offline file (not announced) # with the decrypted content and open it # logUser.info('{path}: RSA OK'.format(path=rscPath)) # This one won't be announced or pinned entry = await ipfsop.addBytes(decrypted, offline=True, pin=False) if not entry: logUser.info( '{path}: cannot import decrypted file'.format( path=rscPath)) return # Open the decrypted file return ensure( self.open(entry['Hash'], fromEncrypted=True, burnAfterReading=True)) else: logUser.debug( '{path}: decryption impossible'.format(path=rscPath)) if mimeType.isText or editObject: tab = TextEditorTab(parent=self.app.mainWindow, editing=editObject, pyramidOrigin=pyramidOrigin) tab.editor.display(ipfsPath) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('text/x-generic'), tooltip=rscPath, current=True, workspace=WS_EDIT) if mimeType.isImage or mimeType.isAnimation: tab = ImageViewerTab(self.app.mainWindow) ensure(tab.view.showImage(rscPath)) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('image/x-generic'), tooltip=rscPath, current=True) if mimeType.isVideo or mimeType.isAudio: tab = self.app.mainWindow.getMediaPlayer() if tab: tab.playFromPath(rscPath) return if mimeType == 'application/pdf' and 0: # not usable yet tab = WebTab(self.app.mainWindow) tab.attach(DWebView(page=PDFViewerPage(rscPath))) self.objectOpened.emit(ipfsPath) return self.app.mainWindow.registerTab( tab, rscShortName, icon=getMimeIcon('application/pdf'), tooltip=rscPath, current=True) if mimeType.isHtml: self.objectOpened.emit(ipfsPath) return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin, workspace=useWorkspace, wsSwitch=wsSwitch).browseFsPath( ipfsPath, schemePreferred=schemePreferred) if mimeType.isDir: indexPath = ipfsPath.child('index.html') stat = await ipfsop.objStat(indexPath.objPath, timeout=8) if stat: self.objectOpened.emit(ipfsPath) return self.app.mainWindow.addBrowserTab( minProfile=minWebProfile, pinBrowsed=pin, workspace=useWorkspace, wsSwitch=wsSwitch).browseFsPath( ipfsPath, schemePreferred=schemePreferred) else: return await self.app.mainWindow.exploreIpfsPath(ipfsPath) if mimeType.isBitTorrent: wStack = self.app.mainWindow.stack with wStack.workspaceCtx(WS_FILES, show=True) as ws: btClient = await ws.getTorrentClient() return await btClient.addTorrentFromIpfs(ipfsPath) if openingFrom in ['filemanager', 'qa', 'didlocal']: await self.needUserConfirm.emit(ipfsPath, mimeType, True) else: await self.needUserConfirm.emit(ipfsPath, mimeType, False)
async def entryAdded(entry): logUser.info('{path}: imported'.format( path=entry.get('Name')))