Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon

main
Bob Mottram 2021-06-07 21:55:15 +01:00
commit bca847e5e5
8 changed files with 382 additions and 304 deletions

31
blog.py
View File

@ -863,3 +863,34 @@ def htmlEditBlog(mediaInstance: bool, translate: {},
editBlogForm += htmlFooter()
return editBlogForm
def pathContainsBlogLink(baseDir: str,
httpPrefix: str, domain: str,
domainFull: str, path: str) -> (str, str):
"""If the path contains a blog entry then return its filename
"""
if '/users/' not in path:
return None, None
userEnding = path.split('/users/', 1)[1]
if '/' not in userEnding:
return None, None
userEnding2 = userEnding.split('/')
nickname = userEnding2[0]
if len(userEnding2) != 2:
return None, None
if len(userEnding2[1]) < 14:
return None, None
userEnding2[1] = userEnding2[1].strip()
if not userEnding2[1].isdigit():
return None, None
# check for blog posts
blogIndexFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/tlblogs.index'
if not os.path.isfile(blogIndexFilename):
return None, None
if '#' + userEnding2[1] + '.' not in open(blogIndexFilename).read():
return None, None
messageId = httpPrefix + '://' + domainFull + \
'/users/' + nickname + '/statuses/' + userEnding2[1]
return locatePost(baseDir, nickname, domain, messageId), nickname

12
city.py
View File

@ -217,3 +217,15 @@ def spoofGeolocation(baseDir: str,
return (default_latitude, default_longitude,
default_latdirection, default_longdirection,
"", "", 0)
def getSpoofedCity(city: str, baseDir: str, nickname: str, domain: str) -> str:
"""Returns the name of the city to use as a GPS spoofing location for
image metadata
"""
cityFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/city.txt'
if os.path.isfile(cityFilename):
with open(cityFilename, 'r') as fp:
city = fp.read().replace('\n', '')
return city

366
daemon.py
View File

@ -24,10 +24,7 @@ from webfinger import webfingerMeta
from webfinger import webfingerNodeInfo
from webfinger import webfingerLookup
from webfinger import webfingerUpdate
from mastoapiv1 import getMastoApiV1Account
from mastoapiv1 import getMastApiV1Id
from mastoapiv1 import getNicknameFromMastoApiV1Id
from metadata import metaDataInstance
from mastoapiv1 import mastoApiV1Response
from metadata import metaDataNodeInfo
from metadata import metadataCustomEmoji
from pgp import getEmailAddress
@ -113,6 +110,9 @@ from threads import threadWithTrace
from threads import removeDormantThreads
from media import replaceYouTube
from media import attachMedia
from media import pathIsImage
from media import pathIsVideo
from media import pathIsAudio
from blocking import mutePost
from blocking import unmutePost
from blocking import setBrochMode
@ -130,12 +130,15 @@ from roles import clearModeratorStatus
from roles import clearEditorStatus
from roles import clearCounselorStatus
from roles import clearArtistStatus
from blog import pathContainsBlogLink
from blog import htmlBlogPageRSS2
from blog import htmlBlogPageRSS3
from blog import htmlBlogView
from blog import htmlBlogPage
from blog import htmlBlogPost
from blog import htmlEditBlog
from webapp_utils import setMinimal
from webapp_utils import isMinimal
from webapp_utils import getAvatarImageUrl
from webapp_utils import htmlHashtagBlocked
from webapp_utils import htmlFollowingList
@ -203,6 +206,7 @@ from shares import addShare
from shares import removeShare
from shares import expireShares
from categories import setHashtagCategory
from utils import permittedDir
from utils import isAccountDir
from utils import getOccupationSkills
from utils import getOccupationName
@ -290,6 +294,7 @@ from filters import addGlobalFilter
from filters import removeGlobalFilter
from context import hasValidContext
from speaker import getSSMLbox
from city import getSpoofedCity
import os
@ -327,18 +332,6 @@ def saveDomainQrcode(baseDir: str, httpPrefix: str,
class PubServer(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def _getSpoofedCity(self, baseDir: str, nickname: str, domain: str) -> str:
"""Returns the name of the city to use as a GPS spoofing location for
image metadata
"""
city = self.server.city
cityFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/city.txt'
if os.path.isfile(cityFilename):
with open(cityFilename, 'r') as fp:
city = fp.read().replace('\n', '')
return city
def _getInstalceUrl(self, callingDomain: str) -> str:
"""Returns the URL for this instance
"""
@ -366,61 +359,11 @@ class PubServer(BaseHTTPRequestHandler):
return self.headers['signature']
return None
def _pathIsImage(self, path: str) -> bool:
if path.endswith('.png') or \
path.endswith('.jpg') or \
path.endswith('.gif') or \
path.endswith('.svg') or \
path.endswith('.avif') or \
path.endswith('.webp'):
return True
return False
def _pathIsVideo(self, path: str) -> bool:
if path.endswith('.ogv') or \
path.endswith('.mp4'):
return True
return False
def _pathIsAudio(self, path: str) -> bool:
if path.endswith('.ogg') or \
path.endswith('.mp3'):
return True
return False
def handle_error(self, request, client_address):
print('ERROR: http server error: ' + str(request) + ', ' +
str(client_address))
pass
def _isMinimal(self, nickname: str) -> bool:
"""Returns true if minimal buttons should be shown
for the given account
"""
accountDir = self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain
if not os.path.isdir(accountDir):
return True
minimalFilename = accountDir + '/.notminimal'
if os.path.isfile(minimalFilename):
return False
return True
def _setMinimal(self, nickname: str, minimal: bool) -> None:
"""Sets whether an account should display minimal buttons
"""
accountDir = self.server.baseDir + '/accounts/' + \
nickname + '@' + self.server.domain
if not os.path.isdir(accountDir):
return
minimalFilename = accountDir + '/.notminimal'
minimalFileExists = os.path.isfile(minimalFilename)
if minimal and minimalFileExists:
os.remove(minimalFilename)
elif not minimal and not minimalFileExists:
with open(minimalFilename, 'w+') as fp:
fp.write('\n')
def _sendReplyToQuestion(self, nickname: str, messageId: str,
answer: str) -> None:
"""Sends a reply to a question
@ -447,7 +390,8 @@ class PubServer(BaseHTTPRequestHandler):
eventDate = None
eventTime = None
location = None
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname, self.server.domain)
messageJson = \
@ -860,6 +804,8 @@ class PubServer(BaseHTTPRequestHandler):
return True
def _hasAccept(self, callingDomain: str) -> bool:
"""Do the http headers have an Accept field?
"""
if self.headers.get('Accept') or callingDomain.endswith('.b32.i2p'):
if not self.headers.get('Accept'):
self.headers['Accept'] = \
@ -890,123 +836,23 @@ class PubServer(BaseHTTPRequestHandler):
print('mastodon api v1: authorized ' + str(authorized))
print('mastodon api v1: nickname ' + str(nickname))
sendJson = None
sendJsonStr = ''
# parts of the api needing authorization
if authorized and nickname:
if path == '/api/v1/accounts/verify_credentials':
sendJson = getMastoApiV1Account(baseDir, nickname, domain)
sendJsonStr = 'masto API account sent for ' + nickname
# Parts of the api which don't need authorization
mastoId = getMastApiV1Id(path)
if mastoId is not None:
pathNickname = getNicknameFromMastoApiV1Id(mastoId)
if pathNickname:
originalPath = path
if '/followers?' in path or \
'/following?' in path or \
'/search?' in path or \
'/relationships?' in path or \
'/statuses?' in path:
path = path.split('?')[0]
if path.endswith('/followers'):
sendJson = []
sendJsonStr = 'masto API followers sent for ' + nickname
elif path.endswith('/following'):
sendJson = []
sendJsonStr = 'masto API following sent for ' + nickname
elif path.endswith('/statuses'):
sendJson = []
sendJsonStr = 'masto API statuses sent for ' + nickname
elif path.endswith('/search'):
sendJson = []
sendJsonStr = 'masto API search sent ' + originalPath
elif path.endswith('/relationships'):
sendJson = []
sendJsonStr = \
'masto API relationships sent ' + originalPath
else:
sendJson = \
getMastoApiV1Account(baseDir, pathNickname, domain)
sendJsonStr = 'masto API account sent for ' + nickname
if path.startswith('/api/v1/blocks'):
sendJson = []
sendJsonStr = 'masto API instance blocks sent'
elif path.startswith('/api/v1/favorites'):
sendJson = []
sendJsonStr = 'masto API favorites sent'
elif path.startswith('/api/v1/follow_requests'):
sendJson = []
sendJsonStr = 'masto API follow requests sent'
elif path.startswith('/api/v1/mutes'):
sendJson = []
sendJsonStr = 'masto API mutes sent'
elif path.startswith('/api/v1/notifications'):
sendJson = []
sendJsonStr = 'masto API notifications sent'
elif path.startswith('/api/v1/reports'):
sendJson = []
sendJsonStr = 'masto API reports sent'
elif path.startswith('/api/v1/statuses'):
sendJson = []
sendJsonStr = 'masto API statuses sent'
elif path.startswith('/api/v1/timelines'):
sendJson = []
sendJsonStr = 'masto API timelines sent'
elif path.startswith('/api/v1/custom_emojis'):
sendJson = customEmoji
sendJsonStr = 'masto API custom emojis sent'
adminNickname = getConfigParam(baseDir, 'admin')
if adminNickname and path == '/api/v1/instance':
instanceDescriptionShort = \
getConfigParam(baseDir,
'instanceDescriptionShort')
if not instanceDescriptionShort:
instanceDescriptionShort = \
translate['Yet another Epicyon Instance']
instanceDescription = getConfigParam(baseDir,
'instanceDescription')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
if callingDomain.endswith('.onion') and onionDomain:
domainFull = onionDomain
httpPrefix = 'http'
elif (callingDomain.endswith('.i2p') and i2pDomain):
domainFull = i2pDomain
httpPrefix = 'http'
if brochModeIsActive(baseDir):
showNodeInfoAccounts = False
sendJson = \
metaDataInstance(showNodeInfoAccounts,
instanceTitle,
instanceDescriptionShort,
instanceDescription,
brochMode = brochModeIsActive(baseDir)
sendJson, sendJsonStr = mastoApiV1Response(path,
callingDomain,
authorized,
httpPrefix,
baseDir,
adminNickname,
domain,
nickname, domain,
domainFull,
onionDomain,
i2pDomain,
translate,
registration,
systemLanguage,
projectVersion)
sendJsonStr = 'masto API instance metadata sent'
elif path.startswith('/api/v1/instance/peers'):
# This is just a dummy result.
# Showing the full list of peers would have privacy implications.
# On a large instance you are somewhat lost in the crowd, but on
# small instances a full list of peers would convey a lot of
# information about the interests of a small number of accounts
sendJson = ['mastodon.social', domainFull]
sendJsonStr = 'masto API peers metadata sent'
elif path.startswith('/api/v1/instance/activity'):
sendJson = []
sendJsonStr = 'masto API activity metadata sent'
projectVersion,
customEmoji,
showNodeInfoAccounts,
brochMode)
if sendJson is not None:
msg = json.dumps(sendJson).encode('utf-8')
@ -1176,16 +1022,6 @@ class PubServer(BaseHTTPRequestHandler):
self._404()
return True
def _permittedDir(self, path: str) -> bool:
"""These are special paths which should not be accessible
directly via GET or POST
"""
if path.startswith('/wfendpoints') or \
path.startswith('/keys') or \
path.startswith('/accounts'):
return False
return True
def _postToOutbox(self, messageJson: {}, version: str,
postToNickname=None) -> bool:
"""post is received by the outbox
@ -1197,7 +1033,8 @@ class PubServer(BaseHTTPRequestHandler):
if postToNickname:
print('Posting to nickname ' + postToNickname)
self.postToNickname = postToNickname
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
postToNickname, self.server.domain)
return postMessageToOutbox(self.server.session,
@ -1427,12 +1264,12 @@ class PubServer(BaseHTTPRequestHandler):
def _isAuthorized(self) -> bool:
self.authorizedNickname = None
if self.path.startswith('/icons/') or \
self.path.startswith('/avatars/') or \
self.path.startswith('/favicon.ico') or \
self.path.startswith('/newswire_favicon.ico') or \
self.path.startswith('/categories.xml') or \
self.path.startswith('/newswire.xml'):
notAuthPaths = (
'/icons/', '/avatars/', '/favicon.ico', '/newswire.xml',
'/newswire_favicon.ico', '/categories.xml'
)
for notAuthStr in notAuthPaths:
if self.path.startswith(notAuthStr):
return False
# token based authenticated used by the web interface
@ -1526,36 +1363,6 @@ class PubServer(BaseHTTPRequestHandler):
print('POST TIMING|' + str(ctr) + '|' + timeDiff)
ctr += 1
def _pathContainsBlogLink(self, baseDir: str,
httpPrefix: str, domain: str,
domainFull: str, path: str) -> (str, str):
"""If the path contains a blog entry then return its filename
"""
if '/users/' not in path:
return None, None
userEnding = path.split('/users/', 1)[1]
if '/' not in userEnding:
return None, None
userEnding2 = userEnding.split('/')
nickname = userEnding2[0]
if len(userEnding2) != 2:
return None, None
if len(userEnding2[1]) < 14:
return None, None
userEnding2[1] = userEnding2[1].strip()
if not userEnding2[1].isdigit():
return None, None
# check for blog posts
blogIndexFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/tlblogs.index'
if not os.path.isfile(blogIndexFilename):
return None, None
if '#' + userEnding2[1] + '.' not in open(blogIndexFilename).read():
return None, None
messageId = httpPrefix + '://' + domainFull + \
'/users/' + nickname + '/statuses/' + userEnding2[1]
return locatePost(baseDir, nickname, domain, messageId), nickname
def _loginScreen(self, path: str, callingDomain: str, cookie: str,
baseDir: str, httpPrefix: str,
domain: str, domainFull: str, port: int,
@ -4184,7 +3991,8 @@ class PubServer(BaseHTTPRequestHandler):
except BaseException:
pass
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
processMetaData(baseDir, nickname, domain,
filename, postImageFilename, city)
@ -6041,9 +5849,9 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> None:
"""Returns a media file
"""
if self._pathIsImage(path) or \
self._pathIsVideo(path) or \
self._pathIsAudio(path):
if pathIsImage(path) or \
pathIsVideo(path) or \
pathIsAudio(path):
mediaStr = path.split('/media/')[1]
mediaFilename = baseDir + '/media/' + mediaStr
if os.path.isfile(mediaFilename):
@ -6071,7 +5879,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> None:
"""Returns an emoji image
"""
if self._pathIsImage(path):
if pathIsImage(path):
emojiStr = path.split('/emoji/')[1]
emojiFilename = baseDir + '/emoji/' + emojiStr
if os.path.isfile(emojiFilename):
@ -7585,7 +7393,9 @@ class PubServer(BaseHTTPRequestHandler):
accessKeys = self.server.keyShortcuts[nickname]
rolesList = getActorRolesList(actorJson)
city = self._getSpoofedCity(baseDir, nickname, domain)
city = \
getSpoofedCity(self.server.city,
baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
self.server.cssCache,
@ -7683,7 +7493,8 @@ class PubServer(BaseHTTPRequestHandler):
actorSkillsList = \
getOccupationSkills(actorJson)
skills = getSkillsFromList(actorSkillsList)
city = self._getSpoofedCity(baseDir,
city = getSpoofedCity(self.server.city,
baseDir,
nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
@ -8073,7 +7884,7 @@ class PubServer(BaseHTTPRequestHandler):
'show inbox page')
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8209,7 +8020,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8338,7 +8149,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8467,7 +8278,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8597,7 +8408,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8735,7 +8546,7 @@ class PubServer(BaseHTTPRequestHandler):
editor = isEditor(baseDir, currNickname)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -8871,7 +8682,7 @@ class PubServer(BaseHTTPRequestHandler):
currNickname = currNickname.split('/')[0]
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -9080,7 +8891,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -9213,7 +9024,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -9338,7 +9149,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = self._isMinimal(nickname)
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
@ -9582,7 +9393,8 @@ class PubServer(BaseHTTPRequestHandler):
accessKeys = \
self.server.keyShortcuts[nickname]
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
self.server.cssCache,
@ -9694,7 +9506,8 @@ class PubServer(BaseHTTPRequestHandler):
accessKeys = \
self.server.keyShortcuts[nickname]
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
self.server.cssCache,
@ -9805,7 +9618,8 @@ class PubServer(BaseHTTPRequestHandler):
accessKeys = \
self.server.keyShortcuts[nickname]
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
self.server.cssCache,
@ -9940,7 +9754,8 @@ class PubServer(BaseHTTPRequestHandler):
accessKeys = \
self.server.keyShortcuts[nickname]
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
self.server.cssCache,
@ -10309,7 +10124,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> bool:
"""Show a shared item image
"""
if not self._pathIsImage(path):
if not pathIsImage(path):
self._404()
return True
@ -10357,7 +10172,7 @@ class PubServer(BaseHTTPRequestHandler):
"""
if '/users/' not in path:
return False
if not self._pathIsImage(path):
if not pathIsImage(path):
return False
avatarStr = path.split('/users/')[1]
if not ('/' in avatarStr and '.temp.' not in path):
@ -10542,7 +10357,8 @@ class PubServer(BaseHTTPRequestHandler):
peertubeInstances = self.server.peertubeInstances
nickname = getNicknameFromActor(path)
if nickname:
city = self._getSpoofedCity(baseDir, nickname, domain)
city = getSpoofedCity(self.server.city,
baseDir, nickname, domain)
else:
city = self.server.city
@ -11290,7 +11106,7 @@ class PubServer(BaseHTTPRequestHandler):
'person options done')
# show blog post
blogFilename, nickname = \
self._pathContainsBlogLink(self.server.baseDir,
pathContainsBlogLink(self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.domainFull,
@ -11569,7 +11385,7 @@ class PubServer(BaseHTTPRequestHandler):
# if not authorized then show the login screen
if htmlGET and self.path != '/login' and \
not self._pathIsImage(self.path) and \
not pathIsImage(self.path) and \
self.path != '/' and \
self.path != '/users/news/linksmobile' and \
self.path != '/users/news/newswiremobile':
@ -11873,7 +11689,7 @@ class PubServer(BaseHTTPRequestHandler):
'avatar background shown done',
'GET busy time')
if not self._permittedDir(self.path):
if not permittedDir(self.path):
if self.server.debug:
print('DEBUG: GET Not permitted')
self._404()
@ -12062,7 +11878,10 @@ class PubServer(BaseHTTPRequestHandler):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
self._setMinimal(nickname, not self._isMinimal(nickname))
notMin = not isMinimal(self.server.baseDir,
self.server.domain, nickname)
setMinimal(self.server.baseDir,
self.server.domain, nickname, notMin)
if not (self.server.mediaInstance or
self.server.blogsInstance):
self.path = '/users/' + nickname + '/inbox'
@ -13227,9 +13046,9 @@ class PubServer(BaseHTTPRequestHandler):
fileLength = -1
if '/media/' in self.path:
if self._pathIsImage(self.path) or \
self._pathIsVideo(self.path) or \
self._pathIsAudio(self.path):
if pathIsImage(self.path) or \
pathIsVideo(self.path) or \
pathIsAudio(self.path):
mediaStr = self.path.split('/media/')[1]
mediaFilename = \
self.server.baseDir + '/media/' + mediaStr
@ -13335,7 +13154,8 @@ class PubServer(BaseHTTPRequestHandler):
filename.endswith('.gif'):
postImageFilename = filename.replace('.temp', '')
print('Removing metadata from ' + postImageFilename)
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname, self.server.domain)
processMetaData(self.server.baseDir,
nickname, self.server.domain,
@ -13448,7 +13268,8 @@ class PubServer(BaseHTTPRequestHandler):
nickname, self.server.domain)
return 1
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname, self.server.domain)
messageJson = \
createPublicPost(self.server.baseDir,
@ -13597,7 +13418,8 @@ class PubServer(BaseHTTPRequestHandler):
imgDescription = fields['imageDescription']
if filename:
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
postJsonObject['object'] = \
@ -13632,7 +13454,8 @@ class PubServer(BaseHTTPRequestHandler):
str(fields['postUrl']))
return -1
elif postType == 'newunlisted':
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13666,7 +13489,8 @@ class PubServer(BaseHTTPRequestHandler):
else:
return -1
elif postType == 'newfollowers':
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13722,7 +13546,8 @@ class PubServer(BaseHTTPRequestHandler):
maximumAttendeeCapacity = \
int(fields['maximumAttendeeCapacity'])
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13762,7 +13587,8 @@ class PubServer(BaseHTTPRequestHandler):
messageJson = None
print('A DM was posted')
if '@' in mentionsStr:
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13806,7 +13632,8 @@ class PubServer(BaseHTTPRequestHandler):
print('A reminder was posted for ' + handle)
if '@' + handle not in mentionsStr:
mentionsStr = '@' + handle + ' ' + mentionsStr
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13843,7 +13670,8 @@ class PubServer(BaseHTTPRequestHandler):
# and not accounts being reported we disable any
# included fediverse addresses by replacing '@' with '-at-'
fields['message'] = fields['message'].replace('@', '-at-')
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13875,7 +13703,8 @@ class PubServer(BaseHTTPRequestHandler):
str(questionCtr)])
if not qOptions:
return -1
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
@ -13914,7 +13743,8 @@ class PubServer(BaseHTTPRequestHandler):
if durationStr:
if ' ' not in durationStr:
durationStr = durationStr + ' days'
city = self._getSpoofedCity(self.server.baseDir,
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
addShare(self.server.baseDir,

View File

@ -8,9 +8,11 @@ __status__ = "Production"
import os
from utils import loadJson
from utils import getConfigParam
from metadata import metaDataInstance
def getMastApiV1Id(path: str) -> int:
def _getMastApiV1Id(path: str) -> int:
"""Extracts the mastodon Id number from the given path
"""
mastoId = None
@ -46,7 +48,7 @@ def getNicknameFromMastoApiV1Id(mastoId: int) -> str:
return nickname[::-1]
def getMastoApiV1Account(baseDir: str, nickname: str, domain: str) -> {}:
def _getMastoApiV1Account(baseDir: str, nickname: str, domain: str) -> {}:
"""See https://github.com/McKael/mastodon-documentation/
blob/master/Using-the-API/API.md#account
Authorization has already been performed
@ -76,3 +78,140 @@ def getMastoApiV1Account(baseDir: str, nickname: str, domain: str) -> {}:
"header_static": accountJson['image']['url']
}
return mastoAccountJson
def mastoApiV1Response(path: str, callingDomain: str,
authorized: bool,
httpPrefix: str,
baseDir: str, nickname: str, domain: str,
domainFull: str,
onionDomain: str, i2pDomain: str,
translate: {},
registration: bool,
systemLanguage: str,
projectVersion: str,
customEmoji: [],
showNodeInfoAccounts: bool,
brochMode: bool) -> ({}, str):
"""This is a vestigil mastodon API for the purpose
of returning an empty result to sites like
https://mastopeek.app-dist.eu
"""
sendJson = None
sendJsonStr = ''
# parts of the api needing authorization
if authorized and nickname:
if path == '/api/v1/accounts/verify_credentials':
sendJson = _getMastoApiV1Account(baseDir, nickname, domain)
sendJsonStr = 'masto API account sent for ' + nickname
# Parts of the api which don't need authorization
mastoId = _getMastApiV1Id(path)
if mastoId is not None:
pathNickname = getNicknameFromMastoApiV1Id(mastoId)
if pathNickname:
originalPath = path
if '/followers?' in path or \
'/following?' in path or \
'/search?' in path or \
'/relationships?' in path or \
'/statuses?' in path:
path = path.split('?')[0]
if path.endswith('/followers'):
sendJson = []
sendJsonStr = 'masto API followers sent for ' + nickname
elif path.endswith('/following'):
sendJson = []
sendJsonStr = 'masto API following sent for ' + nickname
elif path.endswith('/statuses'):
sendJson = []
sendJsonStr = 'masto API statuses sent for ' + nickname
elif path.endswith('/search'):
sendJson = []
sendJsonStr = 'masto API search sent ' + originalPath
elif path.endswith('/relationships'):
sendJson = []
sendJsonStr = \
'masto API relationships sent ' + originalPath
else:
sendJson = \
_getMastoApiV1Account(baseDir, pathNickname, domain)
sendJsonStr = 'masto API account sent for ' + nickname
if path.startswith('/api/v1/blocks'):
sendJson = []
sendJsonStr = 'masto API instance blocks sent'
elif path.startswith('/api/v1/favorites'):
sendJson = []
sendJsonStr = 'masto API favorites sent'
elif path.startswith('/api/v1/follow_requests'):
sendJson = []
sendJsonStr = 'masto API follow requests sent'
elif path.startswith('/api/v1/mutes'):
sendJson = []
sendJsonStr = 'masto API mutes sent'
elif path.startswith('/api/v1/notifications'):
sendJson = []
sendJsonStr = 'masto API notifications sent'
elif path.startswith('/api/v1/reports'):
sendJson = []
sendJsonStr = 'masto API reports sent'
elif path.startswith('/api/v1/statuses'):
sendJson = []
sendJsonStr = 'masto API statuses sent'
elif path.startswith('/api/v1/timelines'):
sendJson = []
sendJsonStr = 'masto API timelines sent'
elif path.startswith('/api/v1/custom_emojis'):
sendJson = customEmoji
sendJsonStr = 'masto API custom emojis sent'
adminNickname = getConfigParam(baseDir, 'admin')
if adminNickname and path == '/api/v1/instance':
instanceDescriptionShort = \
getConfigParam(baseDir,
'instanceDescriptionShort')
if not instanceDescriptionShort:
instanceDescriptionShort = \
translate['Yet another Epicyon Instance']
instanceDescription = getConfigParam(baseDir,
'instanceDescription')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
if callingDomain.endswith('.onion') and onionDomain:
domainFull = onionDomain
httpPrefix = 'http'
elif (callingDomain.endswith('.i2p') and i2pDomain):
domainFull = i2pDomain
httpPrefix = 'http'
if brochMode:
showNodeInfoAccounts = False
sendJson = \
metaDataInstance(showNodeInfoAccounts,
instanceTitle,
instanceDescriptionShort,
instanceDescription,
httpPrefix,
baseDir,
adminNickname,
domain,
domainFull,
registration,
systemLanguage,
projectVersion)
sendJsonStr = 'masto API instance metadata sent'
elif path.startswith('/api/v1/instance/peers'):
# This is just a dummy result.
# Showing the full list of peers would have privacy implications.
# On a large instance you are somewhat lost in the crowd, but on
# small instances a full list of peers would convey a lot of
# information about the interests of a small number of accounts
sendJson = ['mastodon.social', domainFull]
sendJsonStr = 'masto API peers metadata sent'
elif path.startswith('/api/v1/instance/activity'):
sendJson = []
sendJsonStr = 'masto API activity metadata sent'
return sendJson, sendJsonStr

View File

@ -283,3 +283,28 @@ def archiveMedia(baseDir: str, archiveDirectory: str, maxWeeks=4) -> None:
# archive to /dev/null
rmtree(os.path.join(baseDir + '/media', weekDir))
break
def pathIsImage(path: str) -> bool:
if path.endswith('.png') or \
path.endswith('.jpg') or \
path.endswith('.gif') or \
path.endswith('.svg') or \
path.endswith('.avif') or \
path.endswith('.webp'):
return True
return False
def pathIsVideo(path: str) -> bool:
if path.endswith('.ogv') or \
path.endswith('.mp4'):
return True
return False
def pathIsAudio(path: str) -> bool:
if path.endswith('.ogg') or \
path.endswith('.mp3'):
return True
return False

View File

@ -2410,3 +2410,14 @@ def isAccountDir(dirName: str) -> bool:
if 'inbox@' in dirName or 'news@' in dirName:
return False
return True
def permittedDir(path: str) -> bool:
"""These are special paths which should not be accessible
directly via GET or POST
"""
if path.startswith('/wfendpoints') or \
path.startswith('/keys') or \
path.startswith('/accounts'):
return False
return True

View File

@ -1358,3 +1358,33 @@ def htmlKeyboardNavigation(banner: str, links: {}, accessKeys: {},
str(title) + '</a></label></li>\n'
htmlStr += '</ul></div>\n'
return htmlStr
def isMinimal(baseDir: str, domain: str, nickname: str) -> bool:
"""Returns true if minimal buttons should be shown
for the given account
"""
accountDir = baseDir + '/accounts/' + \
nickname + '@' + domain
if not os.path.isdir(accountDir):
return True
minimalFilename = accountDir + '/.notminimal'
if os.path.isfile(minimalFilename):
return False
return True
def setMinimal(baseDir: str, domain: str, nickname: str,
minimal: bool) -> None:
"""Sets whether an account should display minimal buttons
"""
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
if not os.path.isdir(accountDir):
return
minimalFilename = accountDir + '/.notminimal'
minimalFileExists = os.path.isfile(minimalFilename)
if minimal and minimalFileExists:
os.remove(minimalFilename)
elif not minimal and not minimalFileExists:
with open(minimalFilename, 'w+') as fp:
fp.write('\n')