delete.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. __filename__ = "delete.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. import json
  10. from utils import getStatusNumber
  11. from utils import createOutboxDir
  12. from utils import urlPermitted
  13. from utils import getNicknameFromActor
  14. from utils import getDomainFromActor
  15. from utils import locatePost
  16. from utils import deletePost
  17. from utils import removeModerationPostFromIndex
  18. from posts import sendSignedJson
  19. from session import postJson
  20. from webfinger import webfingerHandle
  21. from auth import createBasicAuthHeader
  22. from posts import getPersonBox
  23. def createDelete(session,baseDir: str,federationList: [], \
  24. nickname: str, domain: str, port: int, \
  25. toUrl: str, ccUrl: str, httpPrefix: str, \
  26. objectUrl: str,clientToServer: bool, \
  27. sendThreads: [],postLog: [], \
  28. personCache: {},cachedWebfingers: {}, \
  29. debug: bool) -> {}:
  30. """Creates a delete message
  31. Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
  32. and ccUrl might be a specific person whose post is to be deleted
  33. objectUrl is typically the url of the message, corresponding to url
  34. or atomUri in createPostBase
  35. """
  36. if not urlPermitted(objectUrl,federationList,"inbox:write"):
  37. return None
  38. if ':' in domain:
  39. domain=domain.split(':')[0]
  40. fullDomain=domain
  41. if port:
  42. if port!=80 and port!=443:
  43. if ':' not in domain:
  44. fullDomain=domain+':'+str(port)
  45. statusNumber,published = getStatusNumber()
  46. newDeleteId= \
  47. httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber
  48. newDelete = {
  49. "@context": "https://www.w3.org/ns/activitystreams",
  50. 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
  51. 'atomUri': httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber,
  52. 'cc': [],
  53. 'id': newDeleteId+'/activity',
  54. 'object': objectUrl,
  55. 'published': published,
  56. 'to': [toUrl],
  57. 'type': 'Delete'
  58. }
  59. if ccUrl:
  60. if len(ccUrl)>0:
  61. newDelete['cc']=[ccUrl]
  62. deleteNickname=None
  63. deleteDomain=None
  64. deletePort=None
  65. if '/users/' in objectUrl or \
  66. '/channel/' in objectUrl or \
  67. '/profile/' in objectUrl:
  68. deleteNickname=getNicknameFromActor(objectUrl)
  69. deleteDomain,deletePort=getDomainFromActor(objectUrl)
  70. if deleteNickname and deleteDomain:
  71. sendSignedJson(newDelete,session,baseDir, \
  72. nickname,domain,port, \
  73. deleteNickname,deleteDomain,deletePort, \
  74. 'https://www.w3.org/ns/activitystreams#Public', \
  75. httpPrefix,True,clientToServer,federationList, \
  76. sendThreads,postLog,cachedWebfingers,personCache,debug)
  77. return newDelete
  78. def sendDeleteViaServer(baseDir: str,session, \
  79. fromNickname: str,password: str, \
  80. fromDomain: str,fromPort: int, \
  81. httpPrefix: str,deleteObjectUrl: str, \
  82. cachedWebfingers: {},personCache: {}, \
  83. debug: bool,projectVersion: str) -> {}:
  84. """Creates a delete request message via c2s
  85. """
  86. if not session:
  87. print('WARN: No session for sendDeleteViaServer')
  88. return 6
  89. fromDomainFull=fromDomain
  90. if fromPort:
  91. if fromPort!=80 and fromPort!=443:
  92. if ':' not in fromDomain:
  93. fromDomainFull=fromDomain+':'+str(fromPort)
  94. toUrl = 'https://www.w3.org/ns/activitystreams#Public'
  95. ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers'
  96. newDeleteJson = {
  97. "@context": "https://www.w3.org/ns/activitystreams",
  98. 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
  99. 'cc': [ccUrl],
  100. 'object': deleteObjectUrl,
  101. 'to': [toUrl],
  102. 'type': 'Delete'
  103. }
  104. handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname
  105. # lookup the inbox for the To handle
  106. wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \
  107. fromDomain,projectVersion)
  108. if not wfRequest:
  109. if debug:
  110. print('DEBUG: announce webfinger failed for '+handle)
  111. return 1
  112. postToBox='outbox'
  113. # get the actor inbox for the To handle
  114. inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \
  115. getPersonBox(baseDir,session,wfRequest,personCache, \
  116. projectVersion,httpPrefix,fromNickname, \
  117. fromDomain,postToBox)
  118. if not inboxUrl:
  119. if debug:
  120. print('DEBUG: No '+postToBox+' was found for '+handle)
  121. return 3
  122. if not fromPersonId:
  123. if debug:
  124. print('DEBUG: No actor was found for '+handle)
  125. return 4
  126. authHeader=createBasicAuthHeader(fromNickname,password)
  127. headers = {'host': fromDomain, \
  128. 'Content-type': 'application/json', \
  129. 'Authorization': authHeader}
  130. postResult = \
  131. postJson(session,newDeleteJson,[],inboxUrl,headers,"inbox:write")
  132. #if not postResult:
  133. # if debug:
  134. # print('DEBUG: POST announce failed for c2s to '+inboxUrl)
  135. # return 5
  136. if debug:
  137. print('DEBUG: c2s POST delete request success')
  138. return newDeleteJson
  139. def deletePublic(session,baseDir: str,federationList: [], \
  140. nickname: str, domain: str, port: int, httpPrefix: str, \
  141. objectUrl: str,clientToServer: bool, \
  142. sendThreads: [],postLog: [], \
  143. personCache: {},cachedWebfingers: {}, \
  144. debug: bool) -> {}:
  145. """Makes a public delete activity
  146. """
  147. fromDomain=domain
  148. if port:
  149. if port!=80 and port!=443:
  150. if ':' not in domain:
  151. fromDomain=domain+':'+str(port)
  152. toUrl = 'https://www.w3.org/ns/activitystreams#Public'
  153. ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers'
  154. return createDelete(session,baseDir,federationList, \
  155. nickname,domain,port, \
  156. toUrl,ccUrl,httpPrefix, \
  157. objectUrl,clientToServer, \
  158. sendThreads,postLog, \
  159. personCache,cachedWebfingers, \
  160. debug)
  161. def deletePostPub(session,baseDir: str,federationList: [], \
  162. nickname: str, domain: str, port: int, httpPrefix: str, \
  163. deleteNickname: str, deleteDomain: str, \
  164. deletePort: int, deleteHttpsPrefix: str, \
  165. deleteStatusNumber: int,clientToServer: bool, \
  166. sendThreads: [],postLog: [], \
  167. personCache: {},cachedWebfingers: {}, \
  168. debug: bool) -> {}:
  169. """Deletes a given status post
  170. """
  171. deletedDomain=deleteDomain
  172. if deletePort:
  173. if deletePort!=80 and deletePort!=443:
  174. if ':' not in deletedDomain:
  175. deletedDomain=deletedDomain+':'+str(deletePort)
  176. objectUrl = deleteHttpsPrefix + '://'+deletedDomain+'/users/'+ \
  177. deleteNickname+'/statuses/'+str(deleteStatusNumber)
  178. return deletePublic(session,baseDir,federationList, \
  179. nickname,domain,port,httpPrefix, \
  180. objectUrl,clientToServer, \
  181. sendThreads,postLog, \
  182. personCache,cachedWebfingers, \
  183. debug)
  184. def outboxDelete(baseDir: str,httpPrefix: str, \
  185. nickname: str,domain: str, \
  186. messageJson: {},debug: bool,
  187. allowDeletion: bool) -> None:
  188. """ When a delete request is received by the outbox from c2s
  189. """
  190. if not messageJson.get('type'):
  191. if debug:
  192. print('DEBUG: delete - no type')
  193. return
  194. if not messageJson['type']=='Delete':
  195. if debug:
  196. print('DEBUG: not a delete')
  197. return
  198. if not messageJson.get('object'):
  199. if debug:
  200. print('DEBUG: no object in delete')
  201. return
  202. if not isinstance(messageJson['object'], str):
  203. if debug:
  204. print('DEBUG: delete object is not string')
  205. return
  206. if debug:
  207. print('DEBUG: c2s delete request arrived in outbox')
  208. deletePrefix=httpPrefix+'://'+domain
  209. if not allowDeletion and \
  210. (not messageJson['object'].startswith(deletePrefix) or \
  211. not messageJson['actor'].startswith(deletePrefix)):
  212. if debug:
  213. print('DEBUG: delete not permitted from other instances')
  214. return
  215. messageId=messageJson['object'].replace('/activity','')
  216. if '/statuses/' not in messageId:
  217. if debug:
  218. print('DEBUG: c2s delete object is not a status')
  219. return
  220. if '/users/' not in messageId and \
  221. '/channel/' not in messageId and \
  222. '/profile/' not in messageId:
  223. if debug:
  224. print('DEBUG: c2s delete object has no nickname')
  225. return
  226. deleteNickname=getNicknameFromActor(messageId)
  227. if deleteNickname!=nickname:
  228. if debug:
  229. print("DEBUG: you can't delete a post which wasn't created by you (nickname does not match)")
  230. return
  231. deleteDomain,deletePort=getDomainFromActor(messageId)
  232. if ':' in domain:
  233. domain=domain.split(':')[0]
  234. if deleteDomain!=domain:
  235. if debug:
  236. print("DEBUG: you can't delete a post which wasn't created by you (domain does not match)")
  237. return
  238. removeModerationPostFromIndex(baseDir,messageId,debug)
  239. postFilename=locatePost(baseDir,deleteNickname,deleteDomain,messageId)
  240. if not postFilename:
  241. if debug:
  242. print('DEBUG: c2s delete post not found in inbox or outbox')
  243. print(messageId)
  244. return True
  245. deletePost(baseDir,httpPrefix,deleteNickname,deleteDomain,postFilename,debug)
  246. if debug:
  247. print('DEBUG: post deleted via c2s - '+postFilename)