epicyon/delete.py

210 lines
7.0 KiB
Python

__filename__ = "delete.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "ActivityPub"
import os
from datetime import datetime
from utils import removeDomainPort
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import locatePost
from utils import deletePost
from utils import removeModerationPostFromIndex
from utils import localActorUrl
from session import postJson
from webfinger import webfingerHandle
from auth import createBasicAuthHeader
from posts import getPersonBox
def sendDeleteViaServer(baseDir: str, session,
fromNickname: str, password: str,
fromDomain: str, fromPort: int,
httpPrefix: str, deleteObjectUrl: str,
cachedWebfingers: {}, personCache: {},
debug: bool, projectVersion: str,
signingPrivateKeyPem: str) -> {}:
"""Creates a delete request message via c2s
"""
if not session:
print('WARN: No session for sendDeleteViaServer')
return 6
fromDomainFull = getFullDomain(fromDomain, fromPort)
actor = localActorUrl(httpPrefix, fromNickname, fromDomainFull)
toUrl = 'https://www.w3.org/ns/activitystreams#Public'
ccUrl = actor + '/followers'
newDeleteJson = {
"@context": "https://www.w3.org/ns/activitystreams",
'actor': actor,
'cc': [ccUrl],
'object': deleteObjectUrl,
'to': [toUrl],
'type': 'Delete'
}
handle = httpPrefix + '://' + fromDomainFull + '/@' + fromNickname
# lookup the inbox for the To handle
wfRequest = \
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
fromDomain, projectVersion, debug, False,
signingPrivateKeyPem)
if not wfRequest:
if debug:
print('DEBUG: delete webfinger failed for ' + handle)
return 1
if not isinstance(wfRequest, dict):
print('WARN: delete webfinger for ' + handle +
' did not return a dict. ' + str(wfRequest))
return 1
postToBox = 'outbox'
# get the actor inbox for the To handle
(inboxUrl, pubKeyId, pubKey,
fromPersonId, sharedInbox, avatarUrl,
displayName) = getPersonBox(signingPrivateKeyPem,
baseDir, session, wfRequest, personCache,
projectVersion, httpPrefix, fromNickname,
fromDomain, postToBox, 53036)
if not inboxUrl:
if debug:
print('DEBUG: delete no ' + postToBox +
' was found for ' + handle)
return 3
if not fromPersonId:
if debug:
print('DEBUG: delete no actor was found for ' + handle)
return 4
authHeader = createBasicAuthHeader(fromNickname, password)
headers = {
'host': fromDomain,
'Content-type': 'application/json',
'Authorization': authHeader
}
postResult = \
postJson(httpPrefix, fromDomainFull,
session, newDeleteJson, [], inboxUrl, headers, 3, True)
if not postResult:
if debug:
print('DEBUG: POST delete failed for c2s to ' + inboxUrl)
return 5
if debug:
print('DEBUG: c2s POST delete request success')
return newDeleteJson
def outboxDelete(baseDir: str, httpPrefix: str,
nickname: str, domain: str,
messageJson: {}, debug: bool,
allowDeletion: bool,
recentPostsCache: {}) -> None:
""" When a delete request is received by the outbox from c2s
"""
if not messageJson.get('type'):
if debug:
print('DEBUG: delete - no type')
return
if not messageJson['type'] == 'Delete':
if debug:
print('DEBUG: not a delete')
return
if not messageJson.get('object'):
if debug:
print('DEBUG: no object in delete')
return
if not isinstance(messageJson['object'], str):
if debug:
print('DEBUG: delete object is not string')
return
if debug:
print('DEBUG: c2s delete request arrived in outbox')
deletePrefix = httpPrefix + '://' + domain
if (not allowDeletion and
(not messageJson['object'].startswith(deletePrefix) or
not messageJson['actor'].startswith(deletePrefix))):
if debug:
print('DEBUG: delete not permitted from other instances')
return
messageId = removeIdEnding(messageJson['object'])
if '/statuses/' not in messageId:
if debug:
print('DEBUG: c2s delete object is not a status')
return
if not hasUsersPath(messageId):
if debug:
print('DEBUG: c2s delete object has no nickname')
return
deleteNickname = getNicknameFromActor(messageId)
if deleteNickname != nickname:
if debug:
print("DEBUG: you can't delete a post which " +
"wasn't created by you (nickname does not match)")
return
deleteDomain, deletePort = getDomainFromActor(messageId)
domain = removeDomainPort(domain)
if deleteDomain != domain:
if debug:
print("DEBUG: you can't delete a post which " +
"wasn't created by you (domain does not match)")
return
removeModerationPostFromIndex(baseDir, messageId, debug)
postFilename = locatePost(baseDir, deleteNickname, deleteDomain,
messageId)
if not postFilename:
if debug:
print('DEBUG: c2s delete post not found in inbox or outbox')
print(messageId)
return True
deletePost(baseDir, httpPrefix, deleteNickname, deleteDomain,
postFilename, debug, recentPostsCache)
if debug:
print('DEBUG: post deleted via c2s - ' + postFilename)
def removeOldHashtags(baseDir: str, maxMonths: int) -> str:
"""Remove old hashtags
"""
if maxMonths > 11:
maxMonths = 11
maxDaysSinceEpoch = \
(datetime.utcnow() - datetime(1970, 1 + maxMonths, 1)).days
removeHashtags = []
for subdir, dirs, files in os.walk(baseDir + '/tags'):
for f in files:
tagsFilename = os.path.join(baseDir + '/tags', f)
if not os.path.isfile(tagsFilename):
continue
# get last modified datetime
modTimesinceEpoc = os.path.getmtime(tagsFilename)
lastModifiedDate = datetime.fromtimestamp(modTimesinceEpoc)
fileDaysSinceEpoch = (lastModifiedDate - datetime(1970, 1, 1)).days
# check of the file is too old
if fileDaysSinceEpoch < maxDaysSinceEpoch:
removeHashtags.append(tagsFilename)
break
for removeFilename in removeHashtags:
try:
os.remove(removeFilename)
except BaseException:
pass