acceptreject.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. __filename__ = "acceptreject.py"
  2. __author__ = "Bob Mottram"
  3. __license__ = "AGPL3+"
  4. __version__ = "1.1.0"
  5. __maintainer__ = "Bob Mottram"
  6. __email__ = "bob@freedombone.net"
  7. __status__ = "Production"
  8. import os
  9. from capabilities import capabilitiesAccept
  10. from capabilities import capabilitiesGrantedSave
  11. from utils import urlPermitted
  12. from utils import getDomainFromActor
  13. from utils import getNicknameFromActor
  14. from utils import domainPermitted
  15. from utils import followPerson
  16. def createAcceptReject(baseDir: str, federationList: [],
  17. nickname: str, domain: str, port: int,
  18. toUrl: str, ccUrl: str, httpPrefix: str,
  19. objectJson: {}, ocapJson, acceptType: str) -> {}:
  20. """Accepts or rejects something (eg. a follow request or offer)
  21. Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
  22. and ccUrl might be a specific person favorited or repeated and
  23. the followers url objectUrl is typically the url of the message,
  24. corresponding to url or atomUri in createPostBase
  25. """
  26. if not objectJson.get('actor'):
  27. return None
  28. if not urlPermitted(objectJson['actor'], federationList, "inbox:write"):
  29. return None
  30. if port:
  31. if port != 80 and port != 443:
  32. if ':' not in domain:
  33. domain = domain + ':' + str(port)
  34. newAccept = {
  35. "@context": "https://www.w3.org/ns/activitystreams",
  36. 'type': acceptType,
  37. 'actor': httpPrefix+'://'+domain+'/users/'+nickname,
  38. 'to': [toUrl],
  39. 'cc': [],
  40. 'object': objectJson
  41. }
  42. if ccUrl:
  43. if len(ccUrl) > 0:
  44. newAccept['cc'] = [ccUrl]
  45. # attach capabilities for follow accept
  46. if ocapJson:
  47. newAccept['capabilities'] = ocapJson
  48. return newAccept
  49. def createAccept(baseDir: str, federationList: [],
  50. nickname: str, domain: str, port: int,
  51. toUrl: str, ccUrl: str, httpPrefix: str,
  52. objectJson: {},
  53. acceptedCaps=["inbox:write", "objects:read"]) -> {}:
  54. # create capabilities accept
  55. ocapNew = capabilitiesAccept(baseDir, httpPrefix,
  56. nickname, domain, port,
  57. toUrl, True, acceptedCaps)
  58. return createAcceptReject(baseDir, federationList,
  59. nickname, domain, port,
  60. toUrl, ccUrl, httpPrefix,
  61. objectJson, ocapNew, 'Accept')
  62. def createReject(baseDir: str, federationList: [],
  63. nickname: str, domain: str, port: int,
  64. toUrl: str, ccUrl: str, httpPrefix: str,
  65. objectJson: {}) -> {}:
  66. return createAcceptReject(baseDir, federationList,
  67. nickname, domain, port,
  68. toUrl, ccUrl,
  69. httpPrefix, objectJson, None, 'Reject')
  70. def acceptFollow(baseDir: str, domain: str, messageJson: {},
  71. federationList: [], debug: bool) -> None:
  72. """Receiving a follow Accept activity
  73. """
  74. if not messageJson.get('object'):
  75. return
  76. if not messageJson['object'].get('type'):
  77. return
  78. if not messageJson['object']['type'] == 'Follow':
  79. return
  80. if debug:
  81. print('DEBUG: receiving Follow activity')
  82. if not messageJson['object'].get('actor'):
  83. print('DEBUG: no actor in Follow activity')
  84. return
  85. # no, this isn't a mistake
  86. if not messageJson['object'].get('object'):
  87. print('DEBUG: no object within Follow activity')
  88. return
  89. if not messageJson.get('to'):
  90. if debug:
  91. print('DEBUG: No "to" parameter in follow Accept')
  92. return
  93. if debug:
  94. print('DEBUG: follow Accept received')
  95. thisActor = messageJson['object']['actor']
  96. nickname = getNicknameFromActor(thisActor)
  97. if not nickname:
  98. print('WARN: no nickname found in '+thisActor)
  99. return
  100. acceptedDomain, acceptedPort = getDomainFromActor(thisActor)
  101. if not acceptedDomain:
  102. if debug:
  103. print('DEBUG: domain not found in '+thisActor)
  104. return
  105. if not nickname:
  106. if debug:
  107. print('DEBUG: nickname not found in '+thisActor)
  108. return
  109. if acceptedPort:
  110. if '/' + acceptedDomain + ':' + str(acceptedPort) + \
  111. '/users/' + nickname not in thisActor:
  112. if debug:
  113. print('Port: '+str(acceptedPort))
  114. print('Expected: /' + acceptedDomain + ':' +
  115. str(acceptedPort) + '/users/'+nickname)
  116. print('Actual: '+thisActor)
  117. print('DEBUG: unrecognized actor '+thisActor)
  118. return
  119. else:
  120. if not '/' + acceptedDomain+'/users/' + nickname in thisActor:
  121. if debug:
  122. print('Expected: /'+acceptedDomain+'/users/'+nickname)
  123. print('Actual: '+thisActor)
  124. print('DEBUG: unrecognized actor '+thisActor)
  125. return
  126. followedActor = messageJson['object']['object']
  127. followedDomain, port = getDomainFromActor(followedActor)
  128. if not followedDomain:
  129. print('DEBUG: no domain found within Follow activity object ' +
  130. followedActor)
  131. return
  132. followedDomainFull = followedDomain
  133. if port:
  134. followedDomainFull = followedDomain+':' + str(port)
  135. followedNickname = getNicknameFromActor(followedActor)
  136. if not followedNickname:
  137. print('DEBUG: no nickname found within Follow activity object ' +
  138. followedActor)
  139. return
  140. acceptedDomainFull = acceptedDomain
  141. if acceptedPort:
  142. acceptedDomainFull = acceptedDomain + ':' + str(acceptedPort)
  143. # are capabilities attached? If so then store them
  144. if messageJson.get('capabilities'):
  145. if isinstance(messageJson['capabilities'], dict):
  146. capabilitiesGrantedSave(baseDir,
  147. nickname, acceptedDomainFull,
  148. messageJson['capabilities'])
  149. # has this person already been unfollowed?
  150. unfollowedFilename = baseDir + '/accounts/' + \
  151. nickname + '@' + acceptedDomainFull + '/unfollowed.txt'
  152. if os.path.isfile(unfollowedFilename):
  153. if followedNickname + '@' + followedDomainFull in \
  154. open(unfollowedFilename).read():
  155. if debug:
  156. print('DEBUG: follow accept arrived for ' +
  157. nickname + '@' + acceptedDomainFull +
  158. ' from ' + followedNickname + '@' + followedDomainFull +
  159. ' but they have been unfollowed')
  160. return
  161. if followPerson(baseDir,
  162. nickname, acceptedDomainFull,
  163. followedNickname, followedDomainFull,
  164. federationList, debug):
  165. if debug:
  166. print('DEBUG: ' + nickname + '@' + acceptedDomainFull +
  167. ' followed ' + followedNickname + '@' + followedDomainFull)
  168. else:
  169. if debug:
  170. print('DEBUG: Unable to create follow - ' +
  171. nickname + '@' + acceptedDomain+' -> ' +
  172. followedNickname + '@' + followedDomain)
  173. def receiveAcceptReject(session, baseDir: str,
  174. httpPrefix: str, domain: str, port: int,
  175. sendThreads: [], postLog: [], cachedWebfingers: {},
  176. personCache: {}, messageJson: {}, federationList: [],
  177. debug: bool) -> bool:
  178. """Receives an Accept or Reject within the POST section of HTTPServer
  179. """
  180. if messageJson['type'] != 'Accept' and messageJson['type'] != 'Reject':
  181. return False
  182. if not messageJson.get('actor'):
  183. if debug:
  184. print('DEBUG: ' + messageJson['type'] + ' has no actor')
  185. return False
  186. if '/users/' not in messageJson['actor'] and \
  187. '/channel/' not in messageJson['actor'] and \
  188. '/profile/' not in messageJson['actor']:
  189. if debug:
  190. print('DEBUG: "users" or "profile" missing from actor in ' +
  191. messageJson['type'] + '. Assuming single user instance.')
  192. domain, tempPort = getDomainFromActor(messageJson['actor'])
  193. if not domainPermitted(domain, federationList):
  194. if debug:
  195. print('DEBUG: ' + messageJson['type'] +
  196. ' from domain not permitted - ' + domain)
  197. return False
  198. nickname = getNicknameFromActor(messageJson['actor'])
  199. if not nickname:
  200. # single user instance
  201. nickname = 'dev'
  202. if debug:
  203. print('DEBUG: ' + messageJson['type'] +
  204. ' does not contain a nickname. ' +
  205. 'Assuming single user instance.')
  206. # receive follow accept
  207. acceptFollow(baseDir, domain, messageJson, federationList, debug)
  208. if debug:
  209. print('DEBUG: Uh, ' + messageJson['type'] + ', I guess')
  210. return True