def dropEvent(self, event): mimeData = event.mimeData() if mimeData is None: return if mimeData.hasUrls(): for url in mimeData.urls(): if not url.isValid(): continue if url.scheme() == 'file' and url.isValid(): self.fileDropped.emit(url) else: path = IPFSPath(url.toString(), autoCidConv=True) if path.valid: self.ipfsObjectDropped.emit(path) elif mimeData.hasText(): text = mimeData.text() path = IPFSPath(text, autoCidConv=True) if path.valid: self.ipfsObjectDropped.emit(path) event.acceptProposedAction()
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 or not domainValid(domain): log.info('EthDNS: invalid domain request') return self.urlInvalid(request) pathRaw = await self.ethResolver.resolveEnsDomain(domain) path = IPFSPath(pathRaw, autoCidConv=True) if path and path.valid: await ensDomainResolved.emit(domain, path) sPath = path.child(uPath) if uPath else path log.debug('EthDNS: {domain} resolved to {res}'.format( domain=domain, res=sPath.ipfsUrl)) return request.redirect(QUrl(sPath.dwebUrl)) else: log.info('EthDNS: {domain} resolve failed'.format(domain=domain)) return self.urlNotFound(request)
async def startVideoCall(self, ipfsop, rvTopic): rootPath = IPFSPath(ipfsop.ctx.resources['app-videocall']['Hash']) offerPath = rootPath.child('offer.html') offerPath.fragment = rvTopic tab = self.app.mainWindow.addBrowserTab() tab.browseFsPath(offerPath)
async def ipIdentifierInit(self, ipfsop, ipid: IPIdentifier): # Register the blog as an IP service on the DID blogPath = IPFSPath(joinIpns(self.keyRootId)).child('blog') await ipid.addServiceRaw( { 'id': ipid.didUrl(path='/blog'), 'type': IPService.SRV_TYPE_DWEBBLOG, 'serviceEndpoint': blogPath.ipfsUrl }, publish=False) # Register the Atom feed as an IP service on the DID feedPath = IPFSPath(joinIpns(self.keyRootId)).child('dfeed.atom') await ipid.addServiceRaw( { 'id': ipid.didUrl(path='/feed'), 'type': IPService.SRV_TYPE_ATOMFEED, 'serviceEndpoint': feedPath.ipfsUrl, 'description': 'Dweb Atom feed' }, publish=False) await ipid.addServiceCollection('default') entry = await self.ctx.app.importQtResource('/share/icons/helmet.png') if entry: defAvatar = IPFSPath(entry['Hash']) await ipid.avatarSet(defAvatar.objPath)
async def sendMessage(self, ipfsop, msgText): links = [] words = msgText.split() def addPath(path): oPath = str(path) if len(links) < 4 and oPath not in links: links.append(oPath) for word in words: path = IPFSPath(word, autoCidConv=True) if path.valid: addPath(path) matches = re.findall(r'"([^"]*)"', msgText) for match in matches: path = IPFSPath(match, autoCidConv=True) if path.valid: addPath(path) await self.psService.send(await ChatRoomMessage.make( self.psService.jwsTokenCid, command='MSGMARKDOWN', params=[msgText], links=links))
def test_cids(self): assert cidValid( 'bafkriqa2hf4uwu2dd2nlynbwr3kietn2ywowyzaxperhtmhmfsi5n22yv5zptvfr4o3bhicyshbmdil2qif47au4wmr4ikm3egpfvmuzpfcyc' ) assert cidValid( 'bafkreihszin3nr7ja7ig3l7enb7fph6oo2zx4tutw5qfaiw2kltmzqtp2i') assert cidValid( 'bafkrgqaohz2sgsv4nd2dpcugwp2lgkqzrorqdbc3btlokaig5b2divyazrtghkdmd2qslxc6sk7bpsmptihylsu5l5mv3mqbf56mgvyzixasg' ) assert cidValid( 'bafkr2qffyprvhimferhyfkg6af63marfiplxn6euhncxwy3izaefj3g73ilqlzo5kfgvbbyrnwwmw7nm4wwufkyxfp7htiwabn5b5hdw4rvvk' ) p = IPFSPath( 'bafkr2qffyprvhimferhyfkg6af63marfiplxn6euhncxwy3izaefj3g73ilqlzo5kfgvbbyrnwwmw7nm4wwufkyxfp7htiwabn5b5hdw4rvvk' ) assert p.valid p = IPFSPath( 'bafk4bzacia2aon3c3n5pkaemoij7sm4q4dt3omcr5tkc2sptmm6ifsnpbsx7h77xj4e4pjx7olxtbgsyjsg35mgl3j2q7mel3kuiz2v7ztngkdbv' ) assert p.valid p = IPFSPath( '/ipfs/bafykbzaced4xstofs4tc5q4irede6uzaz3qzcdvcb2eedxgfakzwdyjnxgohq/' ) assert p.valid cid = getCID( 'bafykbzaced4xstofs4tc5q4irede6uzaz3qzcdvcb2eedxgfakzwdyjnxgohq') m = multihash.decode(cid.multihash) assert m.name == 'blake2b-256'
async def registerHashmark(self, ipfsop, mark, button=None, maxIconSize=512 * 1024): """ Register an object in the toolbar with an associated hashmark """ await mark._fetch_all() ipfsPath = IPFSPath(mark.path) if not ipfsPath.valid: return result = await self.analyzer(ipfsPath) if result is None: return mimeType, rscStat = result mIcon = mark.icon.path if mark.icon else None icon = None if mIcon and IPFSPath(mIcon).valid: stat = await ipfsop.objStat(mIcon) statInfo = StatInfo(stat) # Check filesize from the stat on the object if statInfo.valid and statInfo.dataSmallerThan(maxIconSize): icon = await getIconFromIpfs(ipfsop, mIcon) if icon is None: icon = getIcon('unknown-file.png') else: if not await ipfsop.isPinned(mIcon): log.debug('Pinning icon {0}'.format(mIcon)) await ipfsop.ctx.pin(mIcon) elif mimeType: icon = await self.findIcon(ipfsop, ipfsPath, rscStat, mimeType) else: icon = getIcon('unknown-file.png') if not button: button = HashmarkToolButton(mark, parent=self, icon=icon) action = self.addWidget(button) button.setToolTip(iHashmarkInfoToolTipShort(mark)) button.clicked.connect(lambda: ensure( self.app.resourceOpener.open(str(ipfsPath), openingFrom='qa'))) button.deleteRequest.connect( lambda: ensure(self.onDelete(button, action))) else: if icon: button.setIcon(icon) button.setToolTip(iHashmarkInfoToolTipShort(mark))
async def showImage(self, ipfsop, imgPath, fromBlock=False, timeout=10): try: if fromBlock is True: imgData = await ipfsop.waitFor( ipfsop.client.block.get(imgPath), timeout) else: imgData = await ipfsop.waitFor(ipfsop.catObject(imgPath), timeout) if not imgData: raise Exception('Failed to load image') mimeType, statInfo = await self.app.rscAnalyzer(IPFSPath(imgPath)) if mimeType is None: raise Exception('Failed to load image') stat = StatInfo(statInfo) if mimeType.isAnimation: fp = self.app.tempDir.filePath(stat.cid) tmpFile = QFile(fp) if not tmpFile.exists() and tmpFile.open(QFile.ReadWrite): tmpFile.write(imgData) tmpFile.close() self.clip.stop() self.clip.setFileName(fp) self.labelImage.setMovie(self.clip) self.clip.frameChanged.connect(self.onMovieFrameChanged) self.clip.error.connect(self.onMovieError) self.clip.start() self.labelImage.adjustSize() else: img = QImage() img.loadFromData(imgData) self.image = img self.labelImage.setPixmap(QPixmap.fromImage(self.image)) self.labelImage.adjustSize() self.resizePixmap() if self.qrDecoder: # See if we have any QR codes in the image urls = self.qrDecoder.decode(imgData) if urls: self.qrCodesPresent.emit(urls) self.labelImage.adjustSize() self.currentImgPath = IPFSPath(imgPath) self.imageLoaded.emit(self.currentImgPath, mimeType) except Exception: logUser.debug('Failed to load image: {path}'.format(path=imgPath)) messageBox(iImageCannotLoad(imgPath))
async def urlProxiedPath(self, rUrl): domain = rUrl.host() uPath = rUrl.path() if not domain or len(domain) > 512 or not domainValid(domain): return None pathRaw = await self.ethResolver.resolveEnsDomain(domain) path = IPFSPath(pathRaw, autoCidConv=True) if path and path.valid: return path.child(uPath) if uPath else path
def browseManualPage(self, pagePath, fragment=None): manual = self.registry.get(self.defaultManualLang) if not manual: return False manualPath = IPFSPath(manual['Hash']) if not manualPath.valid: return False ipfsPath = manualPath.child(pagePath) ipfsPath.fragment = fragment ensure(self.app.resourceOpener.open(ipfsPath))
async def renderDirectory(self, request, ipfsop, path): ipfsPath = IPFSPath(path) indexPath = ipfsPath.child('index.html') try: data = await ipfsop.catObject(str(indexPath)) except aioipfs.APIError as exc: dec = APIErrorDecoder(exc) if dec.errNoSuchLink(): return await self.directoryListing(request, ipfsop, path) return self.urlInvalid(request) else: return data
def fromJson(mPath, metadata): if not IPFSPath(mPath).valid or not isinstance(metadata, dict): return None hmark = IPFSHashMark({mPath: metadata}) hmark.path = mPath return hmark
def handle_starttag(self, tag, alist): if tag not in ['a', 'link']: return attrs = dict(alist) href = attrs.get('href') if not isinstance(href, str): return if len(href) > 1024: return href = unquote(href) url = QUrl(href) if not url.isValid(): return if not url.scheme() or url.isRelative(): # Relative URL p = self.base.child(href) if p and self.pathValid(p): self.links.append(p) elif isIpfsUrl(url): # Absolute URL p = IPFSPath(url.toString()) if self.pathValid(p): self.links.append(p)
async def createPrivateChannel(self, ipfsop): dagChannels = ipfsop.ctx.currentProfile.dagChatChannels name = self.ui.channelName.text().strip() descr = self.ui.channelDescription.text().strip() uid = doubleUid4() eccPriv, eccPub = await ipfsop.ctx.eccExec.genKeys() privPath = self.app.eccChatChannelsDataLocation.joinpath( 'ecc_channel_{uid}.priv.key') with open(str(privPath), 'w+t') as fd: fd.write(eccPriv) eccEntry = await ipfsop.addString(eccPub) channel = await ipfsop.dagPut({ 'channel': { 'uid': uid, 'name': name, 'description': descr, 'icon': { '/': self.iconSel.iconCid }, 'eccPubKey': { '/': eccEntry['Hash'] } } }) path = IPFSPath(channel) dagChannels.registerPrivate(path.objPath) self.done(1)
async def ensContentHash(domain, sslverify=True, timeout=8): """ Resolve the content hash of an ENS domain with enswhois.org :rtype: IPFSPath """ url = whoisBaseUrl.join(URL('resolve/contenthash/{domain}'.format( domain=domain))) try: with async_timeout.timeout(timeout): async with aiohttp.ClientSession() as session: async with session.get(str(url), verify_ssl=sslverify) as resp: data = await resp.json() addr = data['result']['result'] if isinstance(addr, str) and addr.startswith('ipfs://'): match = re.search( 'ipfs://(?P<cid>[A-Za-z0-9]+)$', addr) if match: return IPFSPath(match.group('cid'), autoCidConv=True) except asyncio.TimeoutError: return None except Exception: log.debug('ensContentHash: Error occured while resolving') return None
def sendHit(self, hit): hitHash = hit.get('hash', None) mimeType = hit.get('mimetype', None) hitSize = hit.get('size', None) ipfsPath = IPFSPath(hitHash, autoCidConv=True) if not ipfsPath.valid: return sizeFormatted = sizeFormat(hitSize if hitSize else 0) pHit = { 'hash': hitHash, 'path': str(ipfsPath), 'url': ipfsPath.ipfsUrl, 'mimetype': mimeType if mimeType else 'application/unknown', 'title': hit.get('title', iUnknown()), 'size': hitSize if hitSize else 0, 'sizeformatted': sizeFormatted, 'description': hit.get('description', None), 'type': hit.get('type', iUnknown()), 'first-seen': hit.get('first-seen', iUnknown()) } self.resultReady.emit('ipfs-search', hitHash, QVariant(pHit))
def __init__(self, dagCid=None, dagRoot=None, offline=False, parent=None): super().__init__(parent) self._dagCid = dagCid self._dagRoot = dagRoot self._dagPath = IPFSPath(self._dagCid) self.evLoaded = asyncio.Event() self.offline = offline
async def browseIpService(self, ipfsop, serviceId, serviceCtx=None): """ Browse/open an IP service registered on an IPID """ log.info('Accessing IP service {}'.format(serviceId)) pService = await ipfsop.ipidManager.getServiceById(serviceId) if not pService: return endpoint = pService.endpoint if pService.type == IPService.SRV_TYPE_ATOMFEED: await self.app.mainWindow.atomButton.atomFeedSubscribe( str(endpoint)) elif pService.type == IPService.SRV_TYPE_VIDEOCALL: from galacteek.ui.videocall import VideoCallInitiator vc = VideoCallInitiator(pService) await vc.start() elif isinstance(endpoint, str): path = IPFSPath(endpoint, autoCidConv=True) await self.open(path)
async def urlHistoryRecord(url, title): from galacteek.browser.schemes import isIpfsUrl item = await urlHistoryGet(url) if not item: qUrl = QUrl(url) scheme = qUrl.scheme() if qUrl.isValid() else '' rootcid = '' rootcidv = 0 if isIpfsUrl(qUrl): ipfsPath = IPFSPath(url) if ipfsPath.valid and ipfsPath.rootCid: rootcid = str(ipfsPath.rootCid) rootcidv = ipfsPath.rootCid.version item = URLHistoryItem(url=url, rootcid=rootcid, rootcidv=rootcidv, scheme=scheme) await item.save() visit = URLHistoryVisit(historyitem=item, title=title) await visit.save() return item, visit
async def add(self, ipfsop, objPath, name=None, description='No description'): path = IPFSPath(objPath, autoCidConv=True) if not path.valid: raise ValueError('Invalid path') if not path.isRoot: oName = path.basename else: oName = name if name else hex(int(time.time() * 10000))[2:] oId = '{didurl}/{o}'.format(didurl=self.id, o=oName) try: async with self.ipid.editService(self.id) as editor: editor.service['serviceEndpoint']['objects'].append({ '@context': await ipfsop.ldContext('IpfsObject'), 'id': oId, 'objectPath': objPath, 'objectName': path.basename, 'objectDescription': description }) return True except Exception as err: self.ipid.message('Error editing collection service: {}'.format( str(err))) return False
async def sync(self, ipfsop, dial): sparql = Sparkie(dial.httpUrl('/sparql')) ids = await self.storedObjectsIds() q = """ PREFIX gs: <ips://galacteek.ld/> SELECT ?uri ?dateCreated ?ipfsPath WHERE { ?uri a gs:GraphTrail ; gs:ipfsPath ?ipfsPath ; gs:dateCreated ?dateCreated . """ + self.idsMinus(ids) + """ } """ reply = await sparql.query(q) try: for res in reply['results']['bindings']: path = IPFSPath(res['ipfsPath']['value']) await runningApp().s.rdfStore(path) except Exception as err: log.debug(f'Sync error: {err}') await sparql.close()
async def loadFeedEntries(self, ipfsop, feedSql, atomFeed): entries = await self.searchEntries(atomFeed.id) existingIds = [ent['entry_id'] for ent in entries] self._handled_by_id[atomFeed.id] = atomFeed for entry in atomFeed.entries: await asyncio.sleep(0.2) if not isinstance(entry.id, str): continue if entry.id not in existingIds: _rid = await self.addEntry(feedSql['id'], entry.id) entry.status = IPFSAtomFeedEntry.ENTRY_STATUS_NEW entry.srow_id = _rid self.processedFeedEntry.emit(atomFeed, entry) if feedSql['autopin_entries'] == 1 and \ feedSql['scheme'] in ['ipns', 'ipfs', 'dweb']: path = IPFSPath(entry.id) if path.valid: log.debug('Atom: autopinning {id}'.format(id=entry.id)) ensure(ipfsop.ctx.pin(path.objPath, qname='atom')) else: for exent in entries: if exent['entry_id'] == entry.id: entry.status = exent['status'] entry.srow_id = exent['id'] self.processedFeedEntry.emit(atomFeed, entry)
async def dagCompact(self, ipfsPath: IPFSPath, context=None): """ Compact a DAG """ try: # Note: we still do the schemas inlining (when # a @context is referenced with IPLD) to # support DAGs which weren't upgraded yet dag = await self.operator.ldInline( await self.operator.dagGet(str(ipfsPath)) ) assert dag is not None if not context: # Get the @context we'll compact with context = await self.operator.dagGet( str(ipfsPath.child('@context')) ) compacted = await jsonld.compact( dag, context, { 'base': gLdBaseUri, 'documentLoader': self.ldLoader, 'compactArrays': True } ) except Exception as err: self.operator.debug(f'Error expanding : {err}') raise err except aioipfs.APIError as err: self.operator.debug(f'IPFS error expanding : {err.message}') raise err else: return compacted
async def sendCyberHit(self, hit): hitHash = hit.get('hash') ipfsPath = IPFSPath(hitHash) if not ipfsPath.valid: return mType, stat = await self.app.rscAnalyzer(ipfsPath, fetchExtraMetadata=True) if not mType or not stat: return sInfo = StatInfo(stat) pHit = { 'hash': hitHash, 'path': str(ipfsPath), 'cyberlink': hit['cyberlink'], 'url': ipfsPath.ipfsUrl, 'mimetype': str(mType), 'title': hitHash, 'size': sInfo.totalSize, 'sizeformatted': sizeFormat(sInfo.totalSize), 'description': None, 'type': iUnknown(), 'first-seen': iUnknown() } self.resultReady.emit('cyber', hitHash, QVariant(pHit)) self.objectStatAvailable.emit(hitHash, stat)
def onDownloadRequested(self, downItem): if not downItem: return downPath = downItem.path() downUrl = downItem.url() if downPath.startswith(self.app.tempDirWeb): downItem.finished.connect( functools.partial(self.pageSaved, downItem)) downItem.accept() return iPath = IPFSPath(qurlPercentDecode(downUrl), autoCidConv=True) dialogPrechoice = 'download' allowOpen = False if iPath.valid: allowOpen = True if iPath.basename and self.shouldAutoOpen(iPath): dialogPrechoice = 'open' return runDialog(DownloadOpenObjectDialog, iPath.ipfsUrl if iPath.valid else downUrl.toString(), downItem, dialogPrechoice, allowOpen, accepted=self.onDialogAccepted)
async def fetchAvatar(self, ipfsop): avatarServiceId = self.ipid.didUrl(path='/avatar') service = await self.ipid.searchServiceById(avatarServiceId) if service: avatarPath = IPFSPath(service.endpoint) if not avatarPath.valid: log.debug(f'Invalid avatar for peer {self.peerId}') self._avatarPath = avatarPath statInfo = StatInfo( await ipfsop.objStat(str(avatarPath), timeout=15) ) if not statInfo.valid or statInfo.dataLargerThan( kilobytes(768)): log.debug(f'Invalid avatar for peer {self.peerId}') return await ipfsop.ctx.pin(str(avatarPath), qname='ipid-avatar') log.debug( f'Getting avatar for peer {self.peerId} from {avatarPath}') image = await getImageFromIpfs(str(avatarPath)) if image and not image.isNull(): self._avatarImage = image else: log.debug(f'Could not fetch avatar for peer {self.peerId}')
async def makePinRequest(self, op, title, descr, objects, priority=0, tags=None): async with self.profile.dagUser as dag: # objects needs to be a list of IPFS objects paths if not isinstance(objects, list): return False username = self.profile.userInfo.iphandle uid = str(uuid.uuid4()) now = datetime.now(timezone.utc) objsReq = [] for obj in objects: path = IPFSPath(obj) if not path.valid: continue try: mType, stat = await self._rscAnalyzer(path) except: continue statInfo = StatInfo(stat) if not statInfo.valid: continue objsReq.append({ 'path': path.objPath, 'totalsize': statInfo.totalSize, 'content_type': str(mType) if mType else None }) pinRequest = { 'pinrequest': { 'body': None, 'title': title, 'description': descr, 'objects': objsReq, 'uuid': uid, 'tags': tags if tags else [], 'author': username, 'priority': priority, 'date_published': isoformat(now), 'version': 1 } } dag.dagRoot[PINREQS_NODEKEY].append(pinRequest) await self.update() return True
async def async_loader(client, url): """ :param url: the URL to retrieve. :return: the RemoteDocument. """ try: if url.startswith('ipschema://'): o = urlparse(url) kList = await client.key.list() ipnsKey = None for key in kList['Keys']: if key['Name'] == o.netloc: ipnsKey = key['Id'] if ipnsKey is None: path = None else: path = IPFSPath(joinIpns(ipnsKey)).child(o.path) else: path = IPFSPath(url) if not path.valid: raise Exception('Not a valid path') if path and path.valid: data = await client.cat(path.objPath) return { 'document': json.loads(data.decode()), 'documentUrl': url, 'contextUrl': None } except aioipfs.APIError as e: log.debug(str(e)) raise e except JsonLdError as e: log.debug(str(e)) raise e except Exception as cause: raise JsonLdError( 'Could not retrieve a JSON-LD document from the URL.', 'jsonld.LoadDocumentError', code='loading document failed', cause=cause)
def changeCid(self, cid): if cidValid(cid): self.rootHash = cid self.rootPath = IPFSPath(self.rootHash, autoCidConv=True) self.cid = cidhelpers.getCID(self.rootHash) self.pinButton.changeObject(self.rootPath) self.initModel()
def onPathEntered(self): text = self.pathEdit.text() self.pathEdit.clear() path = IPFSPath(text) if path.valid: ensure(self.app.ipfsCtx.pinner.queue(path.objPath, True, None)) else: messageBox(iInvalidInput())