mirror of https://gitlab.com/bashrc2/epicyon
				
				
				
			Move shared inbox account into daemon
							parent
							
								
									cb79ddb760
								
							
						
					
					
						commit
						c301f45b33
					
				|  | @ -18,6 +18,7 @@ from webfinger import webfingerLookup | |||
| from webfinger import webfingerHandle | ||||
| from person import personLookup | ||||
| from person import personBoxJson | ||||
| from person import createSharedInbox | ||||
| from posts import outboxMessageCreateWrap | ||||
| from posts import savePostToBox | ||||
| from inbox import inboxPermittedMessage | ||||
|  | @ -540,6 +541,10 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \ | |||
|         httpd.acceptedCaps.append('inbox:noannounce') | ||||
|     if cw: | ||||
|         httpd.acceptedCaps.append('inbox:cw') | ||||
| 
 | ||||
|     print('Creating shared inbox: inbox@'+domain) | ||||
|     createSharedInbox(baseDir,'inbox',domain,port,httpPrefix) | ||||
|          | ||||
|     print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) | ||||
|     httpd.thrInboxQueue= \ | ||||
|         threadWithTrace(target=runInboxQueue, \ | ||||
|  |  | |||
							
								
								
									
										10
									
								
								epicyon.py
								
								
								
								
							
							
						
						
									
										10
									
								
								epicyon.py
								
								
								
								
							|  | @ -400,13 +400,9 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain): | |||
|     setConfigParam(baseDir,'adminPassword',adminPassword) | ||||
|     createPerson(baseDir,nickname,domain,port,httpPrefix,True,adminPassword) | ||||
| 
 | ||||
| if not os.path.isdir(baseDir+'/accounts/inbox@'+domain): | ||||
|     print('Creating shared inbox: inbox@'+domain) | ||||
|     createSharedInbox(baseDir,'inbox',domain,port,httpPrefix) | ||||
| 
 | ||||
| if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain): | ||||
|     print('Creating capabilities account which can sign requests') | ||||
|     createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix) | ||||
| #if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain): | ||||
| #    print('Creating capabilities account which can sign requests') | ||||
| #    createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix) | ||||
| 
 | ||||
| if args.testdata: | ||||
|     nickname='testuser567' | ||||
|  |  | |||
							
								
								
									
										38
									
								
								follow.py
								
								
								
								
							
							
						
						
									
										38
									
								
								follow.py
								
								
								
								
							|  | @ -27,6 +27,8 @@ def getFollowersOfPerson(baseDir: str, \ | |||
|     Used by the shared inbox to know who to send incoming mail to | ||||
|     """ | ||||
|     followers=[] | ||||
|     if ':' in domain: | ||||
|         domain=domain.split(':')[0] | ||||
|     handle=nickname.lower()+'@'+domain.lower() | ||||
|     if not os.path.isdir(baseDir+'/accounts/'+handle): | ||||
|         return followers | ||||
|  | @ -345,38 +347,64 @@ def sendFollowRequest(session,baseDir: str, \ | |||
| 
 | ||||
|     return newFollowJson | ||||
| 
 | ||||
| def getFollowersOfActor(baseDir :str,actor :str,recipientsDict: {}) -> {}: | ||||
| def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}: | ||||
|     """In a shared inbox if we receive a post we know who it's from | ||||
|     and if it's addressed to followers then we need to get a list of those. | ||||
|     This returns a list of account handles which follow the given actor | ||||
|     and also the corresponding capability id if it exists | ||||
|     """ | ||||
|     if debug: | ||||
|         print('DEBUG: getting followers of '+actor) | ||||
|     recipientsDict={} | ||||
|     if ':' not in actor: | ||||
|         return recipientsDict | ||||
|     httpPrefix=actor.split(':')[0] | ||||
|     nickname=getNicknameFromActor(actor) | ||||
|     if not nickname: | ||||
|         if debug: | ||||
|             print('DEBUG: no nickname found in '+actor) | ||||
|         return recipientsDict | ||||
|     domain,port=getDomainFromActor(actor) | ||||
|     if not domain: | ||||
|         if debug: | ||||
|             print('DEBUG: no domain found in '+actor) | ||||
|         return recipientsDict | ||||
|     actorHandle=nickname+'@'+domain | ||||
|     if debug: | ||||
|         print('DEBUG: searching for handle '+actorHandle) | ||||
|     # for each of the accounts | ||||
|     for subdir, dirs, files in os.walk(baseDir+'/accounts'): | ||||
|         for account in dirs: | ||||
|             if '@' in account and not account.startswith('inbox@'): | ||||
|                 followingFilename = os.path.join(subdir, account)+'/following.txt' | ||||
|                 if debug: | ||||
|                     print('DEBUG: examining follows of '+account) | ||||
|                     print(followingFilename) | ||||
|                 if os.path.isfile(followingFilename): | ||||
|                     # does this account follow the given actor? | ||||
|                     if debug: | ||||
|                         print('DEBUG: checking if '+actorHandle+' in '+followingFilename) | ||||
|                     if actorHandle in open(followingFilename).read(): | ||||
|                         if debug: | ||||
|                             print('DEBUG: '+account+' follows '+actorHandle) | ||||
|                         ocapFilename=baseDir+'/accounts/'+account+'/ocap/accept/'+httpPrefix+':##'+domain+':'+str(port)+'#users#'+nickname+'.json' | ||||
|                         if debug: | ||||
|                             print('DEBUG: checking capabilities of'+account) | ||||
|                         if os.path.isfile(ocapFilename):                         | ||||
|                             with open(ocapFilename, 'r') as fp: | ||||
|                                 ocapJson=commentjson.load(fp) | ||||
|                             if ocapJson.get('id'):                                 | ||||
|                                 recipientsDict[account]=ocapJson['id'] | ||||
|                             else: | ||||
|                                 recipientsDict[account]=None | ||||
|                                 if ocapJson.get('id'): | ||||
|                                     if debug: | ||||
|                                         print('DEBUG: capabilities id found for '+account) | ||||
|                  | ||||
|                                     recipientsDict[account]=ocapJson['id'] | ||||
|                                 else: | ||||
|                                     if debug: | ||||
|                                         print('DEBUG: capabilities has no id attribute') | ||||
|                                     recipientsDict[account]=None | ||||
|                         else: | ||||
|                             if debug: | ||||
|                                 print('DEBUG: No capabilities file found for '+account+' granted by '+actorHandle) | ||||
|                                 print(ocapFilename) | ||||
|                             recipientsDict[account]=None | ||||
|     return recipientsDict | ||||
|  |  | |||
							
								
								
									
										80
									
								
								inbox.py
								
								
								
								
							
							
						
						
									
										80
									
								
								inbox.py
								
								
								
								
							|  | @ -19,6 +19,7 @@ from utils import getStatusNumber | |||
| from utils import getDomainFromActor | ||||
| from utils import getNicknameFromActor | ||||
| from utils import domainPermitted | ||||
| from utils import locatePost | ||||
| from httpsig import verifyPostHeaders | ||||
| from session import createSession | ||||
| from session import getJson | ||||
|  | @ -87,7 +88,7 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> boo | |||
|     if not urlPermitted(actor,federationList,"inbox:write"): | ||||
|         return False | ||||
| 
 | ||||
|     if messageJson['type']!='Follow': | ||||
|     if messageJson['type']!='Follow' and messageJson['type']!='Like': | ||||
|         if messageJson.get('object'): | ||||
|             if messageJson['object'].get('inReplyTo'): | ||||
|                 inReplyTo=messageJson['object']['inReplyTo'] | ||||
|  | @ -210,7 +211,7 @@ def inboxCheckCapabilities(baseDir :str,nickname :str,domain :str, \ | |||
| def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \ | ||||
|                            recipientsDict :{}, \ | ||||
|                            domainMatch: str,domain :str, \ | ||||
|                            actor :str) -> bool: | ||||
|                            actor :str,debug: bool) -> bool: | ||||
|     """Given a list of post recipients (toList) from 'to' or 'cc' parameters | ||||
|     populate a recipientsDict with the handle and capabilities id for each | ||||
|     """ | ||||
|  | @ -235,12 +236,23 @@ def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \ | |||
|                         else: | ||||
|                             recipientsDict[handle]=None | ||||
|                 else: | ||||
|                     if debug: | ||||
|                         print('DEBUG: '+ocapFilename+' not found') | ||||
|                     recipientsDict[handle]=None | ||||
|             else: | ||||
|                 if debug: | ||||
|                     print('DEBUG: '+baseDir+'/accounts/'+handle+' does not exist') | ||||
|         else: | ||||
|             if debug: | ||||
|                 print('DEBUG: '+recipient+' is not local to '+domainMatch) | ||||
|                 print(str(toList)) | ||||
|         if recipient.endswith('followers'): | ||||
|             if debug: | ||||
|                 print('DEBUG: followers detected as post recipients') | ||||
|             followerRecipients=True | ||||
|     return followerRecipients,recipientsDict | ||||
| 
 | ||||
| def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int) -> ([],[]): | ||||
| def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int, debug :bool) -> ([],[]): | ||||
|     """Returns dictionaries containing the recipients of the given post | ||||
|     The shared dictionary contains followers | ||||
|     """ | ||||
|  | @ -248,6 +260,9 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : | |||
|     recipientsDictFollowers={} | ||||
| 
 | ||||
|     if not postJsonObject.get('actor'): | ||||
|         if debug: | ||||
|             pprint(postJsonObject) | ||||
|             print('WARNING: inbox post has no actor') | ||||
|         return recipientsDict,recipientsDictFollowers | ||||
| 
 | ||||
|     if ':' in domain: | ||||
|  | @ -264,29 +279,48 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : | |||
|     if postJsonObject.get('object'): | ||||
|         if isinstance(postJsonObject['object'], dict): | ||||
|             if postJsonObject['object'].get('to'): | ||||
|                 if debug: | ||||
|                     print('DEBUG: resolving "to"') | ||||
|                 includesFollowers,recipientsDict= \ | ||||
|                     inboxPostRecipientsAdd(baseDir,httpPrefix, \ | ||||
|                                            postJsonObject['object']['to'], \ | ||||
|                                            recipientsDict, \ | ||||
|                                            domainMatch,domainBase,actor) | ||||
|                                            domainMatch,domainBase, \ | ||||
|                                            actor,debug) | ||||
|                 if includesFollowers: | ||||
|                     followerRecipients=True | ||||
|             else: | ||||
|                 if debug: | ||||
|                     print('DEBUG: inbox post has no "to"') | ||||
| 
 | ||||
|             if postJsonObject['object'].get('cc'): | ||||
|                 includesFollowers,recipientsDict= \ | ||||
|                     inboxPostRecipientsAdd(baseDir,httpPrefix, \ | ||||
|                                            postJsonObject['object']['cc'], \ | ||||
|                                            recipientsDict, \ | ||||
|                                            domainMatch,domainBase,actor) | ||||
|                                            domainMatch,domainBase, \ | ||||
|                                            actor,debug) | ||||
|                 if includesFollowers: | ||||
|                     followerRecipients=True | ||||
|             else: | ||||
|                 if debug: | ||||
|                     print('DEBUG: inbox post has no cc') | ||||
|         else: | ||||
|             if debug: | ||||
|                 if isinstance(postJsonObject['object'], str): | ||||
|                     if '/statuses/' in postJsonObject['object']: | ||||
|                         print('DEBUG: inbox item is a link to a post') | ||||
|                     else: | ||||
|                         if '/users/' in postJsonObject['object']: | ||||
|                             print('DEBUG: inbox item is a link to an actor') | ||||
| 
 | ||||
|     if postJsonObject.get('to'): | ||||
|         includesFollowers,recipientsDict= \ | ||||
|             inboxPostRecipientsAdd(baseDir,httpPrefix, \ | ||||
|                                    postJsonObject['to'], \ | ||||
|                                    recipientsDict, \ | ||||
|                                    domainMatch,domainBase,actor) | ||||
|                                    domainMatch,domainBase, \ | ||||
|                                    actor,debug) | ||||
|         if includesFollowers: | ||||
|             followerRecipients=True | ||||
| 
 | ||||
|  | @ -295,16 +329,19 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : | |||
|             inboxPostRecipientsAdd(baseDir,httpPrefix, \ | ||||
|                                    postJsonObject['cc'], \ | ||||
|                                    recipientsDict, \ | ||||
|                                    domainMatch,domainBase,actor) | ||||
|                                    domainMatch,domainBase, \ | ||||
|                                    actor,debug) | ||||
|         if includesFollowers: | ||||
|             followerRecipients=True | ||||
| 
 | ||||
|     if not followerRecipients: | ||||
|         if debug: | ||||
|             print('DEBUG: no followers were resolved') | ||||
|         return recipientsDict,recipientsDictFollowers | ||||
| 
 | ||||
|     # now resolve the followers | ||||
|     recipientsDictFollowers= \ | ||||
|         getFollowersOfActor(baseDir,actor,recipientsDict) | ||||
|         getFollowersOfActor(baseDir,actor,debug) | ||||
| 
 | ||||
|     return recipientsDict,recipientsDictFollowers | ||||
| 
 | ||||
|  | @ -388,18 +425,7 @@ def receiveLike(session,handle: str,baseDir: str, \ | |||
|     if not os.path.isdir(baseDir+'/accounts/'+handle): | ||||
|         print('DEBUG: unknown recipient of like - '+handle) | ||||
|     # if this post in the outbox of the person? | ||||
|     boxName='outbox' | ||||
|     postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' | ||||
|     if not os.path.isfile(postFilename): | ||||
|         # if this post in the inbox of the person? | ||||
|         boxName='inbox' | ||||
|         postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' | ||||
|         if not os.path.isfile(postFilename): | ||||
|             # if this post in the shared inbox? | ||||
|             handle='inbox@'+domain | ||||
|             postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' | ||||
|             if not os.path.isfile(postFilename): | ||||
|                 postFilename=None | ||||
|     postFilename=locatePost(baseDir,handle.split('@')[0],handle.split('@')[1],messageJson['object']) | ||||
|     if not postFilename: | ||||
|         if debug: | ||||
|             print('DEBUG: post not found in inbox or outbox') | ||||
|  | @ -569,7 +595,15 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache | |||
| 
 | ||||
|             # get recipients list | ||||
|             recipientsDict,recipientsDictFollowers= \ | ||||
|                 inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port) | ||||
|                 inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port,debug) | ||||
|             if len(recipientsDict.items())==0 and \ | ||||
|                len(recipientsDictFollowers.items())==0: | ||||
|                 if debug: | ||||
|                     pprint(queueJson['post']) | ||||
|                     print('DEBUG: no recipients were resolved for post arriving in inbox') | ||||
|                 os.remove(queueFilename) | ||||
|                 queue.pop(0) | ||||
|                 continue | ||||
| 
 | ||||
|             # if there are only a small number of followers then process them as if they | ||||
|             # were specifically addresses to particular accounts | ||||
|  | @ -579,7 +613,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache | |||
|                     if debug: | ||||
|                         print('DEBUG: moving '+str(noOfFollowItems)+' inbox posts addressed to followers') | ||||
|                     for handle,postItem in recipientsDictFollowers.items(): | ||||
|                         recipientsDict['handle']=postItem | ||||
|                         recipientsDict[handle]=postItem | ||||
|                     recipientsDictFollowers={} | ||||
|                 recipientsList=[recipientsDict,recipientsDictFollowers] | ||||
| 
 | ||||
|  | @ -587,7 +621,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache | |||
|                 print('*************************************') | ||||
|                 print('Resolved recipients list:') | ||||
|                 pprint(recipientsDict) | ||||
|                 print('Resolved sollowers list:') | ||||
|                 print('Resolved followers list:') | ||||
|                 pprint(recipientsDictFollowers) | ||||
|                 print('*************************************') | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										174
									
								
								like.py
								
								
								
								
							
							
						
						
									
										174
									
								
								like.py
								
								
								
								
							|  | @ -8,80 +8,28 @@ __status__ = "Production" | |||
| 
 | ||||
| import json | ||||
| import commentjson | ||||
| from pprint import pprint | ||||
| from utils import urlPermitted | ||||
| from utils import getNicknameFromActor | ||||
| from utils import getDomainFromActor | ||||
| from utils import locatePost | ||||
| from posts import sendSignedJson | ||||
| 
 | ||||
| def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ | ||||
|          ccUrl: str,httpPrefix: str,objectUrl: str,clientToServer: bool, \ | ||||
|          sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}) -> {}: | ||||
|     """Creates a like | ||||
|     ccUrl might be a specific person whose post was liked | ||||
|     objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase | ||||
|     """ | ||||
|     if not urlPermitted(objectUrl,federationList,"inbox:write"): | ||||
|         return None | ||||
| 
 | ||||
|     if port!=80 and port!=443: | ||||
|         domain=domain+':'+str(port) | ||||
| 
 | ||||
|     newLikeJson = { | ||||
|         'type': 'Like', | ||||
|         'actor': httpPrefix+'://'+domain+'/users/'+nickname, | ||||
|         'object': objectUrl, | ||||
|         'to': [httpPrefix+'://'+domain+'/users/'+nickname+'/followers'], | ||||
|         'cc': [] | ||||
|     } | ||||
|     if ccUrl: | ||||
|         if len(ccUrl)>0: | ||||
|             newLikeJson['cc']=ccUrl | ||||
| 
 | ||||
|     # Extract the domain and nickname from a statuses link | ||||
|     likedPostNickname=None | ||||
|     likedPostDomain=None | ||||
|     likedPostPort=None | ||||
|     if '/users/' in objectUrl: | ||||
|         likedPostNickname=getNicknameFromActor(objectUrl) | ||||
|         likedPostDomain,likedPostPort=getDomainFromActor(objectUrl) | ||||
| 
 | ||||
|     if likedPostNickname: | ||||
|         sendSignedJson(newlikeJson,session,baseDir, \ | ||||
|                        nickname,domain,port, \ | ||||
|                        likedPostNickname,likedPostDomain,likedPostPort, \ | ||||
|                        'https://www.w3.org/ns/activitystreams#Public', \ | ||||
|                        httpPrefix,True,clientToServer,federationList, \ | ||||
|                        sendThreads,postLog,cachedWebfingers,personCache,debug) | ||||
| 
 | ||||
|     return newLikeJson | ||||
| 
 | ||||
| def likePost(session,baseDir: str,federationList: [], \ | ||||
|              nickname: str, domain: str, port: int, httpPrefix: str, \ | ||||
|              likeNickname: str, likeDomain: str, likePort: int, \ | ||||
|              likeHttps: bool, likeStatusNumber: int, \ | ||||
|              clientToServer: bool,sendThreads: [],postLog: [], \ | ||||
|              personCache: {},cachedWebfingers: {}) -> {}: | ||||
|     """Likes a given status post | ||||
|     """ | ||||
|     likeDomain=likeDomain | ||||
|     if likePort!=80 and likePort!=443: | ||||
|         likeDomain=likeDomain+':'+str(likePort) | ||||
| 
 | ||||
|     objectUrl = \ | ||||
|         httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \ | ||||
|         '/statuses/'+str(likeStatusNumber) | ||||
| 
 | ||||
|     return like(session,baseDir,federationList,nickname,domain,port, \ | ||||
|                 ccUrl,httpPrefix,objectUrl,clientToServer, \ | ||||
|                 sendThreads,postLog,personCache,cachedWebfingers) | ||||
| 
 | ||||
| def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None: | ||||
| def updateLikesCollection(postFilename: str,objectUrl: str, actor: str,debug: bool) -> None: | ||||
|     """Updates the likes collection within a post | ||||
|     """ | ||||
|     with open(postFilename, 'r') as fp: | ||||
|         postJson=commentjson.load(fp) | ||||
|         if not postJson.get('object'): | ||||
|             if debug: | ||||
|                 pprint(postJson) | ||||
|                 print('DEBUG: post '+objectUrl+' has no object') | ||||
|             return | ||||
|         if not objectUrl.endswith('/likes'): | ||||
|             objectUrl=objectUrl+'/likes' | ||||
|         if not postJson.get('likes'): | ||||
|         if not postJson['object'].get('likes'): | ||||
|             if debug: | ||||
|                 print('DEBUG: Adding initial likes to '+objectUrl) | ||||
|             likesJson = { | ||||
|                 'id': objectUrl, | ||||
|                 'type': 'Collection', | ||||
|  | @ -92,17 +40,103 @@ def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None: | |||
|                      | ||||
|                 }]                 | ||||
|             } | ||||
|             postJson['likes']=likesJson | ||||
|             postJson['object']['likes']=likesJson | ||||
|         else: | ||||
|             if postJson['likes'].get('items'): | ||||
|             if postJson['object']['likes'].get('items'): | ||||
|                 for likeItem in postJson['likes']['items']: | ||||
|                     if likeItem['actor']==actor: | ||||
|                         return | ||||
|                     if likeItem.get('actor'): | ||||
|                         if likeItem['actor']==actor: | ||||
|                             return | ||||
|                 newLike={ | ||||
|                     'type': 'Like', | ||||
|                     'actor': actor | ||||
|                 } | ||||
|                 postJson['likes']['items'].append(newLike) | ||||
|                 postJson['likes']['totalItems']=len(postJson['likes']['items']) | ||||
|                 postJson['object']['likes']['items'].append(newLike) | ||||
|                 postJson['object']['likes']['totalItems']=len(postJson['likes']['items']) | ||||
|             else: | ||||
|                 if debug: | ||||
|                     print('DEBUG: likes section of post has no items list') | ||||
| 
 | ||||
|         if debug: | ||||
|             print('DEBUG: saving post with likes added') | ||||
|         with open(postFilename, 'w') as fp: | ||||
|             commentjson.dump(postJson, fp, indent=4, sort_keys=True) | ||||
| 
 | ||||
| def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ | ||||
|          ccList: [],httpPrefix: str,objectUrl: str,clientToServer: bool, \ | ||||
|          sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}, \ | ||||
|          debug: bool) -> {}: | ||||
|     """Creates a like | ||||
|     ccUrl might be a specific person whose post was liked | ||||
|     objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase | ||||
|     """ | ||||
|     if not urlPermitted(objectUrl,federationList,"inbox:write"): | ||||
|         return None | ||||
| 
 | ||||
|     fullDomain=domain | ||||
|     if port!=80 and port!=443: | ||||
|         if ':' not in domain: | ||||
|             fullDomain=domain+':'+str(port) | ||||
| 
 | ||||
|     newLikeJson = { | ||||
|         'type': 'Like', | ||||
|         'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, | ||||
|         'object': objectUrl, | ||||
|         'to': [httpPrefix+'://'+fullDomain+'/users/'+nickname+'/followers'], | ||||
|         'cc': [] | ||||
|     } | ||||
|     if ccList: | ||||
|         if len(ccList)>0: | ||||
|             newLikeJson['cc']=ccList | ||||
| 
 | ||||
|     # Extract the domain and nickname from a statuses link | ||||
|     likedPostNickname=None | ||||
|     likedPostDomain=None | ||||
|     likedPostPort=None | ||||
|     if '/users/' in objectUrl: | ||||
|         likedPostNickname=getNicknameFromActor(objectUrl) | ||||
|         likedPostDomain,likedPostPort=getDomainFromActor(objectUrl) | ||||
| 
 | ||||
|     if likedPostNickname: | ||||
|         postFilename=locatePost(baseDir,nickname,domain,objectUrl) | ||||
|         if not postFilename: | ||||
|             return None | ||||
|          | ||||
|         updateLikesCollection(postFilename,objectUrl,newLikeJson['actor'],debug) | ||||
|          | ||||
|         sendSignedJson(newLikeJson,session,baseDir, \ | ||||
|                        nickname,domain,port, \ | ||||
|                        likedPostNickname,likedPostDomain,likedPostPort, \ | ||||
|                        'https://www.w3.org/ns/activitystreams#Public', \ | ||||
|                        httpPrefix,True,clientToServer,federationList, \ | ||||
|                        sendThreads,postLog,cachedWebfingers,personCache,debug) | ||||
| 
 | ||||
|     return newLikeJson | ||||
| 
 | ||||
| def likePost(session,baseDir: str,federationList: [], \ | ||||
|              nickname: str,domain: str,port: int,httpPrefix: str, \ | ||||
|              likeNickname: str,likeDomain: str,likePort: int, \ | ||||
|              ccList: [], \ | ||||
|              likeStatusNumber: int,clientToServer: bool, \ | ||||
|              sendThreads: [],postLog: [], \ | ||||
|              personCache: {},cachedWebfingers: {}, \ | ||||
|              debug: bool) -> {}: | ||||
|     """Likes a given status post | ||||
|     """ | ||||
|     likeDomain=likeDomain | ||||
|     if likePort!=80 and likePort!=443: | ||||
|         likeDomain=likeDomain+':'+str(likePort) | ||||
| 
 | ||||
|     objectUrl = \ | ||||
|         httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \ | ||||
|         '/statuses/'+str(likeStatusNumber) | ||||
| 
 | ||||
|     if likePort!=80 and likePort!=443: | ||||
|         ccUrl=httpPrefix+'://'+likeDomain+':'+str(likePort)+'/users/'+likeNickname | ||||
|     else: | ||||
|         ccUrl=httpPrefix+'://'+likeDomain+'/users/'+likeNickname | ||||
|          | ||||
|     return like(session,baseDir,federationList,nickname,domain,port, \ | ||||
|                 ccList,httpPrefix,objectUrl,clientToServer, \ | ||||
|                 sendThreads,postLog,personCache,cachedWebfingers,debug) | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,6 +88,14 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \ | |||
|             os.mkdir(baseDir+peopleSubdir) | ||||
|         if not os.path.isdir(baseDir+peopleSubdir+'/'+handle): | ||||
|             os.mkdir(baseDir+peopleSubdir+'/'+handle) | ||||
|         if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/inbox'): | ||||
|             os.mkdir(baseDir+peopleSubdir+'/'+handle+'/inbox') | ||||
|         if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/outbox'): | ||||
|             os.mkdir(baseDir+peopleSubdir+'/'+handle+'/outbox') | ||||
|         if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/ocap'): | ||||
|             os.mkdir(baseDir+peopleSubdir+'/'+handle+'/ocap') | ||||
|         if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/queue'): | ||||
|             os.mkdir(baseDir+peopleSubdir+'/'+handle+'/queue') | ||||
|         filename=baseDir+peopleSubdir+'/'+handle+'.json' | ||||
|         with open(filename, 'w') as fp: | ||||
|             commentjson.dump(newPerson, fp, indent=4, sort_keys=False) | ||||
|  |  | |||
							
								
								
									
										69
									
								
								tests.py
								
								
								
								
							
							
						
						
									
										69
									
								
								tests.py
								
								
								
								
							|  | @ -36,13 +36,13 @@ from follow import unfollowPerson | |||
| from follow import unfollowerOfPerson | ||||
| from follow import getFollowersOfPerson | ||||
| from follow import sendFollowRequest | ||||
| from follow import getFollowersOfActor | ||||
| from person import createPerson | ||||
| from person import setPreferredNickname | ||||
| from person import setBio | ||||
| from auth import createBasicAuthHeader | ||||
| from auth import authorizeBasic | ||||
| from auth import storeBasicCredentials | ||||
| from like import likePost | ||||
| 
 | ||||
| testServerAliceRunning = False | ||||
| testServerBobRunning = False | ||||
|  | @ -210,7 +210,6 @@ def testPostMessageBetweenServers(): | |||
| 
 | ||||
|     httpPrefix='http' | ||||
|     useTor=False | ||||
|     federationList=['127.0.0.50','127.0.0.100'] | ||||
| 
 | ||||
|     baseDir=os.getcwd() | ||||
|     if os.path.isdir(baseDir+'/.tests'): | ||||
|  | @ -223,12 +222,13 @@ def testPostMessageBetweenServers(): | |||
|     aliceDir=baseDir+'/.tests/alice' | ||||
|     aliceDomain='127.0.0.50' | ||||
|     alicePort=61935 | ||||
|     thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,True,True,ocapAlways),daemon=True) | ||||
| 
 | ||||
|     bobDir=baseDir+'/.tests/bob' | ||||
|     bobDomain='127.0.0.100' | ||||
|     bobPort=61936 | ||||
|     thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,True,True,ocapAlways),daemon=True) | ||||
|     federationList=[bobDomain,aliceDomain] | ||||
| 
 | ||||
|     thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True) | ||||
|     thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True) | ||||
| 
 | ||||
|     thrAlice.start() | ||||
|     thrBob.start() | ||||
|  | @ -241,6 +241,7 @@ def testPostMessageBetweenServers(): | |||
|          | ||||
|     time.sleep(1) | ||||
| 
 | ||||
|     print('\n\n*******************************************************') | ||||
|     print('Alice sends to Bob') | ||||
|     os.chdir(aliceDir) | ||||
|     sessionAlice = createSession(aliceDomain,alicePort,useTor) | ||||
|  | @ -255,6 +256,11 @@ def testPostMessageBetweenServers(): | |||
|     ccUrl=None | ||||
|     alicePersonCache={} | ||||
|     aliceCachedWebfingers={} | ||||
| 
 | ||||
|     # nothing in Alice's outbox | ||||
|     outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' | ||||
|     assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0 | ||||
| 
 | ||||
|     sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) | ||||
|     print('sendResult: '+str(sendResult)) | ||||
| 
 | ||||
|  | @ -263,9 +269,53 @@ def testPostMessageBetweenServers(): | |||
|     for i in range(30): | ||||
|         if os.path.isdir(inboxPath): | ||||
|             if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0: | ||||
|                 break | ||||
|                 if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1: | ||||
|                     break | ||||
|         time.sleep(1) | ||||
| 
 | ||||
|     # inbox item created | ||||
|     assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1 | ||||
|     # queue item removed | ||||
|     assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0 | ||||
| 
 | ||||
|     #print('\n\n*******************************************************') | ||||
|     #print("Bob likes Alice's post") | ||||
| 
 | ||||
|     #followerOfPerson(bobDir,'bob',bobDomain,'alice',aliceDomain+':'+str(alicePort),federationList,True) | ||||
|     #followPerson(aliceDir,'alice',aliceDomain,'bob',bobDomain+':'+str(bobPort),federationList,True) | ||||
|     #followList=getFollowersOfPerson(bobDir,'bob',bobDomain,'followers.txt') | ||||
|     #assert len(followList)==1 | ||||
| 
 | ||||
|     #sessionBob = createSession(bobDomain,bobPort,useTor) | ||||
|     #bobSendThreads = [] | ||||
|     #bobPostLog = [] | ||||
|     #bobPersonCache={} | ||||
|     #bobCachedWebfingers={} | ||||
|     #statusNumber=None | ||||
|     #outboxPostFilename=None | ||||
|     #outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' | ||||
|     #for name in os.listdir(outboxPath): | ||||
|     #    if '#statuses#' in name: | ||||
|     #        statusNumber=int(name.split('#statuses#')[1].replace('.json','')) | ||||
|     #        outboxPostFilename=outboxPath+'/'+name | ||||
|     #assert statusNumber | ||||
|     #assert outboxPostFilename | ||||
|     #assert likePost(sessionBob,bobDir,federationList, \ | ||||
|     #                'bob',bobDomain,bobPort,httpPrefix, \ | ||||
|     #                'alice',aliceDomain,alicePort,[], \ | ||||
|     #                statusNumber,False,bobSendThreads,bobPostLog, \ | ||||
|     #                bobPersonCache,bobCachedWebfingers,True) | ||||
| 
 | ||||
|     #for i in range(20): | ||||
|     #    if 'likes' in open(outboxPostFilename).read(): | ||||
|     #        break | ||||
|     #    time.sleep(1) | ||||
| 
 | ||||
|     #with open(outboxPostFilename, 'r') as fp: | ||||
|     #    alicePostJson=commentjson.load(fp) | ||||
|     #    pprint(alicePostJson) | ||||
|     #assert 'likes' in open(outboxPostFilename).read() | ||||
|      | ||||
|     # stop the servers | ||||
|     thrAlice.kill() | ||||
|     thrAlice.join() | ||||
|  | @ -275,11 +325,6 @@ def testPostMessageBetweenServers(): | |||
|     thrBob.join() | ||||
|     assert thrBob.isAlive()==False | ||||
| 
 | ||||
|     # inbox item created | ||||
|     assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1 | ||||
|     # queue item removed | ||||
|     assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0 | ||||
| 
 | ||||
|     os.chdir(baseDir) | ||||
|     shutil.rmtree(aliceDir) | ||||
|     shutil.rmtree(bobDir) | ||||
|  | @ -439,7 +484,7 @@ def testFollowBetweenServers(): | |||
| 
 | ||||
|     assert aliceMessageArrived==True | ||||
|     print('Message from Alice to Bob succeeded, since it was granted capabilities') | ||||
| 
 | ||||
|      | ||||
|     print('\n\n*********************************************************') | ||||
|     print("\nBob changes Alice's capabilities so that she can't reply on his posts") | ||||
|     bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json' | ||||
|  |  | |||
							
								
								
									
										27
									
								
								utils.py
								
								
								
								
							
							
						
						
									
										27
									
								
								utils.py
								
								
								
								
							|  | @ -44,6 +44,8 @@ def createInboxQueueDir(nickname: str,domain: str,baseDir: str) -> str: | |||
| def domainPermitted(domain: str, federationList: []): | ||||
|     if len(federationList)==0: | ||||
|         return True | ||||
|     if ':' in domain: | ||||
|         domain=domain.split(':')[0] | ||||
|     if domain in federationList: | ||||
|         return True | ||||
|     return False | ||||
|  | @ -91,6 +93,8 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ | |||
|         if debug: | ||||
|             print('DEBUG: follow of domain '+followDomain+' not permitted') | ||||
|         return False | ||||
|     if debug: | ||||
|         print('DEBUG: follow of domain '+followDomain) | ||||
|     handle=nickname.lower()+'@'+domain.lower() | ||||
|     handleToFollow=followNickname.lower()+'@'+followDomain.lower() | ||||
|     if not os.path.isdir(baseDir+'/accounts'): | ||||
|  | @ -100,10 +104,33 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ | |||
|     filename=baseDir+'/accounts/'+handle+'/'+followFile | ||||
|     if os.path.isfile(filename): | ||||
|         if handleToFollow in open(filename).read(): | ||||
|             if debug: | ||||
|                 print('DEBUG: follow already exists') | ||||
|             return True | ||||
|         with open(filename, "a") as followfile: | ||||
|             followfile.write(handleToFollow+'\n') | ||||
|             if debug: | ||||
|                 print('DEBUG: follow added') | ||||
|             return True | ||||
|     if debug: | ||||
|         print('DEBUG: creating new following file') | ||||
|     with open(filename, "w") as followfile: | ||||
|         followfile.write(handleToFollow+'\n') | ||||
|     return True | ||||
| 
 | ||||
| def locatePost(baseDir: str,nickname: str,domain: str,postUrl: str): | ||||
|     """Returns the filename for the given status post url | ||||
|     """ | ||||
|     boxName='outbox' | ||||
|     postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' | ||||
|     if not os.path.isfile(postFilename): | ||||
|         # if this post in the inbox of the person? | ||||
|         boxName='inbox' | ||||
|         postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' | ||||
|         if not os.path.isfile(postFilename): | ||||
|             # if this post in the shared inbox? | ||||
|             handle='inbox@'+domain | ||||
|             postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' | ||||
|             if not os.path.isfile(postFilename): | ||||
|                 postFilename=None | ||||
|     return postFilename | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue