epicyon/cache.py

189 lines
6.5 KiB
Python
Raw Normal View History

2020-04-02 09:02:33 +00:00
__filename__ = "cache.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-04-02 09:02:33 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-04-02 09:02:33 +00:00
__status__ = "Production"
2021-06-26 11:16:41 +00:00
__module_group__ = "Core"
2019-06-30 15:03:26 +00:00
2019-08-20 09:16:03 +00:00
import os
2019-06-30 15:18:40 +00:00
import datetime
from session import urlExists
2021-07-31 11:56:28 +00:00
from session import getJson
2019-10-22 11:55:06 +00:00
from utils import loadJson
from utils import saveJson
from utils import getFileCaseInsensitive
2021-07-31 11:56:28 +00:00
from utils import getUserPaths
2020-04-02 09:02:33 +00:00
2020-05-04 19:16:11 +00:00
def _removePersonFromCache(baseDir: str, personUrl: str,
personCache: {}) -> bool:
"""Removes an actor from the cache
"""
cacheFilename = baseDir + '/cache/actors/' + \
2021-06-22 12:42:52 +00:00
personUrl.replace('/', '#') + '.json'
if os.path.isfile(cacheFilename):
try:
os.remove(cacheFilename)
except BaseException:
2021-10-29 16:31:20 +00:00
print('EX: unable to delete cached actor ' + str(cacheFilename))
pass
if personCache.get(personUrl):
del personCache[personUrl]
def checkForChangedActor(session, baseDir: str,
httpPrefix: str, domainFull: str,
personUrl: str, avatarUrl: str, personCache: {},
timeoutSec: int):
"""Checks if the avatar url exists and if not then
the actor has probably changed without receiving an actor/Person Update.
So clear the actor from the cache and it will be refreshed when the next
post from them is sent
"""
if not session or not avatarUrl:
return
if domainFull in avatarUrl:
return
if urlExists(session, avatarUrl, timeoutSec, httpPrefix, domainFull):
return
_removePersonFromCache(baseDir, personUrl, personCache)
2020-04-02 09:02:33 +00:00
def storePersonInCache(baseDir: str, personUrl: str,
personJson: {}, personCache: {},
allowWriteToFile: bool) -> None:
2019-06-30 15:03:26 +00:00
"""Store an actor in the cache
"""
if 'statuses' in personUrl or personUrl.endswith('/actor'):
# This is not an actor or person account
return
2020-04-02 09:02:33 +00:00
currTime = datetime.datetime.utcnow()
personCache[personUrl] = {
2019-07-06 17:00:22 +00:00
"actor": personJson,
"timestamp": currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
}
2019-08-20 09:16:03 +00:00
if not baseDir:
return
# store to file
2021-06-22 11:25:28 +00:00
if not allowWriteToFile:
return
2021-06-22 12:29:17 +00:00
if os.path.isdir(baseDir + '/cache/actors'):
2021-06-22 11:25:28 +00:00
cacheFilename = baseDir + '/cache/actors/' + \
2021-06-22 12:42:52 +00:00
personUrl.replace('/', '#') + '.json'
2021-06-22 11:25:28 +00:00
if not os.path.isfile(cacheFilename):
saveJson(personJson, cacheFilename)
2020-04-02 09:02:33 +00:00
2019-06-30 15:03:26 +00:00
def getPersonFromCache(baseDir: str, personUrl: str, personCache: {},
allowWriteToFile: bool) -> {}:
2019-06-30 15:03:26 +00:00
"""Get an actor from the cache
"""
2019-08-20 09:37:09 +00:00
# if the actor is not in memory then try to load it from file
2020-04-02 09:02:33 +00:00
loadedFromFile = False
2019-08-20 09:37:09 +00:00
if not personCache.get(personUrl):
# does the person exist as a cached file?
2020-04-02 09:02:33 +00:00
cacheFilename = baseDir + '/cache/actors/' + \
2021-06-22 12:42:52 +00:00
personUrl.replace('/', '#') + '.json'
2020-08-29 19:54:30 +00:00
actorFilename = getFileCaseInsensitive(cacheFilename)
if actorFilename:
personJson = loadJson(actorFilename)
if personJson:
storePersonInCache(baseDir, personUrl, personJson,
personCache, False)
2020-04-02 09:02:33 +00:00
loadedFromFile = True
2020-03-22 21:16:02 +00:00
2019-06-30 15:03:26 +00:00
if personCache.get(personUrl):
2019-08-20 09:50:27 +00:00
if not loadedFromFile:
# update the timestamp for the last time the actor was retrieved
2020-04-02 09:02:33 +00:00
currTime = datetime.datetime.utcnow()
currTimeStr = currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
personCache[personUrl]['timestamp'] = currTimeStr
2019-08-20 09:37:09 +00:00
return personCache[personUrl]['actor']
return None
2020-04-02 09:02:33 +00:00
2019-08-20 09:37:09 +00:00
def expirePersonCache(personCache: {}):
"""Expires old entries from the cache in memory
"""
2020-04-02 09:02:33 +00:00
currTime = datetime.datetime.utcnow()
removals = []
for personUrl, cacheJson in personCache.items():
cacheTime = datetime.datetime.strptime(cacheJson['timestamp'],
"%Y-%m-%dT%H:%M:%SZ")
daysSinceCached = (currTime - cacheTime).days
2019-08-20 09:37:09 +00:00
if daysSinceCached > 2:
removals.append(personUrl)
2020-04-02 09:02:33 +00:00
if len(removals) > 0:
2019-08-20 09:37:09 +00:00
for personUrl in removals:
del personCache[personUrl]
2020-04-02 09:02:33 +00:00
print(str(len(removals)) + ' actors were expired from the cache')
2019-08-20 09:37:09 +00:00
2020-04-02 09:02:33 +00:00
def storeWebfingerInCache(handle: str, wf, cachedWebfingers: {}) -> None:
2019-08-20 09:37:09 +00:00
"""Store a webfinger endpoint in the cache
"""
2020-04-02 09:02:33 +00:00
cachedWebfingers[handle] = wf
2019-06-30 15:03:26 +00:00
2020-04-02 09:02:33 +00:00
def getWebfingerFromCache(handle: str, cachedWebfingers: {}) -> {}:
2019-06-30 15:03:26 +00:00
"""Get webfinger endpoint from the cache
"""
if cachedWebfingers.get(handle):
return cachedWebfingers[handle]
return None
2021-07-31 11:56:28 +00:00
def getPersonPubKey(baseDir: str, session, personUrl: str,
personCache: {}, debug: bool,
projectVersion: str, httpPrefix: str,
domain: str, onionDomain: str,
signingPrivateKeyPem: str) -> str:
2021-07-31 11:56:28 +00:00
if not personUrl:
return None
personUrl = personUrl.replace('#main-key', '')
usersPaths = getUserPaths()
for possibleUsersPath in usersPaths:
if personUrl.endswith(possibleUsersPath + 'inbox'):
if debug:
print('DEBUG: Obtaining public key for shared inbox')
personUrl = \
personUrl.replace(possibleUsersPath + 'inbox', '/inbox')
break
personJson = \
getPersonFromCache(baseDir, personUrl, personCache, True)
if not personJson:
if debug:
print('DEBUG: Obtaining public key for ' + personUrl)
personDomain = domain
if onionDomain:
if '.onion/' in personUrl:
personDomain = onionDomain
profileStr = 'https://www.w3.org/ns/activitystreams'
asHeader = {
'Accept': 'application/activity+json; profile="' + profileStr + '"'
}
personJson = \
getJson(signingPrivateKeyPem,
session, personUrl, asHeader, None, debug,
2021-07-31 11:56:28 +00:00
projectVersion, httpPrefix, personDomain)
if not personJson:
return None
pubKey = None
if personJson.get('publicKey'):
if personJson['publicKey'].get('publicKeyPem'):
pubKey = personJson['publicKey']['publicKeyPem']
else:
if personJson.get('publicKeyPem'):
pubKey = personJson['publicKeyPem']
if not pubKey:
if debug:
print('DEBUG: Public key not found for ' + personUrl)
storePersonInCache(baseDir, personUrl, personJson, personCache, True)
return pubKey