Modern ActivityPub compliant server, designed for simplicity and accessibility. Includes calendar and sharing economy features to empower your federated community. https://code.freedombone.net/bashrc/epicyon Docs: https://epicyon.net/#install
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

delete.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. __filename__ = "delete.py"
  2. __author__ = "Bob Mottram"
  3. __license__ = "AGPL3+"
  4. __version__ = "1.2.0"
  5. __maintainer__ = "Bob Mottram"
  6. __email__ = "bob@freedombone.net"
  7. __status__ = "Production"
  8. import os
  9. from datetime import datetime
  10. from utils import hasUsersPath
  11. from utils import getFullDomain
  12. from utils import removeIdEnding
  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 session import postJson
  19. from webfinger import webfingerHandle
  20. from auth import createBasicAuthHeader
  21. from posts import getPersonBox
  22. def sendDeleteViaServer(baseDir: str, session,
  23. fromNickname: str, password: str,
  24. fromDomain: str, fromPort: int,
  25. httpPrefix: str, deleteObjectUrl: str,
  26. cachedWebfingers: {}, personCache: {},
  27. debug: bool, projectVersion: str) -> {}:
  28. """Creates a delete request message via c2s
  29. """
  30. if not session:
  31. print('WARN: No session for sendDeleteViaServer')
  32. return 6
  33. fromDomainFull = getFullDomain(fromDomain, fromPort)
  34. actor = httpPrefix + '://' + fromDomainFull + \
  35. '/users/' + fromNickname
  36. toUrl = 'https://www.w3.org/ns/activitystreams#Public'
  37. ccUrl = actor + '/followers'
  38. newDeleteJson = {
  39. "@context": "https://www.w3.org/ns/activitystreams",
  40. 'actor': actor,
  41. 'cc': [ccUrl],
  42. 'object': deleteObjectUrl,
  43. 'to': [toUrl],
  44. 'type': 'Delete'
  45. }
  46. handle = httpPrefix + '://' + fromDomainFull + '/@' + fromNickname
  47. # lookup the inbox for the To handle
  48. wfRequest = \
  49. webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
  50. fromDomain, projectVersion, debug)
  51. if not wfRequest:
  52. if debug:
  53. print('DEBUG: delete webfinger failed for ' + handle)
  54. return 1
  55. if not isinstance(wfRequest, dict):
  56. print('WARN: delete webfinger for ' + handle +
  57. ' did not return a dict. ' + str(wfRequest))
  58. return 1
  59. postToBox = 'outbox'
  60. # get the actor inbox for the To handle
  61. (inboxUrl, pubKeyId, pubKey,
  62. fromPersonId, sharedInbox, avatarUrl,
  63. displayName) = getPersonBox(baseDir, session, wfRequest, personCache,
  64. projectVersion, httpPrefix, fromNickname,
  65. fromDomain, postToBox, 53036)
  66. if not inboxUrl:
  67. if debug:
  68. print('DEBUG: delete no ' + postToBox +
  69. ' was found for ' + handle)
  70. return 3
  71. if not fromPersonId:
  72. if debug:
  73. print('DEBUG: delete no actor was found for ' + handle)
  74. return 4
  75. authHeader = createBasicAuthHeader(fromNickname, password)
  76. headers = {
  77. 'host': fromDomain,
  78. 'Content-type': 'application/json',
  79. 'Authorization': authHeader
  80. }
  81. postResult = \
  82. postJson(session, newDeleteJson, [], inboxUrl, headers, 3, True)
  83. if not postResult:
  84. if debug:
  85. print('DEBUG: POST delete failed for c2s to ' + inboxUrl)
  86. return 5
  87. if debug:
  88. print('DEBUG: c2s POST delete request success')
  89. return newDeleteJson
  90. def outboxDelete(baseDir: str, httpPrefix: str,
  91. nickname: str, domain: str,
  92. messageJson: {}, debug: bool,
  93. allowDeletion: bool,
  94. recentPostsCache: {}) -> None:
  95. """ When a delete request is received by the outbox from c2s
  96. """
  97. if not messageJson.get('type'):
  98. if debug:
  99. print('DEBUG: delete - no type')
  100. return
  101. if not messageJson['type'] == 'Delete':
  102. if debug:
  103. print('DEBUG: not a delete')
  104. return
  105. if not messageJson.get('object'):
  106. if debug:
  107. print('DEBUG: no object in delete')
  108. return
  109. if not isinstance(messageJson['object'], str):
  110. if debug:
  111. print('DEBUG: delete object is not string')
  112. return
  113. if debug:
  114. print('DEBUG: c2s delete request arrived in outbox')
  115. deletePrefix = httpPrefix + '://' + domain
  116. if (not allowDeletion and
  117. (not messageJson['object'].startswith(deletePrefix) or
  118. not messageJson['actor'].startswith(deletePrefix))):
  119. if debug:
  120. print('DEBUG: delete not permitted from other instances')
  121. return
  122. messageId = removeIdEnding(messageJson['object'])
  123. if '/statuses/' not in messageId:
  124. if debug:
  125. print('DEBUG: c2s delete object is not a status')
  126. return
  127. if not hasUsersPath(messageId):
  128. if debug:
  129. print('DEBUG: c2s delete object has no nickname')
  130. return
  131. deleteNickname = getNicknameFromActor(messageId)
  132. if deleteNickname != nickname:
  133. if debug:
  134. print("DEBUG: you can't delete a post which " +
  135. "wasn't created by you (nickname does not match)")
  136. return
  137. deleteDomain, deletePort = getDomainFromActor(messageId)
  138. if ':' in domain:
  139. domain = domain.split(':')[0]
  140. if deleteDomain != domain:
  141. if debug:
  142. print("DEBUG: you can't delete a post which " +
  143. "wasn't created by you (domain does not match)")
  144. return
  145. removeModerationPostFromIndex(baseDir, messageId, debug)
  146. postFilename = locatePost(baseDir, deleteNickname, deleteDomain,
  147. messageId)
  148. if not postFilename:
  149. if debug:
  150. print('DEBUG: c2s delete post not found in inbox or outbox')
  151. print(messageId)
  152. return True
  153. deletePost(baseDir, httpPrefix, deleteNickname, deleteDomain,
  154. postFilename, debug, recentPostsCache)
  155. if debug:
  156. print('DEBUG: post deleted via c2s - ' + postFilename)
  157. def removeOldHashtags(baseDir: str, maxMonths: int) -> str:
  158. """Remove old hashtags
  159. """
  160. if maxMonths > 11:
  161. maxMonths = 11
  162. maxDaysSinceEpoch = \
  163. (datetime.utcnow() - datetime(1970, 1 + maxMonths, 1)).days
  164. removeHashtags = []
  165. for subdir, dirs, files in os.walk(baseDir + '/tags'):
  166. for f in files:
  167. tagsFilename = os.path.join(baseDir + '/tags', f)
  168. if not os.path.isfile(tagsFilename):
  169. continue
  170. # get last modified datetime
  171. modTimesinceEpoc = os.path.getmtime(tagsFilename)
  172. lastModifiedDate = datetime.fromtimestamp(modTimesinceEpoc)
  173. fileDaysSinceEpoch = (lastModifiedDate - datetime(1970, 1, 1)).days
  174. # check of the file is too old
  175. if fileDaysSinceEpoch < maxDaysSinceEpoch:
  176. removeHashtags.append(tagsFilename)
  177. break
  178. for removeFilename in removeHashtags:
  179. try:
  180. os.remove(removeFilename)
  181. except BaseException:
  182. pass