def child(self, path, normalize=False): if not isinstance(path, str): raise ValueError('Need string') if self.subPath is None and (path == '..' or path.startswith('../')): # Not crossing the /{ipfs,ipns} NS return self if normalize: cPath = normp(path) return IPFSPath( normp(posixIpfsPath.join(self.objPath, cPath.lstrip('/')))) else: cPath = normpPreserve(path) return IPFSPath(posixIpfsPath.join(self.objPath, cPath.lstrip('/')))
async def cat(self, op, path): try: return await op.client.cat( posixIpfsPath.join(self.dagCid, path)) except aioipfs.APIError as err: self.debug(f'Cat error ({path}): {err.message}') return None
def p2pEndpointMake(peerId: str, serviceName: str, protocolVersion=None): args = ['/p2p', peerId, 'x', serviceName.lstrip('/')] if protocolVersion: args.append(protocolVersion) return posixIpfsPath.join(*args)
def fullPath(self): parentHash = self.parentHash if parentHash: fp = joinIpfs( posixIpfsPath.join(parentHash, self.entry['Name'])) else: fp = joinIpfs(self.entry['Hash']) return fp + '/' if self.isDir() else fp
def p2pEndpointAddrExplode(addr: str): """ Explode a P2P service endpoint address such as : /p2p/12D3KooWD3bfmNbuuuVCYwkjnFt3ukm3qaB3hDED3peHHXawvRAi/x/videocall/room1/1.0.0 /p2p/12D3KooWD3bfmNbuuuVCYwkjnFt3ukm3qaB3hDED3peHHXawvRAi/x/test into its components, returning a tuple in the form (peerId, protoFull, protoVersion) protoFull can be passed to 'ipfs p2p dial' Example: ('12D3KooWD3bfmNbuuuVCYwkjnFt3ukm3qaB3hDED3peHHXawvRAi', '/x/videocall/room1/1.0.0', '1.0.0') protoVersion is not mandatory """ parts = addr.lstrip(posixIpfsPath.sep).split(posixIpfsPath.sep) try: assert parts.pop(0) == 'p2p' peerId = parts.pop(0) prefix = parts.pop(0) assert peerIdRe.match(peerId) assert prefix == 'x' pVersion = None protoA = [prefix] protoPart = parts.pop(0) protoA.append(protoPart) while protoPart: try: protoPart = parts.pop(0) except IndexError: break protoA.append(protoPart) try: v = StrictVersion(protoPart) except Exception: pass else: # Found a version, should be last element pVersion = v assert len(parts) == 0 break protoFull = posixIpfsPath.sep + posixIpfsPath.join(*protoA) return peerId, protoFull, pVersion except Exception: log.warning(f'Invalid P2P endpoint address: {addr}') return None
async def get(self, op, path): self.debug('DAG get: {}'.format(posixIpfsPath.join(self.dagCid, path))) try: dagNode = await op.dagGet( posixIpfsPath.join(self.dagCid, path)) except aioipfs.APIError as err: log.debug('DAG get: {0}. An error occured: {1}'.format( path, err.message)) return None if isinstance(dagNode, dict) and 'data' in dagNode and \ 'links' in dagNode: """ Try and decode the protobuf data """ msg = pb.decodeUnixfsDagNode(dagNode['data']) if not msg: return return msg['data'] else: return dagNode
async def walk(self, op, path='', maxObjSize=0, depth=1): """ Do not use this! """ data = await self.get(path) if data is None: return if isinstance(data, dict) and 'data' in data and 'links' in data: """ Try and decode the protobuf data """ msg = pb.decodeUnixfsDagNode(data['data']) if not msg: return if maxObjSize == 0 or \ (maxObjSize > 0 and msg['size'] < maxObjSize): await yield_((path, DAGObj(msg['data']))) if not data: return if isinstance(data, dict): for objKey, objValue in data.items(): if objKey == '/': out = await op.client.cat(objValue) await yield_((path, DAGObj(out))) if objKey == 'data' or objKey == 'links': continue else: await yield_from_( self.walk(op, path=posixIpfsPath.join(path, objKey), maxObjSize=maxObjSize, depth=depth) ) elif isinstance(data, list): for idx, obj in enumerate(data): await yield_from_( self.walk(op, path=posixIpfsPath.join(path, str(idx)), maxObjSize=maxObjSize) ) elif isinstance(data, str): await yield_((path, DAGObj(data)))
async def followPeerFeed(self, op, piCtx): identMsg = piCtx.ident path = posixIpfsPath.join(joinIpns(identMsg.dagIpns), DWEB_ATOM_FEEDFN) ipfsPath = IPFSPath(path) try: await self.app.sqliteDb.feeds.follow(ipfsPath.ipfsUrl) except Exception: # TODO pass
def feedAddPinRequests(self, requests, feed): for id, reqobj in enumerate(requests): req = reqobj['pinrequest'] rpath = self.sitePath.child( posixIpfsPath.join(PINREQS_NODEKEY, str(id), 'view')) fEntry = feed.add_entry() fEntry.title('Pin request: {}'.format(req['title'])) fEntry.id(rpath.ipfsUrl) fEntry.link(href=rpath.ipfsUrl, rel='alternate') fEntry.published(req['date_published']) fEntry.author({'name': self.profile.userInfo.iphandle})
def data(self, column, role): if column == 0: handle = self.peerItem.ctx.spaceHandle exploded = didExplode(self.service.id) if exploded['path']: return posixIpfsPath.join(handle.human, exploded['path'].lstrip('/')) if column == 1: return str(self.service.id) return QVariant(None)
def data(self, column, role): if column == 0: handle = self.peerItem.ctx.spaceHandle exploded = didExplode(self.sObject['id']) if exploded and exploded['path']: return posixIpfsPath.join(handle.short, exploded['path'].lstrip('/')) if column == 1: return self.sObject['id'] return QVariant(None)
def getFullPath(self): """ Returns the full IPFS path of the entry associated with this item (preserving file names) if we have the parent's hash, or the IPFS path with the entry's hash otherwise """ if self._parentCid: return joinIpfs( posixIpfsPath.join(cidConvertBase32(self._parentCid), self.filename)) else: return joinIpfs(cidConvertBase32(self.entry['Hash']))
async def getTitleDirectory(client, path): indexPath = posixIpfsPath.join(path, 'index.html') try: data = await client.cat(indexPath) except aioipfs.APIError as exc: if exc.code == 0 and exc.message.startswith('no link named'): return None if exc.code == 0 and exc.message == 'this dag node is a directory': # Wicked! return None else: return runTitleParser(data)
async def savePlaylist(self, ipfsop, obj, name): objPath = posixIpfsPath.join(self.profile.pathPlaylists, name) exists = await ipfsop.filesStat(objPath) if exists: await ipfsop.filesRm(objPath) ent = await ipfsop.client.core.add_json(obj.root) if ent: await ipfsop.filesLinkFp(ent, objPath) self.playlistIpfsPath = joinIpfs(ent['Hash']) self.copyPathAction.setEnabled(True) self.update()
async def profileLoad(self, ipfsop, pName): if await self.profileExists(pName): profile = UserProfile(self, pName, posixIpfsPath.join( GFILES_ROOT_PATH, 'profile.{}'.format(pName)) ) if pName not in self.profiles: self.profiles[pName] = profile self.currentProfile = profile try: async for msg in profile.init(ipfsop): yield msg except Exception as e: log.info('Could not initialize profile: {}'.format( str(e)), exc_info=True) raise e self.profileEmitAvail() self.profileChange(pName)
async def profileNew(self, ipfsop, pName, initOptions=None, emitavail=True): profile = UserProfile(self, pName, posixIpfsPath.join( GFILES_ROOT_PATH, 'profile.{}'.format(pName)), initOptions=initOptions ) if not self.currentProfile: self.currentProfile = profile try: async for msg in profile.init(ipfsop): yield msg except Exception as e: log.info('Could not initialize profile: {}'.format( str(e)), exc_info=True) raise e self.profiles[pName] = profile self.profileChange(pName) self.profileEmitAvail()
def feedAddPosts(self, blogPosts, feed, ftype='ipfs'): for post in blogPosts: ppath = self.sitePath.child( posixIpfsPath.join('blog', post['postname'], 'view')) fEntry = feed.add_entry() fEntry.title(post['title']) if ftype == 'ipfs': url = ppath.ipfsUrl elif ftype == 'publicgw': url = ppath.publicGwUrl fEntry.id(url) fEntry.link(href=url, rel='alternate') fEntry.updated(post['date_modified']) fEntry.published(post['date_published']) fEntry.content(content=markitdown(post['body']), type='html') fEntry.author({'name': self.profile.userInfo.iphandle}) for tag in post['tags']: fEntry.category(term=tag)
def pathOrbitalConfig(self): return posixIpfsPath.join(self.pathOrbital, 'config')
def joinIpns(path): if isinstance(path, str): return posixIpfsPath.join('/ipns/', path)
def pathjoin(self, path1, path2): return posixIpfsPath.join(path1, path2.lstrip('/'))
def pathHMarksLibrary(self): return posixIpfsPath.join(self.pathData, 'hmarks_library')
async def peerNode(self, peerId): if peerId in self.nodes: return await self.get(posixIpfsPath.join('nodes', peerId))
def pathOrbital(self): return posixIpfsPath.join(self.pathData, 'orbital')
def pathEdagSeedsAll(self): return posixIpfsPath.join(self.pathEDagsSeeds, 'ipseeds_main_mega.enc.edag')
async def init(self, ipfsop): """ Initialize the profile's filesystem """ yield 10, 'Initializing filesystem' await ipfsop.filesMkdir(self.root) for directory in self.tree: await ipfsop.filesMkdir(directory) wPath = posixIpfsPath.join(self.pathHome, 'welcome') welcomeEnt = await ipfsop.filesList(wPath) if not welcomeEnt: try: welcome = readQrcTextFile(':/share/static/misc/welcome.md') tmpPath = self.ctx.app.tempDir.filePath('welcome.html') await asyncWriteFile(tmpPath, markitdown(welcome), mode='w+t') await ipfsop.filesLink(await ipfsop.addPath(tmpPath, wrap=True), self.pathHome, name='welcome') except Exception: pass self.filesModel = createMFSModel() self.filesModel.setupItemsFromProfile(self) yield 20, 'Generating IPNS keys' key = await ipfsop.keyFind(self.keyRoot) if key is not None: self.keyRootId = key.get('Id', None) else: self.userLogInfo('Generating main IPNS key') result = await ipfsop.keyGen(self.keyRoot) self.keyRootId = result.get('Id', None) self.debug('IPNS key({0}): {1}'.format(self.keyRoot, self.keyRootId)) yield 30, 'Initializing crypto' if not await self.cryptoInit(): self.userLogInfo('Error while initializing crypto') raise ProfileError('Crypto init failed') if not await ipfsop.hasDagCommand(): self.userLogInfo('No DAG API! ..') raise ProfileError('No DAG API') yield 40, 'Initializing EDAGs ..' self._dagUser = UserDAG(self.pathUserDagMeta, loop=self.ctx.loop) self._dagChatChannels = ChannelsDAG(self.pathChatChannelsDagMeta, loop=self.ctx.loop) self._dagNetwork = PeersGraphDAG(self.pathEDagNetwork, loop=self.ctx.loop, autoUpdateDates=True) # Seeds self.dagSeedsMain = SeedsEDag(self.pathEdagSeedsMain, loop=self.ctx.loop, cipheredMeta=True) self.dagSeedsAll = MegaSeedsEDag(self.pathEdagSeedsAll, loop=self.ctx.loop, cipheredMeta=True) yield 50, 'Loading EDAGs ..' ensure(self.dagUser.load()) await self.dagUser.loaded # Chat channels await self.dagChatChannels.load() # Network EDAG await self.dagNetwork.load() # Seeds EDAGs await self.dagSeedsMain.load() await self.dagSeedsAll.load() await self.dagSeedsAll.associate(self.dagSeedsMain) self.dagUser.dagCidChanged.connect(self.onDagChange) ensure(self.publishDag(allowOffline=True, reschedule=True)) yield 60, 'Loading user blog ..' self.userWebsite = UserWebsite(self.dagUser, self, self.keyRootId, self.ctx.app.jinjaEnv, parent=self) await self.userWebsite.init() ensure(self.update()) self._initialized = True yield 70, 'Importing manual ..' ensure(self.ctx.app.manuals.importManuals(self)) self._initOptions = {} yield 100, 'Profile ready'
def pathEDagNetwork(self): return posixIpfsPath.join(self.pathEDags, 'network.edag')
def pathChatChannelsDagMeta(self): return posixIpfsPath.join(self.pathEDags, 'chatchannels.edag')
def pathUserDagMeta(self): return posixIpfsPath.join(self.pathData, 'dag.main')
def pathUserInfo(self): return posixIpfsPath.join(self.pathData, 'userinfo.json.enc')
def pathPlaylists(self): return posixIpfsPath.join(self.pathData, 'playlists')