def personBoxJson(baseDir: str,domain: str,port: int,path: str, \ httpPrefix: str,noOfItems: int,boxname: str, \ authorized: bool,ocapAlways: bool) -> []: """Obtain the inbox/outbox/moderation feed for the given person """ if boxname!='inbox' and boxname!='dm' and \ boxname!='outbox' and boxname!='moderation': return None if not '/' + boxname in path: return None # Only show the header by default headerOnly = True # handle page numbers pageNumber = None if '?page=' in path: pageNumber = path.split('?page=')[1] if pageNumber == 'true': pageNumber = 1 else: try: pageNumber = int(pageNumber) except: pass path = path.split('?page=')[0] headerOnly = False if not path.endswith('/' + boxname): return None nickname = None if path.startswith('/users/'): nickname = path.replace('/users/', '', 1).replace('/' + boxname, '') if path.startswith('/@'): nickname = path.replace('/@', '', 1).replace('/' + boxname, '') if not nickname: return None if not validNickname(domain, nickname): return None if boxname == 'inbox': return createInbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,ocapAlways,pageNumber) if boxname == 'dm': return createDMTimeline(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,ocapAlways,pageNumber) elif boxname == 'outbox': return createOutbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,authorized,pageNumber) elif boxname == 'moderation': return createModeration(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,authorized,pageNumber) return None
def createPerson(baseDir: str,nickname: str,domain: str,port: int, \ httpPrefix: str, saveToFile: bool,password=None) -> (str,str,{},{}): """Returns the private key, public key, actor and webfinger endpoint """ if not validNickname(domain, nickname): return None, None, None, None # If a config.json file doesn't exist then don't decrement # remaining registrations counter remainingConfigExists = getConfigParam(baseDir, 'registrationsRemaining') if remainingConfigExists: registrationsRemaining = int(remainingConfigExists) if registrationsRemaining <= 0: return None, None, None, None privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint = \ createPersonBase(baseDir,nickname,domain,port,httpPrefix,saveToFile,password) if noOfAccounts(baseDir) == 1: #print(nickname+' becomes the instance admin and a moderator') setRole(baseDir, nickname, domain, 'instance', 'admin') setRole(baseDir, nickname, domain, 'instance', 'moderator') setRole(baseDir, nickname, domain, 'instance', 'delegator') setConfigParam(baseDir, 'admin', nickname) if not os.path.isdir(baseDir + '/accounts'): os.mkdir(baseDir + '/accounts') if not os.path.isdir(baseDir + '/accounts/' + nickname + '@' + domain): os.mkdir(baseDir + '/accounts/' + nickname + '@' + domain) if os.path.isfile(baseDir + '/img/default-avatar.png'): copyfile( baseDir + '/img/default-avatar.png', baseDir + '/accounts/' + nickname + '@' + domain + '/avatar.png') if os.path.isfile(baseDir + '/img/image.png'): copyfile( baseDir + '/img/image.png', baseDir + '/accounts/' + nickname + '@' + domain + '/image.png') if os.path.isfile(baseDir + '/img/banner.png'): copyfile( baseDir + '/img/banner.png', baseDir + '/accounts/' + nickname + '@' + domain + '/banner.png') if remainingConfigExists: registrationsRemaining -= 1 setConfigParam(baseDir, 'registrationsRemaining', str(registrationsRemaining)) return privateKeyPem, publicKeyPem, newPerson, webfingerEndpoint
def registerAccount(baseDir: str,httpPrefix: str,domain: str,port: int, \ nickname: str,password: str) -> bool: """Registers a new account from the web interface """ if accountExists(baseDir, nickname, domain): return False if not validNickname(domain, nickname): print('REGISTER: Nickname ' + nickname + ' is invalid') return False if len(password) < 8: print('REGISTER: Password should be at least 8 characters') return False privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint= \ createPerson(baseDir,nickname,domain,port, \ httpPrefix,True,password) if privateKeyPem: return True return False
def personLookup(domain: str, path: str, baseDir: str) -> {}: """Lookup the person for an given nickname """ if path.endswith('#main-key'): path = path.replace('#main-key', '') # is this a shared inbox lookup? isSharedInbox = False if path == '/inbox' or path == '/users/inbox' or path == '/sharedInbox': # shared inbox actor on @domain@domain path = '/users/' + domain isSharedInbox = True else: notPersonLookup=['/inbox','/outbox','/outboxarchive', \ '/followers','/following','/featured', \ '.png','.jpg','.gif','.mpv'] for ending in notPersonLookup: if path.endswith(ending): return None nickname = None if path.startswith('/users/'): nickname = path.replace('/users/', '', 1) if path.startswith('/@'): nickname = path.replace('/@', '', 1) if not nickname: return None if not isSharedInbox and not validNickname(domain, nickname): return None if ':' in domain: domain = domain.split(':')[0] handle = nickname + '@' + domain filename = baseDir + '/accounts/' + handle + '.json' if not os.path.isfile(filename): return None personJson = {"user": "******"} try: with open(filename, 'r') as fp: personJson = commentjson.load(fp) except: print('WARN: Failed to load actor ' + filename) return None return personJson
def personInboxJson(baseDir: str,domain: str,port: int,path: str, \ httpPrefix: str,noOfItems: int,ocapAlways: bool) -> []: """Obtain the inbox feed for the given person Authentication is expected to have already happened """ if not '/inbox' in path: return None # Only show the header by default headerOnly = True # handle page numbers pageNumber = None if '?page=' in path: pageNumber = path.split('?page=')[1] if pageNumber == 'true': pageNumber = 1 else: try: pageNumber = int(pageNumber) except: pass path = path.split('?page=')[0] headerOnly = False if not path.endswith('/inbox'): return None nickname = None if path.startswith('/users/'): nickname = path.replace('/users/', '', 1).replace('/inbox', '') if path.startswith('/@'): nickname = path.replace('/@', '', 1).replace('/inbox', '') if not nickname: return None if not validNickname(domain, nickname): return None return createInbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,ocapAlways,pageNumber)
def getSharesFeedForPerson(baseDir: str, \ domain: str,port: int, \ path: str,httpPrefix: str, \ sharesPerPage=12) -> {}: """Returns the shares for an account from GET requests """ if '/shares' not in path: return None # handle page numbers headerOnly = True pageNumber = None if '?page=' in path: pageNumber = path.split('?page=')[1] if pageNumber == 'true': pageNumber = 1 else: try: pageNumber = int(pageNumber) except: pass path = path.split('?page=')[0] headerOnly = False if not path.endswith('/shares'): return None nickname = None if path.startswith('/users/'): nickname = path.replace('/users/', '', 1).replace('/shares', '') if path.startswith('/@'): nickname = path.replace('/@', '', 1).replace('/shares', '') if not nickname: return None if not validNickname(domain, nickname): return None if port: if port != 80 and port != 443: if ':' not in domain: domain = domain + ':' + str(port) handleDomain = domain if ':' in handleDomain: handleDomain = domain.split(':')[0] handle = nickname + '@' + handleDomain sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' if headerOnly: noOfShares = 0 if os.path.isfile(sharesFilename): with open(sharesFilename, 'r') as fp: sharesJson = commentjson.load(fp) noOfShares = len(sharesJson.items()) shares = { '@context': 'https://www.w3.org/ns/activitystreams', 'first': httpPrefix + '://' + domain + '/users/' + nickname + '/shares?page=1', 'id': httpPrefix + '://' + domain + '/users/' + nickname + '/shares', 'totalItems': str(noOfShares), 'type': 'OrderedCollection' } return shares if not pageNumber: pageNumber = 1 nextPageNumber = int(pageNumber + 1) shares = { '@context': 'https://www.w3.org/ns/activitystreams', 'id': httpPrefix + '://' + domain + '/users/' + nickname + '/shares?page=' + str(pageNumber), 'orderedItems': [], 'partOf': httpPrefix + '://' + domain + '/users/' + nickname + '/shares', 'totalItems': 0, 'type': 'OrderedCollectionPage' } if not os.path.isfile(sharesFilename): print("test5") return shares currPage = 1 pageCtr = 0 totalCtr = 0 with open(sharesFilename, 'r') as fp: sharesJson = commentjson.load(fp) for itemID, item in sharesJson.items(): pageCtr += 1 totalCtr += 1 if currPage == pageNumber: shares['orderedItems'].append(item) if pageCtr >= sharesPerPage: pageCtr = 0 currPage += 1 shares['totalItems'] = totalCtr lastPage = int(totalCtr / sharesPerPage) if lastPage < 1: lastPage = 1 if nextPageNumber > lastPage: shares[ 'next'] = httpPrefix + '://' + domain + '/users/' + nickname + '/shares?page=' + str( lastPage) return shares
def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \ httpPrefix: str, authenticated: bool, followsPerPage=12, \ followFile='following') -> {}: """Returns the following and followers feeds from GET requests """ # Show a small number of follows to non-authenticated viewers if not authenticated: followsPerPage=6 if '/'+followFile not in path: return None # handle page numbers headerOnly=True pageNumber=None if '?page=' in path: pageNumber=path.split('?page=')[1] if pageNumber=='true' or not authenticated: pageNumber=1 else: try: pageNumber=int(pageNumber) except: pass path=path.split('?page=')[0] headerOnly=False if not path.endswith('/'+followFile): return None nickname=None if path.startswith('/users/'): nickname=path.replace('/users/','',1).replace('/'+followFile,'') if path.startswith('/@'): nickname=path.replace('/@','',1).replace('/'+followFile,'') if not nickname: return None if not validNickname(domain,nickname): return None if port: if port!=80 and port!=443: if ':' not in domain: domain=domain+':'+str(port) if headerOnly: following = { '@context': 'https://www.w3.org/ns/activitystreams', 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page=1', 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, 'totalItems': getNoOfFollows(baseDir,nickname,domain,authenticated), 'type': 'OrderedCollection'} return following if not pageNumber: pageNumber=1 nextPageNumber=int(pageNumber+1) following = { '@context': 'https://www.w3.org/ns/activitystreams', 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(pageNumber), 'orderedItems': [], 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, 'totalItems': 0, 'type': 'OrderedCollectionPage'} handleDomain=domain if ':' in handleDomain: handleDomain=domain.split(':')[0] handle=nickname.lower()+'@'+handleDomain.lower() filename=baseDir+'/accounts/'+handle+'/'+followFile+'.txt' if not os.path.isfile(filename): return following currPage=1 pageCtr=0 totalCtr=0 with open(filename, "r") as f: lines = f.readlines() for line in lines: if '#' not in line: if '@' in line and not line.startswith('http'): pageCtr += 1 totalCtr += 1 if currPage==pageNumber: url = httpPrefix + '://' + line.lower().replace('\n','').split('@')[1] + \ '/users/' + line.lower().replace('\n','').split('@')[0] following['orderedItems'].append(url) elif (line.startswith('http') or line.startswith('dat')) and '/users/' in line: pageCtr += 1 totalCtr += 1 if currPage==pageNumber: following['orderedItems'].append(line.lower().replace('\n','')) if pageCtr>=followsPerPage: pageCtr=0 currPage += 1 following['totalItems']=totalCtr lastPage=int(totalCtr/followsPerPage) if lastPage<1: lastPage=1 if nextPageNumber>lastPage: following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) return following