| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | __filename__ = "follow.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2019-08-29 13:35:29 +00:00
										 |  |  | __version__ = "1.0.0" | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							|  |  |  | __email__ = "bob@freedombone.net" | 
					
						
							|  |  |  | __status__ = "Production" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import json | 
					
						
							| 
									
										
										
										
											2019-10-11 18:03:58 +00:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | from pprint import pprint | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | from utils import validNickname | 
					
						
							| 
									
										
										
										
											2019-07-02 10:39:55 +00:00
										 |  |  | from utils import domainPermitted | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  | from utils import getDomainFromActor | 
					
						
							|  |  |  | from utils import getNicknameFromActor | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  | from utils import getStatusNumber | 
					
						
							|  |  |  | from utils import followPerson | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | from posts import sendSignedJson | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  | from posts import getPersonBox | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  | from utils import loadJson | 
					
						
							|  |  |  | from utils import saveJson | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  | from acceptreject import createAccept | 
					
						
							| 
									
										
										
										
											2019-09-09 12:19:00 +00:00
										 |  |  | from acceptreject import createReject | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  | from webfinger import webfingerHandle | 
					
						
							|  |  |  | from auth import createBasicAuthHeader | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  | from auth import createPassword | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  | from session import postJson | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  | def removeFromFollowBase(baseDir: str, \ | 
					
						
							|  |  |  |                          nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |                          acceptOrDenyHandle: str,followFile: str, \ | 
					
						
							|  |  |  |                          debug: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |     """Removes a handle from follow requests or rejects file
 | 
					
						
							| 
									
										
										
										
											2019-09-18 17:04:19 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     accountsDir=baseDir+'/accounts/'+handle | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |     approveFollowsFilename=accountsDir+'/'+followFile+'.txt' | 
					
						
							| 
									
										
										
										
											2019-09-18 17:04:19 +00:00
										 |  |  |     if not os.path.isfile(approveFollowsFilename): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('WARN: Follow requests file '+approveFollowsFilename+' not found') | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |     if acceptOrDenyHandle not in open(approveFollowsFilename).read(): | 
					
						
							| 
									
										
										
										
											2019-09-18 17:04:19 +00:00
										 |  |  |         return | 
					
						
							|  |  |  |     approvefilenew = open(approveFollowsFilename+'.new', 'w+') | 
					
						
							|  |  |  |     with open(approveFollowsFilename, 'r') as approvefile: | 
					
						
							|  |  |  |         for approveHandle in approvefile: | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |             if not approveHandle.startswith(acceptOrDenyHandle): | 
					
						
							| 
									
										
										
										
											2019-09-18 17:04:19 +00:00
										 |  |  |                 approvefilenew.write(approveHandle) | 
					
						
							|  |  |  |     approvefilenew.close() | 
					
						
							|  |  |  |     os.rename(approveFollowsFilename+'.new',approveFollowsFilename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  | def removeFromFollowRequests(baseDir: str, \ | 
					
						
							|  |  |  |                              nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |                              denyHandle: str,debug: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |     """Removes a handle from follow requests
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     removeFromFollowBase(baseDir,nickname,domain, \ | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |                          denyHandle,'followrequests',debug) | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def removeFromFollowRejects(baseDir: str, \ | 
					
						
							|  |  |  |                             nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |                             acceptHandle: str,debug: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  |     """Removes a handle from follow rejects
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     removeFromFollowBase(baseDir,nickname,domain, \ | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |                          acceptHandle,'followrejects',debug) | 
					
						
							| 
									
										
										
										
											2019-10-06 09:48:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-29 19:46:30 +00:00
										 |  |  | def isFollowingActor(baseDir: str,nickname: str,domain: str,actor: str) -> bool: | 
					
						
							|  |  |  |     """Is the given actor a follower of the given nickname?
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     if not os.path.isdir(baseDir+'/accounts/'+handle): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-31 12:25:42 +00:00
										 |  |  |     followingFile=baseDir+'/accounts/'+handle+'/following.txt'     | 
					
						
							|  |  |  |     if not os.path.isfile(followingFile): | 
					
						
							| 
									
										
										
										
											2019-07-29 19:46:30 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-31 12:25:42 +00:00
										 |  |  |     if actor in open(followingFile).read(): | 
					
						
							| 
									
										
										
										
											2019-07-29 19:46:30 +00:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2019-08-31 12:25:42 +00:00
										 |  |  |     followingNickname=getNicknameFromActor(actor) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |     if not followingNickname: | 
					
						
							|  |  |  |         print('WARN: unable to find nickname in '+actor) | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-31 12:25:42 +00:00
										 |  |  |     followingDomain,followingPort=getDomainFromActor(actor) | 
					
						
							|  |  |  |     followingHandle=followingNickname+'@'+followingDomain | 
					
						
							|  |  |  |     if followingPort: | 
					
						
							|  |  |  |         if followingPort!=80 and followingPort!=443: | 
					
						
							|  |  |  |             if ':' not in followingHandle: | 
					
						
							|  |  |  |                 followingHandle+=':'+str(followingPort) | 
					
						
							|  |  |  |     if followingHandle in open(followingFile).read(): | 
					
						
							| 
									
										
										
										
											2019-07-29 19:46:30 +00:00
										 |  |  |         return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def getFollowersOfPerson(baseDir: str, \ | 
					
						
							|  |  |  |                          nickname: str,domain: str, \ | 
					
						
							|  |  |  |                          followFile='following.txt') -> []: | 
					
						
							| 
									
										
										
										
											2019-07-05 12:35:29 +00:00
										 |  |  |     """Returns a list containing the followers of the given person
 | 
					
						
							|  |  |  |     Used by the shared inbox to know who to send incoming mail to | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     followers=[] | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-07-29 19:46:30 +00:00
										 |  |  |     handle=nickname+'@'+domain | 
					
						
							| 
									
										
										
										
											2019-07-05 12:35:29 +00:00
										 |  |  |     if not os.path.isdir(baseDir+'/accounts/'+handle): | 
					
						
							|  |  |  |         return followers | 
					
						
							|  |  |  |     for subdir, dirs, files in os.walk(baseDir+'/accounts'): | 
					
						
							|  |  |  |         for account in dirs: | 
					
						
							|  |  |  |             filename = os.path.join(subdir, account)+'/'+followFile | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |             if account == handle or account.startswith('inbox@'): | 
					
						
							| 
									
										
										
										
											2019-07-05 12:35:29 +00:00
										 |  |  |                 continue | 
					
						
							|  |  |  |             if not os.path.isfile(filename): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             with open(filename, 'r') as followingfile: | 
					
						
							|  |  |  |                 for followingHandle in followingfile: | 
					
						
							|  |  |  |                     if followingHandle.replace('\n','')==handle: | 
					
						
							|  |  |  |                         if account not in followers: | 
					
						
							|  |  |  |                             followers.append(account) | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |     return followers | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  | def followerOfPerson(baseDir: str,nickname: str, domain: str, \ | 
					
						
							|  |  |  |                      followerNickname: str, followerDomain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  |                      federationList: [],debug :bool) -> bool: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Adds a follower of the given person
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  |     return followPerson(baseDir,nickname,domain, \ | 
					
						
							|  |  |  |                         followerNickname,followerDomain, \ | 
					
						
							|  |  |  |                         federationList,debug,'followers.txt') | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-31 15:43:19 +00:00
										 |  |  | def isFollowerOfPerson(baseDir: str,nickname: str, domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-31 15:17:07 +00:00
										 |  |  |                        followerNickname: str, followerDomain: str) -> bool: | 
					
						
							|  |  |  |     """is the given nickname a follower of followerNickname?
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |     followersFile=baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt' | 
					
						
							|  |  |  |     if not os.path.isfile(followersFile): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     return followerNickname+'@'+followerDomain in open(followersFile).read() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  | def unfollowPerson(baseDir: str,nickname: str, domain: str, \ | 
					
						
							|  |  |  |                    followNickname: str, followDomain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |                    followFile='following.txt', \ | 
					
						
							|  |  |  |                    debug=False) -> bool: | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  |     """Removes a person to the follow list
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-10-30 21:44:16 +00:00
										 |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     handleToUnfollow=followNickname+'@'+followDomain | 
					
						
							| 
									
										
										
										
											2019-07-02 17:20:15 +00:00
										 |  |  |     if not os.path.isdir(baseDir+'/accounts'): | 
					
						
							|  |  |  |         os.mkdir(baseDir+'/accounts') | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  |     if not os.path.isdir(baseDir+'/accounts/'+handle): | 
					
						
							|  |  |  |         os.mkdir(baseDir+'/accounts/'+handle) | 
					
						
							|  |  |  |     filename=baseDir+'/accounts/'+handle+'/'+followFile | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     if not os.path.isfile(filename): | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follow file '+filename+' was not found') | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |         return False | 
					
						
							|  |  |  |     if handleToUnfollow not in open(filename).read(): | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: handle to unfollow '+handleToUnfollow+' is not in '+filename) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |         return | 
					
						
							|  |  |  |     with open(filename, "r") as f: | 
					
						
							|  |  |  |         lines = f.readlines() | 
					
						
							|  |  |  |     with open(filename, "w") as f: | 
					
						
							|  |  |  |         for line in lines: | 
					
						
							|  |  |  |             if line.strip("\n") != handleToUnfollow: | 
					
						
							|  |  |  |                 f.write(line) | 
					
						
							| 
									
										
										
										
											2019-10-30 21:44:16 +00:00
										 |  |  |     return True | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  | def unfollowerOfPerson(baseDir: str,nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |                        followerNickname: str,followerDomain: str, \ | 
					
						
							|  |  |  |                        debug=False) -> bool: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Remove a follower of a person
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-17 10:38:10 +00:00
										 |  |  |     return unfollowPerson(baseDir,nickname,domain, \ | 
					
						
							| 
									
										
										
										
											2019-07-17 11:54:13 +00:00
										 |  |  |                           followerNickname,followerDomain, \ | 
					
						
							|  |  |  |                           'followers.txt',debug) | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def clearFollows(baseDir: str,nickname: str,domain: str, \ | 
					
						
							|  |  |  |                  followFile='following.txt') -> None: | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  |     """Removes all follows
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     handle=nickname.lower()+'@'+domain.lower() | 
					
						
							| 
									
										
										
										
											2019-07-02 17:20:15 +00:00
										 |  |  |     if not os.path.isdir(baseDir+'/accounts'): | 
					
						
							|  |  |  |         os.mkdir(baseDir+'/accounts') | 
					
						
							| 
									
										
										
										
											2019-06-29 18:23:13 +00:00
										 |  |  |     if not os.path.isdir(baseDir+'/accounts/'+handle): | 
					
						
							|  |  |  |         os.mkdir(baseDir+'/accounts/'+handle) | 
					
						
							|  |  |  |     filename=baseDir+'/accounts/'+handle+'/'+followFile | 
					
						
							|  |  |  |     if os.path.isfile(filename): | 
					
						
							|  |  |  |         os.remove(filename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  | def clearFollowers(baseDir: str,nickname: str,domain: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Removes all followers
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     clearFollows(baseDir,nickname, domain,'followers.txt') | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def getNoOfFollows(baseDir: str,nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |                    authenticated: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                    followFile='following.txt') -> int: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Returns the number of follows or followers
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |     # only show number of followers to authenticated | 
					
						
							|  |  |  |     # account holders | 
					
						
							| 
									
										
										
										
											2019-08-17 11:48:12 +00:00
										 |  |  |     #if not authenticated: | 
					
						
							|  |  |  |     #    return 9999 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     handle=nickname.lower()+'@'+domain.lower() | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     filename=baseDir+'/accounts/'+handle+'/'+followFile | 
					
						
							|  |  |  |     if not os.path.isfile(filename): | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  |     ctr = 0 | 
					
						
							|  |  |  |     with open(filename, "r") as f: | 
					
						
							|  |  |  |         lines = f.readlines() | 
					
						
							|  |  |  |         for line in lines: | 
					
						
							|  |  |  |             if '#' not in line: | 
					
						
							|  |  |  |                 if '@' in line and '.' in line and not line.startswith('http'): | 
					
						
							|  |  |  |                     ctr += 1 | 
					
						
							|  |  |  |                 elif line.startswith('http') and '/users/' in line: | 
					
						
							|  |  |  |                     ctr += 1 | 
					
						
							|  |  |  |     return ctr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  | def getNoOfFollowers(baseDir: str,nickname: str,domain: str,authenticated: bool) -> int: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Returns the number of followers of the given person
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |     return getNoOfFollows(baseDir,nickname,domain,authenticated,'followers.txt') | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |                      httpPrefix: str, authenticated: bool, | 
					
						
							|  |  |  |                      followsPerPage=12, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                      followFile='following') -> {}: | 
					
						
							| 
									
										
										
										
											2019-06-29 21:13:44 +00:00
										 |  |  |     """Returns the following and followers feeds from GET requests
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |     # Show a small number of follows to non-authenticated viewers | 
					
						
							|  |  |  |     if not authenticated: | 
					
						
							|  |  |  |         followsPerPage=6 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     if '/'+followFile not in path: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     # handle page numbers | 
					
						
							|  |  |  |     headerOnly=True | 
					
						
							|  |  |  |     pageNumber=None     | 
					
						
							|  |  |  |     if '?page=' in path: | 
					
						
							|  |  |  |         pageNumber=path.split('?page=')[1] | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |         if pageNumber=='true' or not authenticated: | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |             pageNumber=1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 pageNumber=int(pageNumber) | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |         path=path.split('?page=')[0] | 
					
						
							|  |  |  |         headerOnly=False | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if not path.endswith('/'+followFile): | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     nickname=None | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     if path.startswith('/users/'): | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |         nickname=path.replace('/users/','',1).replace('/'+followFile,'') | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     if path.startswith('/@'): | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |         nickname=path.replace('/@','',1).replace('/'+followFile,'') | 
					
						
							|  |  |  |     if not nickname: | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-08-23 13:47:29 +00:00
										 |  |  |     if not validNickname(domain,nickname): | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-06-30 19:01:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     if headerOnly: | 
					
						
							|  |  |  |         following = { | 
					
						
							|  |  |  |             '@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |             'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page=1', | 
					
						
							|  |  |  |             'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, | 
					
						
							| 
									
										
										
										
											2019-07-19 08:40:51 +00:00
										 |  |  |             'totalItems': getNoOfFollows(baseDir,nickname,domain,authenticated), | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |             'type': 'OrderedCollection'} | 
					
						
							|  |  |  |         return following | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not pageNumber: | 
					
						
							|  |  |  |         pageNumber=1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nextPageNumber=int(pageNumber+1) | 
					
						
							|  |  |  |     following = { | 
					
						
							|  |  |  |         '@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |         'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(pageNumber), | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |         'orderedItems': [], | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |         'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |         'totalItems': 0, | 
					
						
							|  |  |  |         'type': 'OrderedCollectionPage'}         | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 11:44:31 +00:00
										 |  |  |     handleDomain=domain | 
					
						
							|  |  |  |     if ':' in handleDomain: | 
					
						
							|  |  |  |         handleDomain=domain.split(':')[0] | 
					
						
							|  |  |  |     handle=nickname.lower()+'@'+handleDomain.lower() | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     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: | 
					
						
							| 
									
										
										
										
											2019-07-22 11:44:31 +00:00
										 |  |  |                 if '@' in line and not line.startswith('http'): | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |                     pageCtr += 1 | 
					
						
							|  |  |  |                     totalCtr += 1 | 
					
						
							|  |  |  |                     if currPage==pageNumber: | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                         url = httpPrefix + '://' + line.lower().replace('\n','').split('@')[1] + \ | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |                             '/users/' + line.lower().replace('\n','').split('@')[0] | 
					
						
							|  |  |  |                         following['orderedItems'].append(url) | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                 elif (line.startswith('http') or line.startswith('dat')) and '/users/' in line: | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |                     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: | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |         following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) | 
					
						
							| 
									
										
										
										
											2019-06-29 20:21:37 +00:00
										 |  |  |     return following | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 20:03:50 +00:00
										 |  |  | def followApprovalRequired(baseDir: str,nicknameToFollow: str, \ | 
					
						
							|  |  |  |                            domainToFollow: str,debug: bool) -> bool: | 
					
						
							|  |  |  |     """ Returns the policy for follower approvals
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     manuallyApproveFollows=False | 
					
						
							| 
									
										
										
										
											2019-08-31 14:42:35 +00:00
										 |  |  |     if ':' in domainToFollow: | 
					
						
							|  |  |  |         domainToFollow=domainToFollow.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-07-19 20:03:50 +00:00
										 |  |  |     actorFilename=baseDir+'/accounts/'+nicknameToFollow+'@'+domainToFollow+'.json' | 
					
						
							|  |  |  |     if os.path.isfile(actorFilename): | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |         actor=loadJson(actorFilename) | 
					
						
							| 
									
										
										
										
											2019-09-30 22:39:02 +00:00
										 |  |  |         if actor: | 
					
						
							| 
									
										
										
										
											2019-07-19 20:03:50 +00:00
										 |  |  |             if actor.get('manuallyApprovesFollowers'): | 
					
						
							|  |  |  |                 manuallyApproveFollows=actor['manuallyApprovesFollowers'] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('manuallyApprovesFollowers is missing from '+actorFilename) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: Actor file not found: '+actorFilename) | 
					
						
							|  |  |  |     return manuallyApproveFollows | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def storeFollowRequest(baseDir: str, \ | 
					
						
							|  |  |  |                        nicknameToFollow: str,domainToFollow: str,port: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-20 13:35:58 +00:00
										 |  |  |                        nickname: str,domain: str,fromPort: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |                        followJson: {}, \ | 
					
						
							|  |  |  |                        debug: bool) -> bool: | 
					
						
							|  |  |  |     """Stores the follow request for later use
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     accountsDir=baseDir+'/accounts/'+nicknameToFollow+'@'+domainToFollow | 
					
						
							| 
									
										
										
										
											2019-08-07 11:49:38 +00:00
										 |  |  |     if not os.path.isdir(accountsDir): | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     approveHandle=nickname+'@'+domain | 
					
						
							|  |  |  |     if fromPort: | 
					
						
							|  |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 approveHandle=nickname+'@'+domain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-26 22:38:09 +00:00
										 |  |  |     followersFilename=accountsDir+'/followers.txt' | 
					
						
							|  |  |  |     if os.path.isfile(followersFilename): | 
					
						
							|  |  |  |         if approveHandle in open(followersFilename).read(): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 print('DEBUG: '+ \ | 
					
						
							|  |  |  |                       nicknameToFollow+'@'+domainToFollow+ \ | 
					
						
							|  |  |  |                       ' already following '+approveHandle) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-18 19:01:07 +00:00
										 |  |  |     # should this follow be denied? | 
					
						
							|  |  |  |     denyFollowsFilename=accountsDir+'/followrejects.txt' | 
					
						
							|  |  |  |     if os.path.isfile(denyFollowsFilename): | 
					
						
							| 
									
										
										
										
											2019-09-18 19:05:08 +00:00
										 |  |  |         if approveHandle in open(denyFollowsFilename).read(): | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |             removeFromFollowRequests(baseDir,nicknameToFollow,domainToFollow,approveHandle,debug) | 
					
						
							| 
									
										
										
										
											2019-09-18 19:10:53 +00:00
										 |  |  |             print(approveHandle+' was already denied as a follower of '+nicknameToFollow) | 
					
						
							| 
									
										
										
										
											2019-09-18 19:01:07 +00:00
										 |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |     # add to a file which contains a list of requests | 
					
						
							| 
									
										
										
										
											2019-08-07 11:49:38 +00:00
										 |  |  |     approveFollowsFilename=accountsDir+'/followrequests.txt' | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |     if os.path.isfile(approveFollowsFilename): | 
					
						
							|  |  |  |         if approveHandle not in open(approveFollowsFilename).read(): | 
					
						
							|  |  |  |             with open(approveFollowsFilename, "a") as fp: | 
					
						
							|  |  |  |                 fp.write(approveHandle+'\n') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 print('DEBUG: '+approveHandle+' is already awaiting approval') | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         with open(approveFollowsFilename, "w") as fp: | 
					
						
							|  |  |  |             fp.write(approveHandle+'\n') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # store the follow request in its own directory | 
					
						
							|  |  |  |     # We don't rely upon the inbox because items in there could expire | 
					
						
							|  |  |  |     requestsDir=accountsDir+'/requests' | 
					
						
							|  |  |  |     if not os.path.isdir(requestsDir): | 
					
						
							|  |  |  |         os.mkdir(requestsDir) | 
					
						
							|  |  |  |     followActivityfilename=requestsDir+'/'+approveHandle+'.follow' | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |     return saveJson(followJson,followActivityfilename) | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                          port: int,sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                          cachedWebfingers: {},personCache: {}, \ | 
					
						
							|  |  |  |                          messageJson: {},federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                          debug : bool,projectVersion: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 17:54:08 +00:00
										 |  |  |                          acceptedCaps=["inbox:write","objects:read"]) -> bool: | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  |     """Receives a follow request within the POST section of HTTPServer
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |     if not messageJson['type'].startswith('Follow'): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:28 +00:00
										 |  |  |     print('Receiving follow request') | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     if not messageJson.get('actor'): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follow request has no actor') | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |     if '/users/' not in messageJson['actor'] and \ | 
					
						
							|  |  |  |        '/channel/' not in messageJson['actor'] and \ | 
					
						
							|  |  |  |        '/profile/' not in messageJson['actor']: | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-09-09 09:41:31 +00:00
										 |  |  |             print('DEBUG: "users" or "profile" missing from actor')             | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     domain,tempPort=getDomainFromActor(messageJson['actor']) | 
					
						
							|  |  |  |     fromPort=port | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |     domainFull=domain | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     if tempPort: | 
					
						
							|  |  |  |         fromPort=tempPort | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if tempPort!=80 and tempPort!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(tempPort) | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |     if not domainPermitted(domain,federationList): | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follower from domain not permitted - '+domain) | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     nickname=getNicknameFromActor(messageJson['actor']) | 
					
						
							|  |  |  |     if not nickname: | 
					
						
							| 
									
										
										
										
											2019-10-21 12:27:47 +00:00
										 |  |  |         # single user instance | 
					
						
							|  |  |  |         nickname='dev' | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-10-21 12:27:47 +00:00
										 |  |  |             print('DEBUG: follow request does not contain a nickname. Assuming single user instance.') | 
					
						
							| 
									
										
										
										
											2019-08-15 17:05:22 +00:00
										 |  |  |     if not messageJson.get('to'): | 
					
						
							|  |  |  |         messageJson['to']=messageJson['object'] | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     handle=nickname.lower()+'@'+domain.lower() | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |     if '/users/' not in messageJson['object'] and \ | 
					
						
							|  |  |  |        '/channel/' not in messageJson['object'] and \ | 
					
						
							|  |  |  |        '/profile/' not in messageJson['object']: | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-09-09 09:41:31 +00:00
										 |  |  |             print('DEBUG: "users" or "profile" not found within object') | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     domainToFollow,tempPort=getDomainFromActor(messageJson['object']) | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |     if not domainPermitted(domainToFollow,federationList): | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follow domain not permitted '+domainToFollow) | 
					
						
							| 
									
										
										
										
											2019-08-26 18:41:35 +00:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |     domainToFollowFull=domainToFollow | 
					
						
							|  |  |  |     if tempPort: | 
					
						
							|  |  |  |         if tempPort!=80 and tempPort!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if ':' not in domainToFollow: | 
					
						
							|  |  |  |                 domainToFollowFull=domainToFollow+':'+str(tempPort)             | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     nicknameToFollow=getNicknameFromActor(messageJson['object']) | 
					
						
							|  |  |  |     if not nicknameToFollow: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follow request does not contain a nickname for the account followed') | 
					
						
							| 
									
										
										
										
											2019-08-26 18:41:35 +00:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2019-08-04 21:25:19 +00:00
										 |  |  |     handleToFollow=nicknameToFollow+'@'+domainToFollow | 
					
						
							| 
									
										
										
										
											2019-07-02 18:17:04 +00:00
										 |  |  |     if domainToFollow==domain: | 
					
						
							|  |  |  |         if not os.path.isdir(baseDir+'/accounts/'+handleToFollow): | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                 print('DEBUG: followed account not found - '+ \ | 
					
						
							|  |  |  |                       baseDir+'/accounts/'+handleToFollow) | 
					
						
							| 
									
										
										
										
											2019-08-26 18:41:35 +00:00
										 |  |  |             return True | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2019-08-31 15:46:38 +00:00
										 |  |  |     if isFollowerOfPerson(baseDir, \ | 
					
						
							|  |  |  |                           nicknameToFollow,domainToFollowFull, \ | 
					
						
							|  |  |  |                           nickname,domainFull): | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |             print('DEBUG: '+nickname+'@'+domain+ \ | 
					
						
							|  |  |  |                   ' is already a follower of '+ \ | 
					
						
							|  |  |  |                   nicknameToFollow+'@'+domainToFollow) | 
					
						
							| 
									
										
										
										
											2019-08-26 18:41:35 +00:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2019-07-19 20:03:50 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     # what is the followers policy? | 
					
						
							|  |  |  |     if followApprovalRequired(baseDir,nicknameToFollow, \ | 
					
						
							|  |  |  |                               domainToFollow,debug): | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:28 +00:00
										 |  |  |         print('Storing follow request for approval') | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |         return storeFollowRequest(baseDir, \ | 
					
						
							|  |  |  |                                   nicknameToFollow,domainToFollow,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-20 13:35:58 +00:00
										 |  |  |                                   nickname,domain,fromPort, | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |                                   messageJson,debug) | 
					
						
							| 
									
										
										
										
											2019-08-31 14:42:35 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-09-01 20:03:20 +00:00
										 |  |  |         print('Follow request does not require approval') | 
					
						
							| 
									
										
										
										
											2019-09-01 20:28:43 +00:00
										 |  |  |         # update the followers | 
					
						
							|  |  |  |         if os.path.isdir(baseDir+'/accounts/'+nicknameToFollow+'@'+domainToFollow): | 
					
						
							|  |  |  |             followersFilename=baseDir+'/accounts/'+nicknameToFollow+'@'+domainToFollow+'/followers.txt' | 
					
						
							|  |  |  |             approveHandle=nickname+'@'+domain | 
					
						
							|  |  |  |             if fromPort: | 
					
						
							|  |  |  |                 approveHandle=approveHandle+':'+str(fromPort) | 
					
						
							|  |  |  |             print('Updating followers file: '+followersFilename+' adding '+approveHandle) | 
					
						
							|  |  |  |             if os.path.isfile(followersFilename): | 
					
						
							|  |  |  |                 if approveHandle not in open(followersFilename).read(): | 
					
						
							| 
									
										
										
										
											2019-10-26 15:15:38 +00:00
										 |  |  |                     try: | 
					
						
							|  |  |  |                         with open(followersFilename, 'r+') as followersFile: | 
					
						
							|  |  |  |                             content = followersFile.read() | 
					
						
							|  |  |  |                             followersFile.seek(0, 0) | 
					
						
							|  |  |  |                             followersFile.write(approveHandle+'\n'+content) | 
					
						
							|  |  |  |                     except Exception as e: | 
					
						
							|  |  |  |                         print('WARN: Failed to write entry to followers file '+str(e)) | 
					
						
							| 
									
										
										
										
											2019-09-01 20:28:43 +00:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 followersFile=open(followersFilename, "w+") | 
					
						
							|  |  |  |                 followersFile.write(approveHandle+'\n') | 
					
						
							|  |  |  |                 followersFile.close() | 
					
						
							| 
									
										
										
										
											2019-08-15 16:05:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print('Beginning follow accept') | 
					
						
							| 
									
										
										
										
											2019-07-20 13:31:20 +00:00
										 |  |  |     return followedAccountAccepts(session,baseDir,httpPrefix, \ | 
					
						
							|  |  |  |                                   nicknameToFollow,domainToFollow,port, \ | 
					
						
							|  |  |  |                                   nickname,domain,fromPort, \ | 
					
						
							|  |  |  |                                   messageJson['actor'],federationList, | 
					
						
							|  |  |  |                                   messageJson,acceptedCaps, \ | 
					
						
							|  |  |  |                                   sendThreads,postLog, \ | 
					
						
							|  |  |  |                                   cachedWebfingers,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                                   debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-19 20:03:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 08:33:18 +00:00
										 |  |  | def followedAccountAccepts(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                            nicknameToFollow: str,domainToFollow: str,port: int, \ | 
					
						
							|  |  |  |                            nickname: str,domain: str,fromPort: int, \ | 
					
						
							|  |  |  |                            personUrl: str,federationList: [], \ | 
					
						
							|  |  |  |                            followJson: {},acceptedCaps: [], \ | 
					
						
							|  |  |  |                            sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                            cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                            debug: bool,projectVersion: str): | 
					
						
							| 
									
										
										
										
											2019-07-20 08:33:18 +00:00
										 |  |  |     """The person receiving a follow request accepts the new follower
 | 
					
						
							|  |  |  |     and sends back an Accept activity | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-10-06 09:57:49 +00:00
										 |  |  |     acceptHandle=nickname+'@'+domain | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     # send accept back | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     if debug: | 
					
						
							| 
									
										
										
										
											2019-07-20 08:33:18 +00:00
										 |  |  |         print('DEBUG: sending Accept activity for follow request which arrived at '+ \ | 
					
						
							| 
									
										
										
										
											2019-10-06 09:57:49 +00:00
										 |  |  |               nicknameToFollow+'@'+domainToFollow+' back to '+acceptHandle) | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |     acceptJson=createAccept(baseDir,federationList, \ | 
					
						
							| 
									
										
										
										
											2019-07-07 13:53:12 +00:00
										 |  |  |                             nicknameToFollow,domainToFollow,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-20 08:33:18 +00:00
										 |  |  |                             personUrl,'',httpPrefix,followJson,acceptedCaps) | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         pprint(acceptJson) | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |         print('DEBUG: sending follow Accept from '+ \ | 
					
						
							|  |  |  |               nicknameToFollow+'@'+domainToFollow+ \ | 
					
						
							|  |  |  |               ' port '+str(port)+' to '+ \ | 
					
						
							| 
									
										
										
										
											2019-10-06 09:57:49 +00:00
										 |  |  |               acceptHandle+' port '+ str(fromPort)) | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     clientToServer=False | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     return sendSignedJson(acceptJson,session,baseDir, \ | 
					
						
							|  |  |  |                           nicknameToFollow,domainToFollow,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 15:17:21 +00:00
										 |  |  |                           nickname,domain,fromPort, '', \ | 
					
						
							|  |  |  |                           httpPrefix,True,clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                           federationList, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                           sendThreads,postLog,cachedWebfingers, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                           personCache,debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-09 12:19:00 +00:00
										 |  |  | def followedAccountRejects(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                            nicknameToFollow: str,domainToFollow: str,port: int, \ | 
					
						
							|  |  |  |                            nickname: str,domain: str,fromPort: int, \ | 
					
						
							|  |  |  |                            personUrl: str,federationList: [], \ | 
					
						
							|  |  |  |                            followJson: {}, \ | 
					
						
							|  |  |  |                            sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                            cachedWebfingers: {},personCache: {}, \ | 
					
						
							|  |  |  |                            debug: bool,projectVersion: str): | 
					
						
							|  |  |  |     """The person receiving a follow request rejects the new follower
 | 
					
						
							|  |  |  |     and sends back a Reject activity | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     # send reject back | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: sending Reject activity for follow request which arrived at '+ \ | 
					
						
							|  |  |  |               nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain) | 
					
						
							|  |  |  |     rejectJson=createReject(baseDir,federationList, \ | 
					
						
							|  |  |  |                             nicknameToFollow,domainToFollow,port, \ | 
					
						
							|  |  |  |                             personUrl,'',httpPrefix,followJson) | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         pprint(rejectJson) | 
					
						
							|  |  |  |         print('DEBUG: sending follow Reject from '+ \ | 
					
						
							|  |  |  |               nicknameToFollow+'@'+domainToFollow+ \ | 
					
						
							|  |  |  |               ' port '+str(port)+' to '+ \ | 
					
						
							|  |  |  |               nickname+'@'+domain+' port '+ str(fromPort)) | 
					
						
							|  |  |  |     clientToServer=False | 
					
						
							| 
									
										
										
										
											2019-09-18 17:30:19 +00:00
										 |  |  |     denyHandle=nickname+'@'+domain | 
					
						
							|  |  |  |     if fromPort: | 
					
						
							| 
									
										
										
										
											2019-09-18 17:31:14 +00:00
										 |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							| 
									
										
										
										
											2019-09-18 17:30:19 +00:00
										 |  |  |             denyHandle=denyHandle+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-10-15 09:12:58 +00:00
										 |  |  |     removeFromFollowRequests(baseDir,nicknameToFollow,domainToFollow,denyHandle,debug) | 
					
						
							| 
									
										
										
										
											2019-09-09 12:19:00 +00:00
										 |  |  |     return sendSignedJson(rejectJson,session,baseDir, \ | 
					
						
							|  |  |  |                           nicknameToFollow,domainToFollow,port, \ | 
					
						
							|  |  |  |                           nickname,domain,fromPort, '', \ | 
					
						
							|  |  |  |                           httpPrefix,True,clientToServer, \ | 
					
						
							|  |  |  |                           federationList, \ | 
					
						
							|  |  |  |                           sendThreads,postLog,cachedWebfingers, \ | 
					
						
							|  |  |  |                           personCache,debug,projectVersion) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def sendFollowRequest(session,baseDir: str, \ | 
					
						
							|  |  |  |                       nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                       followNickname: str,followDomain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-29 16:13:48 +00:00
										 |  |  |                       followPort: int,followHttpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                       clientToServer: bool,federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                       sendThreads: [],postLog: [],cachedWebfingers: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                       personCache: {},debug : bool, \ | 
					
						
							|  |  |  |                       projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-02 20:54:22 +00:00
										 |  |  |     """Gets the json object for sending a follow request
 | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     """    
 | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  |     if not domainPermitted(followDomain,federationList): | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-10-21 14:12:22 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-07-07 13:53:12 +00:00
										 |  |  |     fullDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     followActor=httpPrefix+'://'+domain+'/users/'+nickname | 
					
						
							|  |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fullDomain=domain+':'+str(port) | 
					
						
							|  |  |  |                 followActor=httpPrefix+'://'+domain+':'+str(port)+'/users/'+nickname | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     requestDomain=followDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if followPort: | 
					
						
							|  |  |  |         if followPort!=80 and followPort!=443: | 
					
						
							|  |  |  |             if ':' not in followDomain: | 
					
						
							|  |  |  |                 requestDomain=followDomain+':'+str(followPort) | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2019-10-21 14:12:22 +00:00
										 |  |  |     if followNickname: | 
					
						
							|  |  |  |         followedId=followHttpPrefix+'://'+requestDomain+'/users/'+followNickname | 
					
						
							|  |  |  |         followHandle=followNickname+'@'+requestDomain | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: sendFollowRequest - assuming single user instance') | 
					
						
							|  |  |  |         followedId=followHttpPrefix+'://'+requestDomain | 
					
						
							|  |  |  |         singleUserNickname='dev' | 
					
						
							|  |  |  |         followHandle=singleUserNickname+'@'+requestDomain | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 20:32:21 +00:00
										 |  |  |     newFollowJson = { | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |         '@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         'id': followActor+'/statuses/'+str(statusNumber), | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  |         'type': 'Follow', | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |         'actor': followActor, | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |         'object': followedId | 
					
						
							| 
									
										
										
										
											2019-07-02 18:38:51 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-02 19:05:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-06 10:34:40 +00:00
										 |  |  |     # Remove any follow requests rejected for the account being followed. | 
					
						
							|  |  |  |     # It's assumed that if you are following someone then you are | 
					
						
							|  |  |  |     # ok with them following back. If this isn't the case then a rejected | 
					
						
							|  |  |  |     # follow request will block them again. | 
					
						
							|  |  |  |     removeFromFollowRejects(baseDir, \ | 
					
						
							|  |  |  |                             nickname,domain, \ | 
					
						
							| 
									
										
										
										
											2019-10-21 14:12:22 +00:00
										 |  |  |                             followHandle,debug) | 
					
						
							| 
									
										
										
										
											2019-10-06 10:34:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 20:32:21 +00:00
										 |  |  |     sendSignedJson(newFollowJson,session,baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 19:24:52 +00:00
										 |  |  |                    followNickname,followDomain,followPort, \ | 
					
						
							|  |  |  |                    'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |                    httpPrefix,True,clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                    federationList, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                    sendThreads,postLog,cachedWebfingers,personCache, \ | 
					
						
							|  |  |  |                    debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-05 20:32:21 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return newFollowJson | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def sendFollowRequestViaServer(baseDir: str,session, \ | 
					
						
							|  |  |  |                                fromNickname: str,password: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |                                fromDomain: str,fromPort: int, \ | 
					
						
							|  |  |  |                                followNickname: str,followDomain: str,followPort: int, \ | 
					
						
							|  |  |  |                                httpPrefix: str, \ | 
					
						
							|  |  |  |                                cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                                debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |     """Creates a follow request via c2s
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendFollowRequestViaServer') | 
					
						
							|  |  |  |         return 6 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fromDomainFull=fromDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if fromPort: | 
					
						
							|  |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							|  |  |  |             if ':' not in fromDomain: | 
					
						
							|  |  |  |                 fromDomainFull=fromDomain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-18 11:35:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |     followDomainFull=followDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if followPort: | 
					
						
							|  |  |  |         if followPort!=80 and followPort!=443: | 
					
						
							|  |  |  |             if ':' not in followDomain: | 
					
						
							|  |  |  |                 followDomainFull=followDomain+':'+str(followPort) | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname     | 
					
						
							|  |  |  |     followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							|  |  |  |     newFollowJson = { | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |         '@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         'id': followActor+'/statuses/'+str(statusNumber), | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |         'type': 'Follow', | 
					
						
							|  |  |  |         'actor': followActor, | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |         'object': followedId | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |     wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                                 fromDomain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: announce webfinger failed for '+handle) | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     postToBox='outbox' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get the actor inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix,fromNickname, \ | 
					
						
							|  |  |  |                      fromDomain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |                       | 
					
						
							|  |  |  |     if not inboxUrl: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No '+postToBox+' was found for '+handle) | 
					
						
							|  |  |  |         return 3 | 
					
						
							|  |  |  |     if not fromPersonId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No actor was found for '+handle) | 
					
						
							|  |  |  |         return 4 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     authHeader=createBasicAuthHeader(fromNickname,password) | 
					
						
							|  |  |  |       | 
					
						
							|  |  |  |     headers = {'host': fromDomain, \ | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |                'Content-type': 'application/json', \ | 
					
						
							| 
									
										
										
										
											2019-07-16 21:38:06 +00:00
										 |  |  |                'Authorization': authHeader} | 
					
						
							|  |  |  |     postResult = \ | 
					
						
							|  |  |  |         postJson(session,newFollowJson,[],inboxUrl,headers,"inbox:write") | 
					
						
							|  |  |  |     #if not postResult: | 
					
						
							|  |  |  |     #    if debug: | 
					
						
							|  |  |  |     #        print('DEBUG: POST announce failed for c2s to '+inboxUrl) | 
					
						
							|  |  |  |     #    return 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s POST follow success') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return newFollowJson | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def sendUnfollowRequestViaServer(baseDir: str,session, \ | 
					
						
							|  |  |  |                                  fromNickname: str,password: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |                                  fromDomain: str,fromPort: int, \ | 
					
						
							|  |  |  |                                  followNickname: str,followDomain: str,followPort: int, \ | 
					
						
							|  |  |  |                                  httpPrefix: str, \ | 
					
						
							|  |  |  |                                  cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                                  debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     """Creates a unfollow request via c2s
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendUnfollowRequestViaServer') | 
					
						
							|  |  |  |         return 6 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fromDomainFull=fromDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if fromPort: | 
					
						
							|  |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							|  |  |  |             if ':' not in fromDomain: | 
					
						
							|  |  |  |                 fromDomainFull=fromDomain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     followDomainFull=followDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if followPort: | 
					
						
							|  |  |  |         if followPort!=80 and followPort!=443: | 
					
						
							|  |  |  |             if ':' not in followDomain: | 
					
						
							|  |  |  |                 followDomainFull=followDomain+':'+str(followPort) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname     | 
					
						
							|  |  |  |     followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     unfollowJson = { | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |         '@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         'id': followActor+'/statuses/'+str(statusNumber)+'/undo', | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |         'type': 'Undo', | 
					
						
							|  |  |  |         'actor': followActor, | 
					
						
							|  |  |  |         'object': { | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |             'id': followActor+'/statuses/'+str(statusNumber), | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |             'type': 'Follow', | 
					
						
							|  |  |  |             'actor': followActor, | 
					
						
							| 
									
										
										
										
											2019-08-16 21:52:11 +00:00
										 |  |  |             'object': followedId | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |     wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                                 fromDomain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: announce webfinger failed for '+handle) | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     postToBox='outbox' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get the actor inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix,fromNickname, \ | 
					
						
							|  |  |  |                      fromDomain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |                       | 
					
						
							|  |  |  |     if not inboxUrl: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No '+postToBox+' was found for '+handle) | 
					
						
							|  |  |  |         return 3 | 
					
						
							|  |  |  |     if not fromPersonId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No actor was found for '+handle) | 
					
						
							|  |  |  |         return 4 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     authHeader=createBasicAuthHeader(fromNickname,password) | 
					
						
							|  |  |  |       | 
					
						
							|  |  |  |     headers = {'host': fromDomain, \ | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |                'Content-type': 'application/json', \ | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |                'Authorization': authHeader} | 
					
						
							|  |  |  |     postResult = \ | 
					
						
							|  |  |  |         postJson(session,unfollowJson,[],inboxUrl,headers,"inbox:write") | 
					
						
							|  |  |  |     #if not postResult: | 
					
						
							|  |  |  |     #    if debug: | 
					
						
							|  |  |  |     #        print('DEBUG: POST announce failed for c2s to '+inboxUrl) | 
					
						
							|  |  |  |     #    return 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s POST unfollow success') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return unfollowJson | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  | def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     """In a shared inbox if we receive a post we know who it's from
 | 
					
						
							| 
									
										
										
										
											2019-07-08 17:15:55 +00:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: getting followers of '+actor) | 
					
						
							|  |  |  |     recipientsDict={} | 
					
						
							| 
									
										
										
										
											2019-07-08 17:15:55 +00:00
										 |  |  |     if ':' not in actor: | 
					
						
							| 
									
										
										
										
											2019-07-08 22:12:24 +00:00
										 |  |  |         return recipientsDict | 
					
						
							| 
									
										
										
										
											2019-07-08 17:15:55 +00:00
										 |  |  |     httpPrefix=actor.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     nickname=getNicknameFromActor(actor) | 
					
						
							|  |  |  |     if not nickname: | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: no nickname found in '+actor) | 
					
						
							| 
									
										
										
										
											2019-07-08 22:12:24 +00:00
										 |  |  |         return recipientsDict | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     domain,port=getDomainFromActor(actor) | 
					
						
							|  |  |  |     if not domain: | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: no domain found in '+actor) | 
					
						
							| 
									
										
										
										
											2019-07-08 22:12:24 +00:00
										 |  |  |         return recipientsDict | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     actorHandle=nickname+'@'+domain | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: searching for handle '+actorHandle) | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |     # 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' | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('DEBUG: examining follows of '+account) | 
					
						
							|  |  |  |                     print(followingFilename) | 
					
						
							| 
									
										
										
										
											2019-07-08 16:49:12 +00:00
										 |  |  |                 if os.path.isfile(followingFilename): | 
					
						
							|  |  |  |                     # does this account follow the given actor? | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                     if debug: | 
					
						
							|  |  |  |                         print('DEBUG: checking if '+actorHandle+' in '+followingFilename) | 
					
						
							| 
									
										
										
										
											2019-07-08 17:15:55 +00:00
										 |  |  |                     if actorHandle in open(followingFilename).read(): | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                         if debug: | 
					
						
							|  |  |  |                             print('DEBUG: '+account+' follows '+actorHandle) | 
					
						
							| 
									
										
										
										
											2019-07-08 22:12:24 +00:00
										 |  |  |                         ocapFilename=baseDir+'/accounts/'+account+'/ocap/accept/'+httpPrefix+':##'+domain+':'+str(port)+'#users#'+nickname+'.json' | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                         if debug: | 
					
						
							|  |  |  |                             print('DEBUG: checking capabilities of'+account) | 
					
						
							| 
									
										
										
										
											2019-09-30 22:39:02 +00:00
										 |  |  |                         if os.path.isfile(ocapFilename): | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |                             ocapJson=loadJson(ocapFilename) | 
					
						
							| 
									
										
										
										
											2019-09-30 22:39:02 +00:00
										 |  |  |                             if ocapJson: | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                                 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 | 
					
						
							| 
									
										
										
										
											2019-07-08 17:15:55 +00:00
										 |  |  |                         else: | 
					
						
							| 
									
										
										
										
											2019-07-11 12:29:31 +00:00
										 |  |  |                             if debug: | 
					
						
							|  |  |  |                                 print('DEBUG: No capabilities file found for '+account+' granted by '+actorHandle) | 
					
						
							|  |  |  |                                 print(ocapFilename) | 
					
						
							| 
									
										
										
										
											2019-07-08 22:12:24 +00:00
										 |  |  |                             recipientsDict[account]=None | 
					
						
							|  |  |  |     return recipientsDict | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def outboxUndoFollow(baseDir: str,messageJson: {},debug: bool) -> None: | 
					
						
							|  |  |  |     """When an unfollow request is received by the outbox from c2s
 | 
					
						
							|  |  |  |     This removes the followed handle from the following.txt file | 
					
						
							|  |  |  |     of the relevant account | 
					
						
							| 
									
										
										
										
											2019-07-29 20:36:26 +00:00
										 |  |  |     TODO the unfollow should also be sent to the previously followed account | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     if not messageJson.get('type'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['type']=='Undo': | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson.get('object'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not isinstance(messageJson['object'], dict): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['object'].get('type'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['object']['type']=='Follow': | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['object'].get('object'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not messageJson['object'].get('actor'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if not isinstance(messageJson['object']['object'], str): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: undo follow arrived in outbox') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nicknameFollower=getNicknameFromActor(messageJson['object']['actor']) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |     if not nicknameFollower: | 
					
						
							|  |  |  |         print('WARN: unable to find nickname in '+messageJson['object']['actor']) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     domainFollower,portFollower=getDomainFromActor(messageJson['object']['actor']) | 
					
						
							|  |  |  |     domainFollowerFull=domainFollower | 
					
						
							|  |  |  |     if portFollower: | 
					
						
							|  |  |  |         if portFollower!=80 and portFollower!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if ':' not in domainFollower: | 
					
						
							|  |  |  |                 domainFollowerFull=domainFollower+':'+str(portFollower) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     nicknameFollowing=getNicknameFromActor(messageJson['object']['object']) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |     if not nicknameFollowing: | 
					
						
							|  |  |  |         print('WARN: unable to find nickname in '+messageJson['object']['object']) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  |     domainFollowing,portFollowing=getDomainFromActor(messageJson['object']['object']) | 
					
						
							|  |  |  |     domainFollowingFull=domainFollowing | 
					
						
							|  |  |  |     if portFollowing: | 
					
						
							|  |  |  |         if portFollowing!=80 and portFollowing!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if ':' not in domainFollowing: | 
					
						
							|  |  |  |                 domainFollowingFull=domainFollowing+':'+str(portFollowing) | 
					
						
							| 
									
										
										
										
											2019-07-17 10:34:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if unfollowPerson(baseDir,nicknameFollower,domainFollowerFull, \ | 
					
						
							|  |  |  |                       nicknameFollowing,domainFollowingFull): | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: '+nicknameFollower+' unfollowed '+nicknameFollowing+'@'+domainFollowingFull) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('WARN: '+nicknameFollower+' could not unfollow '+nicknameFollowing+'@'+domainFollowingFull) |