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

main
Bob Mottram 2021-05-10 16:56:12 +01:00
commit bd46688305
33 changed files with 4902 additions and 274 deletions

422
daemon.py
View File

@ -239,7 +239,7 @@ from content import addHtmlTags
from content import extractMediaInFormPOST
from content import saveMediaInFormPOST
from content import extractTextFieldsInPOST
from media import removeMetaData
from media import processMetaData
from cache import checkForChangedActor
from cache import storePersonInCache
from cache import getPersonFromCache
@ -312,6 +312,18 @@ 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
"""
@ -420,6 +432,9 @@ class PubServer(BaseHTTPRequestHandler):
eventDate = None
eventTime = None
location = None
city = self._getSpoofedCity(self.server.baseDir,
nickname, self.server.domain)
messageJson = \
createPublicPost(self.server.baseDir,
nickname,
@ -428,7 +443,7 @@ class PubServer(BaseHTTPRequestHandler):
answer, False, False, False,
commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
inReplyTo,
inReplyToAtomUri,
subject,
@ -1144,9 +1159,13 @@ class PubServer(BaseHTTPRequestHandler):
Client to server message post
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
"""
city = self.server.city
if postToNickname:
print('Posting to nickname ' + postToNickname)
self.postToNickname = postToNickname
city = self._getSpoofedCity(self.server.baseDir,
postToNickname, self.server.domain)
return postMessageToOutbox(self.server.session,
self.server.translate,
@ -1170,7 +1189,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.debug,
self.server.YTReplacementDomain,
self.server.showPublishedDateOnly,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
city)
def _postToOutboxThread(self, messageJson: {}) -> bool:
"""Creates a thread to send a post
@ -4075,7 +4095,11 @@ class PubServer(BaseHTTPRequestHandler):
os.remove(postImageFilename + '.etag')
except BaseException:
pass
removeMetaData(filename, postImageFilename)
city = self._getSpoofedCity(baseDir, nickname, domain)
processMetaData(baseDir, nickname, domain,
filename, postImageFilename, city)
if os.path.isfile(postImageFilename):
print('profile update POST ' + mType +
' image or font saved to ' + postImageFilename)
@ -4226,6 +4250,13 @@ class PubServer(BaseHTTPRequestHandler):
nickname,
pwd)
# change city
if fields.get('cityDropdown'):
cityFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/city.txt'
with open(cityFilename, 'w+') as fp:
fp.write(fields['cityDropdown'])
# change displayed name
if fields.get('displayNickname'):
if fields['displayNickname'] != actorJson['name']:
@ -4244,118 +4275,175 @@ class PubServer(BaseHTTPRequestHandler):
if checkNameAndBio:
redirectPath = 'previewAvatar'
# change media instance status
if fields.get('mediaInstance'):
self.server.mediaInstance = False
self.server.defaultTimeline = 'inbox'
if fields['mediaInstance'] == 'on':
self.server.mediaInstance = True
self.server.blogsInstance = False
self.server.newsInstance = False
self.server.defaultTimeline = 'tlmedia'
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.mediaInstance:
if nickname == adminNickname:
# change media instance status
if fields.get('mediaInstance'):
self.server.mediaInstance = False
self.server.defaultTimeline = 'inbox'
if fields['mediaInstance'] == 'on':
self.server.mediaInstance = True
self.server.blogsInstance = False
self.server.newsInstance = False
self.server.defaultTimeline = 'tlmedia'
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
# is this a news theme?
if isNewsThemeName(self.server.baseDir,
self.server.themeName):
fields['newsInstance'] = 'on'
# change news instance status
if fields.get('newsInstance'):
self.server.newsInstance = False
self.server.defaultTimeline = 'inbox'
if fields['newsInstance'] == 'on':
self.server.newsInstance = True
self.server.blogsInstance = False
self.server.mediaInstance = False
self.server.defaultTimeline = 'tlfeatures'
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.newsInstance:
self.server.newsInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(baseDir,
"newsInstance",
self.server.mediaInstance)
# change blog instance status
if fields.get('blogsInstance'):
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
if fields['blogsInstance'] == 'on':
self.server.blogsInstance = True
self.server.mediaInstance = False
self.server.newsInstance = False
self.server.defaultTimeline = 'tlblogs'
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.blogsInstance:
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.mediaInstance:
self.server.mediaInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
# change theme
if fields.get('themeDropdown'):
self.server.themeName = fields['themeDropdown']
setTheme(baseDir, self.server.themeName, domain,
allowLocalNetworkAccess, systemLanguage)
self.server.textModeBanner = \
getTextModeBanner(self.server.baseDir)
self.server.iconsCache = {}
self.server.fontsCache = {}
self.server.showPublishAsIcon = \
getConfigParam(self.server.baseDir,
'showPublishAsIcon')
self.server.fullWidthTimelineButtonHeader = \
getConfigParam(self.server.baseDir,
'fullWidthTimelineButtonHeader')
self.server.iconsAsButtons = \
getConfigParam(self.server.baseDir,
'iconsAsButtons')
self.server.rssIconAtTop = \
getConfigParam(self.server.baseDir,
'rssIconAtTop')
self.server.publishButtonAtTop = \
getConfigParam(self.server.baseDir,
'publishButtonAtTop')
setNewsAvatar(baseDir,
fields['themeDropdown'],
httpPrefix,
domain,
domainFull)
# is this a news theme?
if isNewsThemeName(self.server.baseDir,
self.server.themeName):
fields['newsInstance'] = 'on'
# change news instance status
if fields.get('newsInstance'):
self.server.newsInstance = False
self.server.defaultTimeline = 'inbox'
if fields['newsInstance'] == 'on':
self.server.newsInstance = True
self.server.blogsInstance = False
self.server.mediaInstance = False
self.server.defaultTimeline = 'tlfeatures'
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.newsInstance:
self.server.newsInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(baseDir,
"newsInstance",
self.server.mediaInstance)
# change blog instance status
if fields.get('blogsInstance'):
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
if fields['blogsInstance'] == 'on':
self.server.blogsInstance = True
self.server.mediaInstance = False
self.server.newsInstance = False
self.server.defaultTimeline = 'tlblogs'
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
setConfigParam(baseDir,
"mediaInstance",
self.server.mediaInstance)
setConfigParam(baseDir,
"newsInstance",
self.server.newsInstance)
else:
if self.server.blogsInstance:
self.server.blogsInstance = False
self.server.defaultTimeline = 'inbox'
setConfigParam(baseDir,
"blogsInstance",
self.server.blogsInstance)
# change theme
if fields.get('themeDropdown'):
self.server.themeName = fields['themeDropdown']
setTheme(baseDir, self.server.themeName, domain,
allowLocalNetworkAccess, systemLanguage)
self.server.textModeBanner = \
getTextModeBanner(self.server.baseDir)
self.server.iconsCache = {}
self.server.fontsCache = {}
self.server.showPublishAsIcon = \
getConfigParam(self.server.baseDir,
'showPublishAsIcon')
self.server.fullWidthTimelineButtonHeader = \
getConfigParam(self.server.baseDir,
'fullWidthTimelineButtonHeader')
self.server.iconsAsButtons = \
getConfigParam(self.server.baseDir,
'iconsAsButtons')
self.server.rssIconAtTop = \
getConfigParam(self.server.baseDir,
'rssIconAtTop')
self.server.publishButtonAtTop = \
getConfigParam(self.server.baseDir,
'publishButtonAtTop')
setNewsAvatar(baseDir,
fields['themeDropdown'],
httpPrefix,
domain,
domainFull)
# change instance title
if fields.get('instanceTitle'):
currInstanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
if fields['instanceTitle'] != currInstanceTitle:
setConfigParam(baseDir, 'instanceTitle',
fields['instanceTitle'])
# change YouTube alternate domain
if fields.get('ytdomain'):
currYTDomain = self.server.YTReplacementDomain
if fields['ytdomain'] != currYTDomain:
newYTDomain = fields['ytdomain']
if '://' in newYTDomain:
newYTDomain = newYTDomain.split('://')[1]
if '/' in newYTDomain:
newYTDomain = newYTDomain.split('/')[0]
if '.' in newYTDomain:
setConfigParam(baseDir,
'youtubedomain',
newYTDomain)
self.server.YTReplacementDomain = \
newYTDomain
else:
setConfigParam(baseDir,
'youtubedomain', '')
self.server.YTReplacementDomain = None
# change instance description
currInstanceDescriptionShort = \
getConfigParam(baseDir,
'instanceDescriptionShort')
if fields.get('instanceDescriptionShort'):
if fields['instanceDescriptionShort'] != \
currInstanceDescriptionShort:
iDesc = fields['instanceDescriptionShort']
setConfigParam(baseDir,
'instanceDescriptionShort',
iDesc)
else:
if currInstanceDescriptionShort:
setConfigParam(baseDir,
'instanceDescriptionShort', '')
currInstanceDescription = \
getConfigParam(baseDir, 'instanceDescription')
if fields.get('instanceDescription'):
if fields['instanceDescription'] != \
currInstanceDescription:
setConfigParam(baseDir,
'instanceDescription',
fields['instanceDescription'])
else:
if currInstanceDescription:
setConfigParam(baseDir,
'instanceDescription', '')
# change email address
currentEmailAddress = getEmailAddress(actorJson)
@ -4536,62 +4624,6 @@ class PubServer(BaseHTTPRequestHandler):
del actorJson['alsoKnownAs']
actorChanged = True
# change instance title
if fields.get('instanceTitle'):
currInstanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
if fields['instanceTitle'] != currInstanceTitle:
setConfigParam(baseDir, 'instanceTitle',
fields['instanceTitle'])
# change YouTube alternate domain
if fields.get('ytdomain'):
currYTDomain = self.server.YTReplacementDomain
if fields['ytdomain'] != currYTDomain:
newYTDomain = fields['ytdomain']
if '://' in newYTDomain:
newYTDomain = newYTDomain.split('://')[1]
if '/' in newYTDomain:
newYTDomain = newYTDomain.split('/')[0]
if '.' in newYTDomain:
setConfigParam(baseDir,
'youtubedomain',
newYTDomain)
self.server.YTReplacementDomain = \
newYTDomain
else:
setConfigParam(baseDir,
'youtubedomain', '')
self.server.YTReplacementDomain = None
# change instance description
currInstanceDescriptionShort = \
getConfigParam(baseDir,
'instanceDescriptionShort')
if fields.get('instanceDescriptionShort'):
if fields['instanceDescriptionShort'] != \
currInstanceDescriptionShort:
iDesc = fields['instanceDescriptionShort']
setConfigParam(baseDir,
'instanceDescriptionShort',
iDesc)
else:
if currInstanceDescriptionShort:
setConfigParam(baseDir,
'instanceDescriptionShort', '')
currInstanceDescription = \
getConfigParam(baseDir, 'instanceDescription')
if fields.get('instanceDescription'):
if fields['instanceDescription'] != \
currInstanceDescription:
setConfigParam(baseDir,
'instanceDescription',
fields['instanceDescription'])
else:
if currInstanceDescription:
setConfigParam(baseDir,
'instanceDescription', '')
# change user bio
if fields.get('bio'):
if fields['bio'] != actorJson['summary']:
@ -10293,6 +10325,11 @@ class PubServer(BaseHTTPRequestHandler):
"""
if '/users/' in path and path.endswith('/editprofile'):
peertubeInstances = self.server.peertubeInstances
nickname = getNicknameFromActor(path)
if nickname:
city = self._getSpoofedCity(baseDir, nickname, domain)
else:
city = self.server.city
msg = htmlEditProfile(self.server.cssCache,
translate,
baseDir,
@ -10302,7 +10339,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.defaultTimeline,
self.server.themeName,
peertubeInstances,
self.server.textModeBanner).encode('utf-8')
self.server.textModeBanner,
city).encode('utf-8')
if msg:
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -13053,7 +13091,11 @@ class PubServer(BaseHTTPRequestHandler):
filename.endswith('.gif'):
postImageFilename = filename.replace('.temp', '')
print('Removing metadata from ' + postImageFilename)
removeMetaData(filename, postImageFilename)
city = self._getSpoofedCity(self.server.baseDir,
nickname, self.server.domain)
processMetaData(self.server.baseDir,
nickname, self.server.domain,
filename, postImageFilename, city)
if os.path.isfile(postImageFilename):
print('POST media saved to ' + postImageFilename)
else:
@ -13158,6 +13200,8 @@ class PubServer(BaseHTTPRequestHandler):
nickname, self.server.domain)
return 1
city = self._getSpoofedCity(self.server.baseDir,
nickname, self.server.domain)
messageJson = \
createPublicPost(self.server.baseDir,
nickname,
@ -13168,6 +13212,7 @@ class PubServer(BaseHTTPRequestHandler):
False, False, False, commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['replyTo'], fields['replyTo'],
fields['subject'], fields['schedulePost'],
fields['eventDate'], fields['eventTime'],
@ -13304,15 +13349,20 @@ class PubServer(BaseHTTPRequestHandler):
imgDescription = fields['imageDescription']
if filename:
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
postJsonObject['object'] = \
attachMedia(self.server.baseDir,
self.server.httpPrefix,
nickname,
self.server.domain,
self.server.port,
postJsonObject['object'],
filename,
attachmentMediaType,
imgDescription)
imgDescription,
city)
replaceYouTube(postJsonObject,
self.server.YTReplacementDomain)
@ -13334,6 +13384,9 @@ class PubServer(BaseHTTPRequestHandler):
str(fields['postUrl']))
return -1
elif postType == 'newunlisted':
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createUnlistedPost(self.server.baseDir,
nickname,
@ -13343,6 +13396,7 @@ class PubServer(BaseHTTPRequestHandler):
False, False, False, commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
@ -13364,6 +13418,9 @@ class PubServer(BaseHTTPRequestHandler):
else:
return -1
elif postType == 'newfollowers':
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createFollowersOnlyPost(self.server.baseDir,
nickname,
@ -13375,6 +13432,7 @@ class PubServer(BaseHTTPRequestHandler):
commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
@ -13416,6 +13474,9 @@ class PubServer(BaseHTTPRequestHandler):
maximumAttendeeCapacity = \
int(fields['maximumAttendeeCapacity'])
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createEventPost(self.server.baseDir,
nickname,
@ -13427,6 +13488,7 @@ class PubServer(BaseHTTPRequestHandler):
False, False, commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['subject'],
fields['schedulePost'],
fields['eventDate'],
@ -13452,6 +13514,9 @@ class PubServer(BaseHTTPRequestHandler):
messageJson = None
print('A DM was posted')
if '@' in mentionsStr:
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createDirectMessagePost(self.server.baseDir,
nickname,
@ -13464,6 +13529,7 @@ class PubServer(BaseHTTPRequestHandler):
commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['replyTo'],
fields['replyTo'],
fields['subject'],
@ -13492,6 +13558,9 @@ class PubServer(BaseHTTPRequestHandler):
print('A reminder was posted for ' + handle)
if '@' + handle not in mentionsStr:
mentionsStr = '@' + handle + ' ' + mentionsStr
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createDirectMessagePost(self.server.baseDir,
nickname,
@ -13502,6 +13571,7 @@ class PubServer(BaseHTTPRequestHandler):
True, False, False, False,
filename, attachmentMediaType,
fields['imageDescription'],
city,
None, None,
fields['subject'],
True, fields['schedulePost'],
@ -13525,6 +13595,9 @@ 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,
nickname,
self.server.domain)
messageJson = \
createReportPost(self.server.baseDir,
nickname,
@ -13534,6 +13607,7 @@ class PubServer(BaseHTTPRequestHandler):
True, False, False, True,
filename, attachmentMediaType,
fields['imageDescription'],
city,
self.server.debug, fields['subject'])
if messageJson:
if self._postToOutbox(messageJson, __version__, nickname):
@ -13553,6 +13627,9 @@ class PubServer(BaseHTTPRequestHandler):
str(questionCtr)])
if not qOptions:
return -1
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createQuestionPost(self.server.baseDir,
nickname,
@ -13564,6 +13641,7 @@ class PubServer(BaseHTTPRequestHandler):
commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['subject'],
int(fields['duration']))
if messageJson:
@ -13588,6 +13666,9 @@ class PubServer(BaseHTTPRequestHandler):
if durationStr:
if ' ' not in durationStr:
durationStr = durationStr + ' days'
city = self._getSpoofedCity(self.server.baseDir,
nickname,
self.server.domain)
addShare(self.server.baseDir,
self.server.httpPrefix,
nickname,
@ -13599,7 +13680,8 @@ class PubServer(BaseHTTPRequestHandler):
fields['category'],
fields['location'],
durationStr,
self.server.debug)
self.server.debug,
city)
if filename:
if os.path.isfile(filename):
os.remove(filename)
@ -14661,7 +14743,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
break
def runDaemon(showNodeInfoAccounts: bool,
def runDaemon(city: str,
showNodeInfoAccounts: bool,
showNodeInfoVersion: bool,
brochMode: bool,
verifyAllSignatures: bool,
@ -14819,6 +14902,9 @@ def runDaemon(showNodeInfoAccounts: bool,
print('ERROR: no translations were loaded')
sys.exit()
# spoofed city for gps location misdirection
httpd.city = city
# For moderated newswire feeds this is the amount of time allowed
# for voting after the post arrives
httpd.votingTimeMins = votingTimeMins

View File

@ -458,6 +458,7 @@ def _desktopReplyToPost(session, postId: str,
isArticle = False
subject = None
commentsEnabled = True
city = 'London, England'
sayStr = 'Sending reply'
_sayCommand(sayStr, sayStr, screenreader, systemLanguage, espeak)
if sendPostViaServer(__version__,
@ -466,7 +467,7 @@ def _desktopReplyToPost(session, postId: str,
toNickname, toDomain, toPort, ccUrl,
httpPrefix, replyMessage, followersOnly,
commentsEnabled, attach, mediaType,
attachedImageDescription,
attachedImageDescription, city,
cachedWebfingers, personCache, isArticle,
debug, postId, postId, subject) == 0:
sayStr = 'Reply sent'
@ -514,6 +515,7 @@ def _desktopNewPost(session,
attach = None
mediaType = None
attachedImageDescription = None
city = 'London, England'
isArticle = False
subject = None
commentsEnabled = True
@ -526,7 +528,7 @@ def _desktopNewPost(session,
None, '#Public', port, ccUrl,
httpPrefix, newMessage, followersOnly,
commentsEnabled, attach, mediaType,
attachedImageDescription,
attachedImageDescription, city,
cachedWebfingers, personCache, isArticle,
debug, None, None, subject) == 0:
sayStr = 'Post sent'
@ -1158,6 +1160,7 @@ def _desktopNewDMbase(session, toHandle: str,
attach = None
mediaType = None
attachedImageDescription = None
city = 'London, England'
isArticle = False
subject = None
commentsEnabled = True
@ -1206,7 +1209,7 @@ def _desktopNewDMbase(session, toHandle: str,
toNickname, toDomain, toPort, ccUrl,
httpPrefix, newMessage, followersOnly,
commentsEnabled, attach, mediaType,
attachedImageDescription,
attachedImageDescription, city,
cachedWebfingers, personCache, isArticle,
debug, None, None, subject) == 0:
sayStr = 'Direct message sent'

View File

@ -762,5 +762,6 @@
"talkingemote2": "talkingEmote2",
"veryhappyemote": "veryHappyEmote",
"worriedemote": "worriedEmote",
"tor": "tor"
"tor": "tor",
"pine64": "pine64"
}

BIN
emoji/pine64.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -464,6 +464,9 @@ parser.add_argument('--attach', dest='attach', type=str,
default=None, help='File to attach to a post')
parser.add_argument('--imagedescription', dest='imageDescription', type=str,
default=None, help='Description of an attached image')
parser.add_argument('--city', dest='city', type=str,
default='London, England',
help='Spoofed city for image metadata misdirection')
parser.add_argument('--warning', '--warn', '--cwsubject', '--subject',
dest='subject', type=str, default=None,
help='Subject of content warning')
@ -1110,6 +1113,7 @@ if args.message:
followersOnly = args.followersonly
clientToServer = args.client
attachedImageDescription = args.imageDescription
city = 'London, England'
sendThreads = []
postLog = []
personCache = {}
@ -1130,7 +1134,7 @@ if args.message:
toNickname, toDomain, toPort, ccUrl,
httpPrefix, sendMessage, followersOnly,
args.commentsEnabled, attach, mediaType,
attachedImageDescription,
attachedImageDescription, city,
cachedWebfingers, personCache, isArticle,
args.debug, replyTo, replyTo, subject)
for i in range(10):
@ -1959,8 +1963,9 @@ if args.avatar:
if not args.nickname:
print('Specify a nickname with --nickname [name]')
sys.exit()
city = 'London, England'
if setProfileImage(baseDir, httpPrefix, args.nickname, domain,
port, args.avatar, 'avatar', '128x128'):
port, args.avatar, 'avatar', '128x128', city):
print('Avatar added for ' + args.nickname)
else:
print('Avatar was not added for ' + args.nickname)
@ -1973,8 +1978,10 @@ if args.backgroundImage:
if not args.nickname:
print('Specify a nickname with --nickname [name]')
sys.exit()
city = 'London, England'
if setProfileImage(baseDir, httpPrefix, args.nickname, domain,
port, args.backgroundImage, 'background', '256x256'):
port, args.backgroundImage, 'background',
'256x256', city):
print('Background image added for ' + args.nickname)
else:
print('Background image was not added for ' + args.nickname)
@ -2347,6 +2354,7 @@ if args.unfilterStr:
sys.exit()
if args.testdata:
city = 'London, England'
nickname = 'testuser567'
password = 'boringpassword'
print('Generating some test data for user: ' + nickname)
@ -2394,7 +2402,7 @@ if args.testdata:
"mechanical",
"City",
"2 months",
debug)
debug, city)
addShare(baseDir,
httpPrefix, nickname, domain, port,
"witch hat",
@ -2404,7 +2412,7 @@ if args.testdata:
"clothing",
"City",
"3 months",
debug)
debug, city)
deleteAllPosts(baseDir, nickname, domain, 'inbox')
deleteAllPosts(baseDir, nickname, domain, 'outbox')
@ -2416,6 +2424,7 @@ if args.testdata:
testAttachImageFilename = None
testMediaType = None
testImageDescription = None
testCity = 'London, England'
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"like this is totally just a #test man",
@ -2424,7 +2433,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Zoiks!!!",
testFollowersOnly,
@ -2432,7 +2441,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Hey scoob we need like a hundred more #milkshakes",
testFollowersOnly,
@ -2440,7 +2449,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Getting kinda spooky around here",
testFollowersOnly,
@ -2448,7 +2457,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription,
testMediaType, testImageDescription, testCity,
'someone')
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"And they would have gotten away with it too" +
@ -2458,7 +2467,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
'img/logo.png', 'image/png',
'Description of image')
'Description of image', testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"man these centralized sites are like the worst!",
testFollowersOnly,
@ -2466,7 +2475,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"another mystery solved #test",
testFollowersOnly,
@ -2474,7 +2483,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"let's go bowling",
testFollowersOnly,
@ -2482,7 +2491,7 @@ if args.testdata:
testC2S,
testCommentsEnabled,
testAttachImageFilename,
testMediaType, testImageDescription)
testMediaType, testImageDescription, testCity)
domainFull = domain + ':' + str(port)
clearFollows(baseDir, nickname, domain)
@ -2620,6 +2629,11 @@ showNodeInfoVersion = \
if showNodeInfoVersion is not None:
args.showNodeInfoVersion = bool(showNodeInfoVersion)
city = \
getConfigParam(baseDir, 'city')
if city is not None:
args.city = city
YTDomain = getConfigParam(baseDir, 'youtubedomain')
if YTDomain:
if '://' in YTDomain:
@ -2634,7 +2648,8 @@ if setTheme(baseDir, themeName, domain,
print('Theme set to ' + themeName)
if __name__ == "__main__":
runDaemon(args.showNodeInfoAccounts,
runDaemon(args.city,
args.showNodeInfoAccounts,
args.showNodeInfoVersion,
args.brochMode,
args.verifyAllSignatures,

View File

@ -2183,6 +2183,7 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str,
attachImageFilename = None
mediaType = None
imageDescription = ''
city = 'London, England'
inReplyTo = removeIdEnding(senderPostId)
inReplyToAtomUri = None
schedulePost = False
@ -2195,7 +2196,7 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str,
saveToFile, clientToServer,
commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
inReplyTo, inReplyToAtomUri,
subject, debug, schedulePost,
eventDate, eventTime, location)

4158
locations.txt 100644

File diff suppressed because it is too large Load Diff

209
media.py
View File

@ -8,6 +8,9 @@ __status__ = "Production"
import os
import datetime
import random
import math
from random import randint
from hashlib import sha1
from auth import createPassword
from utils import getFullDomain
@ -37,7 +40,7 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None:
replacementDomain)
def removeMetaData(imageFilename: str, outputFilename: str) -> None:
def _removeMetaData(imageFilename: str, outputFilename: str) -> None:
"""Attempts to do this with pure python didn't work well,
so better to use a dedicated tool if one is installed
"""
@ -53,6 +56,201 @@ def removeMetaData(imageFilename: str, outputFilename: str) -> None:
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
def _getCityPulse(currTimeOfDay, decoySeed: int) -> (float, float):
"""The data decoy
This simulates expected average patterns of movement in a city.
Jane or Joe average lives and works in the city, commuting in
and out of the central district for work. They have a unique
life pattern, which machine learning can latch onto.
This returns a polar coordinate:
Distance from the city centre is in the range 0.0 - 1.0
Angle is in radians
"""
randgen = random.Random(decoySeed)
variance = 3
busyStates = ("work", "shop", "play", "party")
dataDecoyState = "sleep"
dataDecoyIndex = 0
weekday = currTimeOfDay.weekday()
minHour = 7 + randint(0, variance)
maxHour = 17 + randint(0, variance)
if currTimeOfDay.hour > minHour:
if currTimeOfDay.hour <= maxHour:
if weekday < 5:
dataDecoyState = "work"
dataDecoyIndex = 1
elif weekday == 5:
dataDecoyState = "shop"
dataDecoyIndex = 2
else:
dataDecoyState = "play"
dataDecoyIndex = 3
else:
if weekday < 5:
dataDecoyState = "evening"
dataDecoyIndex = 4
else:
dataDecoyState = "party"
dataDecoyIndex = 5
angleRadians = \
(randgen.randint(0, 100000 - 5 + dataDecoyIndex) / 100000) * \
2 * math.pi
# some people are quite random, others have more predictable habits
decoyRandomness = randgen.randint(1, 3)
# occasionally throw in a wildcard to keep the machine learning guessing
if randint(0, 100) < decoyRandomness:
distanceFromCityCenter = (randint(0, 100000) / 100000)
angleRadians = (randint(0, 100000) / 100000) * 2 * math.pi
else:
# what consitutes the central district is fuzzy
centralDistrictFuzz = (randgen.randint(0, 100000) / 100000) * 0.1
busyRadius = 0.3 + centralDistrictFuzz
if dataDecoyState in busyStates:
# if we are busy then we're somewhere in the city center
distanceFromCityCenter = \
(randgen.randint(0, 100000) / 100000) * busyRadius
else:
# otherwise we're in the burbs
distanceFromCityCenter = busyRadius + \
((1.0 - busyRadius) * (randgen.randint(0, 100000) / 100000))
return distanceFromCityCenter, angleRadians
def spoofGeolocation(baseDir: str,
city: str, currTime, decoySeed: int,
citiesList: []) -> (float, float, str, str):
"""Given a city and the current time spoofs the location
for an image
returns latitude, longitude, N/S, E/W
"""
locationsFilename = baseDir + '/custom_locations.txt'
if not os.path.isfile(locationsFilename):
locationsFilename = baseDir + '/locations.txt'
cityRadius = 0.1
variance = 0.001
default_latitude = 51.8744
default_longitude = 0.368333
default_latdirection = 'N'
default_longdirection = 'W'
if citiesList:
cities = citiesList
else:
if not os.path.isfile(locationsFilename):
return (default_latitude, default_longitude,
default_latdirection, default_longdirection)
cities = []
with open(locationsFilename, "r") as f:
cities = f.readlines()
city = city.lower()
for cityName in cities:
if city in cityName.lower():
latitude = cityName.split(':')[1]
longitude = cityName.split(':')[2]
latdirection = 'N'
longdirection = 'E'
if 'S' in latitude:
latdirection = 'S'
latitude = latitude.replace('S', '')
if 'W' in longitude:
longdirection = 'W'
longitude = longitude.replace('W', '')
latitude = float(latitude)
longitude = float(longitude)
# get the time of day at the city
approxTimeZone = int(longitude / 15.0)
if longdirection == 'E':
approxTimeZone = -approxTimeZone
currTimeAdjusted = currTime - \
datetime.timedelta(hours=approxTimeZone)
# patterns of activity change in the city over time
(distanceFromCityCenter, angleRadians) = \
_getCityPulse(currTimeAdjusted, decoySeed)
# Get the position within the city, with some randomness added
latitude += \
distanceFromCityCenter * cityRadius * math.cos(angleRadians)
# add a small amount of variance around the location
fraction = randint(0, 100000) / 100000
latitude += (fraction * fraction * variance) - (variance / 2.0)
longitude += \
distanceFromCityCenter * cityRadius * math.sin(angleRadians)
# add a small amount of variance around the location
fraction = randint(0, 100000) / 100000
longitude += (fraction * fraction * variance) - (variance / 2.0)
# gps locations aren't transcendental, so round to a fixed
# number of decimal places
latitude = int(latitude * 10000) / 10000.0
longitude = int(longitude * 10000) / 10000.0
return latitude, longitude, latdirection, longdirection
return (default_latitude, default_longitude,
default_latdirection, default_longdirection)
def _spoofMetaData(baseDir: str, nickname: str, domain: str,
outputFilename: str, spoofCity: str) -> None:
"""Spoof image metadata using a decoy model for a given city
"""
if not os.path.isfile(outputFilename):
print('ERROR: unable to spoof metadata within ' + outputFilename)
return
# get the random seed used to generate a unique pattern for this account
decoySeedFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/decoyseed'
decoySeed = 63725
if os.path.isfile(decoySeedFilename):
with open(decoySeedFilename, 'r') as fp:
decoySeed = int(fp.read())
else:
decoySeed = randint(10000, 10000000000000000)
try:
with open(decoySeedFilename, 'w+') as fp:
fp.write(str(decoySeed))
except BaseException:
pass
if os.path.isfile('/usr/bin/exiftool'):
print('Spoofing metadata in ' + outputFilename + ' using exiftool')
currTimeAdjusted = \
datetime.datetime.utcnow() - \
datetime.timedelta(minutes=randint(2, 120))
published = currTimeAdjusted.strftime("%Y:%m:%d %H:%M:%S+00:00")
(latitude, longitude, latitudeRef, longitudeRef) = \
spoofGeolocation(baseDir, spoofCity, currTimeAdjusted,
decoySeed, None)
os.system('exiftool -artist="' + nickname + '" ' +
'-DateTimeOriginal="' + published + '" ' +
'-FileModifyDate="' + published + '" ' +
'-CreateDate="' + published + '" ' +
'-GPSLongitudeRef=' + longitudeRef + ' ' +
'-GPSAltitude=0 ' +
'-GPSLongitude=' + str(longitude) + ' ' +
'-GPSLatitudeRef=' + latitudeRef + ' ' +
'-GPSLatitude=' + str(latitude) + ' ' +
'-Comment="" ' +
outputFilename) # nosec
else:
print('ERROR: exiftool is not installed')
return
def processMetaData(baseDir: str, nickname: str, domain: str,
imageFilename: str, outputFilename: str,
city: str) -> None:
"""Handles image metadata. This tries to spoof the metadata
if possible, but otherwise just removes it
"""
# first remove the metadata
_removeMetaData(imageFilename, outputFilename)
# now add some spoofed data to misdirect surveillance capitalists
_spoofMetaData(baseDir, nickname, domain, outputFilename, city)
def _isMedia(imageFilename: str) -> bool:
"""Is the given file a media file?
"""
@ -131,9 +329,11 @@ def _updateEtag(mediaFilename: str) -> None:
pass
def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
def attachMedia(baseDir: str, httpPrefix: str,
nickname: str, domain: str, port: int,
postJson: {}, imageFilename: str,
mediaType: str, description: str) -> {}:
mediaType: str, description: str,
city: str) -> {}:
"""Attaches media to a json object post
The description can be None
"""
@ -179,7 +379,8 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
if baseDir:
if mediaType.startswith('image/'):
removeMetaData(imageFilename, mediaFilename)
processMetaData(baseDir, nickname, domain,
imageFilename, mediaFilename, city)
else:
copyfile(imageFilename, mediaFilename)
_updateEtag(mediaFilename)

View File

@ -527,11 +527,17 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
# NOTE: the id when the post is created will not be
# consistent (it's based on the current time, not the
# published time), so we change that later
saveToFile = False
attachImageFilename = None
mediaType = None
imageDescription = None
city = 'London, England'
blog = createNewsPost(baseDir,
domain, port, httpPrefix,
rssDescription,
followersOnly, False,
None, None, None,
followersOnly, saveToFile,
attachImageFilename, mediaType,
imageDescription, city,
rssTitle)
if not blog:
continue

View File

@ -176,7 +176,8 @@ def postMessageToOutbox(session, translate: {},
proxyType: str, version: str, debug: bool,
YTReplacementDomain: str,
showPublishedDateOnly: bool,
allowLocalNetworkAccess: bool) -> bool:
allowLocalNetworkAccess: bool,
city: str) -> bool:
"""post is received by the outbox
Client to server message post
https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery
@ -545,7 +546,7 @@ def postMessageToOutbox(session, translate: {},
print('DEBUG: handle share uploads')
outboxShareUpload(baseDir, httpPrefix,
postToNickname, domain,
port, messageJson, debug)
port, messageJson, debug, city)
if debug:
print('DEBUG: handle undo share uploads')

View File

@ -34,7 +34,7 @@ from posts import createModeration
from auth import storeBasicCredentials
from auth import removePassword
from roles import setRole
from media import removeMetaData
from media import processMetaData
from utils import getStatusNumber
from utils import getFullDomain
from utils import validNickname
@ -74,7 +74,7 @@ def generateRSAKey() -> (str, str):
def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
port: int, imageFilename: str, imageType: str,
resolution: str) -> bool:
resolution: str, city: str) -> bool:
"""Saves the given image file as an avatar or background
image for the given person
"""
@ -135,7 +135,8 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
'/usr/bin/convert ' + imageFilename + ' -size ' + \
resolution + ' -quality 50 ' + profileFilename
subprocess.call(cmd, shell=True)
removeMetaData(profileFilename, profileFilename)
processMetaData(baseDir, nickname, domain,
profileFilename, profileFilename, city)
return True
return False

View File

@ -867,7 +867,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str,
mediaType: str, imageDescription: str,
mediaType: str, imageDescription: str, city: str,
isModerationReport: bool,
isArticle: bool,
inReplyTo=None,
@ -1059,6 +1059,10 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
idStr = \
httpPrefix + '://' + domain + '/users/' + nickname + \
'/statuses/' + statusNumber + '/replies'
newPostUrl = \
httpPrefix + '://' + domain + '/@' + nickname + '/'+statusNumber
newPostAttributedTo = \
httpPrefix + '://' + domain + '/users/' + nickname
newPost = {
'@context': postContext,
'id': newPostId + '/activity',
@ -1073,8 +1077,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'summary': summary,
'inReplyTo': inReplyTo,
'published': published,
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber,
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname,
'url': newPostUrl,
'attributedTo': newPostAttributedTo,
'to': toRecipients,
'cc': toCC,
'sensitive': sensitive,
@ -1101,9 +1105,9 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
}
if attachImageFilename:
newPost['object'] = \
attachMedia(baseDir, httpPrefix, domain, port,
attachMedia(baseDir, httpPrefix, nickname, domain, port,
newPost['object'], attachImageFilename,
mediaType, imageDescription)
mediaType, imageDescription, city)
_appendEventFields(newPost['object'], eventUUID, eventStatus,
anonymousParticipationEnabled,
repliesModerationOption,
@ -1115,6 +1119,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
idStr = \
httpPrefix + '://' + domain + '/users/' + nickname + \
'/statuses/' + statusNumber + '/replies'
newPostUrl = \
httpPrefix + '://' + domain + '/@' + nickname+'/' + statusNumber
newPost = {
"@context": postContext,
'id': newPostId,
@ -1122,8 +1128,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'summary': summary,
'inReplyTo': inReplyTo,
'published': published,
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber,
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname,
'url': newPostUrl,
'attributedTo': httpPrefix + '://' + domain + '/users/' + nickname,
'to': toRecipients,
'cc': toCC,
'sensitive': sensitive,
@ -1149,9 +1155,9 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
}
if attachImageFilename:
newPost = \
attachMedia(baseDir, httpPrefix, domain, port,
attachMedia(baseDir, httpPrefix, nickname, domain, port,
newPost, attachImageFilename,
mediaType, imageDescription)
mediaType, imageDescription, city)
_appendEventFields(newPost, eventUUID, eventStatus,
anonymousParticipationEnabled,
repliesModerationOption,
@ -1391,7 +1397,7 @@ def createPublicPost(baseDir: str,
content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
inReplyTo=None, inReplyToAtomUri=None, subject=None,
schedulePost=False,
eventDate=None, eventTime=None, location=None,
@ -1417,7 +1423,7 @@ def createPublicPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
isModerationReport, isArticle,
inReplyTo, inReplyToAtomUri, subject,
schedulePost, eventDate, eventTime, location,
@ -1464,7 +1470,7 @@ def createBlogPost(baseDir: str,
content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
inReplyTo=None, inReplyToAtomUri=None, subject=None,
schedulePost=False,
eventDate=None, eventTime=None, location=None) -> {}:
@ -1474,7 +1480,7 @@ def createBlogPost(baseDir: str,
content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
inReplyTo, inReplyToAtomUri, subject,
schedulePost,
eventDate, eventTime, location, True)
@ -1489,7 +1495,7 @@ def createNewsPost(baseDir: str,
domain: str, port: int, httpPrefix: str,
content: str, followersOnly: bool, saveToFile: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
subject: str) -> {}:
clientToServer = False
inReplyTo = None
@ -1504,7 +1510,7 @@ def createNewsPost(baseDir: str,
content, followersOnly, saveToFile,
clientToServer, False,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
inReplyTo, inReplyToAtomUri, subject,
schedulePost,
eventDate, eventTime, location, True)
@ -1518,7 +1524,7 @@ def createQuestionPost(baseDir: str,
followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
subject: str, durationDays: int) -> {}:
"""Question post with multiple choice options
"""
@ -1531,7 +1537,7 @@ def createQuestionPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
False, False, None, None, subject,
False, None, None, None, None, None,
None, None, None,
@ -1562,7 +1568,7 @@ def createUnlistedPost(baseDir: str,
content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
inReplyTo=None, inReplyToAtomUri=None, subject=None,
schedulePost=False,
eventDate=None, eventTime=None, location=None) -> {}:
@ -1576,8 +1582,9 @@ def createUnlistedPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
False, False, inReplyTo, inReplyToAtomUri, subject,
imageDescription, city,
False, False,
inReplyTo, inReplyToAtomUri, subject,
schedulePost, eventDate, eventTime, location,
None, None, None, None, None,
None, None, None, None, None)
@ -1590,7 +1597,7 @@ def createFollowersOnlyPost(baseDir: str,
saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
inReplyTo=None, inReplyToAtomUri=None,
subject=None, schedulePost=False,
eventDate=None, eventTime=None,
@ -1605,8 +1612,9 @@ def createFollowersOnlyPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
False, False, inReplyTo, inReplyToAtomUri, subject,
imageDescription, city,
False, False,
inReplyTo, inReplyToAtomUri, subject,
schedulePost, eventDate, eventTime, location,
None, None, None, None, None,
None, None, None, None, None)
@ -1619,7 +1627,7 @@ def createEventPost(baseDir: str,
saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
subject=None, schedulePost=False,
eventDate=None, eventTime=None,
location=None, category=None, joinMode=None,
@ -1652,7 +1660,7 @@ def createEventPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
False, False, None, None, subject,
schedulePost, eventDate, eventTime, location,
eventUUID, category, joinMode,
@ -1704,7 +1712,7 @@ def createDirectMessagePost(baseDir: str,
saveToFile: bool, clientToServer: bool,
commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
inReplyTo=None, inReplyToAtomUri=None,
subject=None, debug=False,
schedulePost=False,
@ -1727,8 +1735,9 @@ def createDirectMessagePost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
False, False, inReplyTo, inReplyToAtomUri, subject,
imageDescription, city,
False, False,
inReplyTo, inReplyToAtomUri, subject,
schedulePost, eventDate, eventTime, location,
None, None, None, None, None,
None, None, None, None, None)
@ -1748,7 +1757,7 @@ def createReportPost(baseDir: str,
content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
debug: bool, subject=None) -> {}:
"""Send a report to moderators
"""
@ -1817,7 +1826,7 @@ def createReportPost(baseDir: str,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
True, False, None, None, subject,
False, None, None, None, None, None,
None, None, None,
@ -1902,7 +1911,7 @@ def sendPost(projectVersion: str,
saveToFile: bool, clientToServer: bool,
commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
federationList: [], sendThreads: [], postLog: [],
cachedWebfingers: {}, personCache: {},
isArticle: bool,
@ -1961,7 +1970,7 @@ def sendPost(projectVersion: str,
followersOnly, saveToFile, clientToServer,
commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
False, isArticle, inReplyTo,
inReplyToAtomUri, subject,
False, None, None, None, None, None,
@ -2023,7 +2032,7 @@ def sendPostViaServer(projectVersion: str,
httpPrefix: str, content: str, followersOnly: bool,
commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str,
imageDescription: str, city: str,
cachedWebfingers: {}, personCache: {},
isArticle: bool, debug=False, inReplyTo=None,
inReplyToAtomUri=None, subject=None) -> int:
@ -2100,7 +2109,7 @@ def sendPostViaServer(projectVersion: str,
followersOnly, saveToFile, clientToServer,
commentsEnabled,
attachImageFilename, mediaType,
imageDescription,
imageDescription, city,
False, isArticle, inReplyTo,
inReplyToAtomUri, subject,
False, None, None, None, None, None,

View File

@ -108,7 +108,8 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd,
httpd.debug,
httpd.YTReplacementDomain,
httpd.showPublishedDateOnly,
httpd.allowLocalNetworkAccess):
httpd.allowLocalNetworkAccess,
httpd.city):
indexLines.remove(line)
os.remove(postFilename)
continue

View File

@ -18,7 +18,7 @@ from utils import validNickname
from utils import loadJson
from utils import saveJson
from utils import getImageExtensions
from media import removeMetaData
from media import processMetaData
def getValidSharedItemID(displayName: str) -> str:
@ -73,7 +73,7 @@ def addShare(baseDir: str,
httpPrefix: str, nickname: str, domain: str, port: int,
displayName: str, summary: str, imageFilename: str,
itemType: str, itemCategory: str, location: str,
duration: str, debug: bool) -> None:
duration: str, debug: bool, city: str) -> None:
"""Adds a new share
"""
sharesFilename = baseDir + '/accounts/' + \
@ -129,7 +129,9 @@ def addShare(baseDir: str,
formats = getImageExtensions()
for ext in formats:
if imageFilename.endswith('.' + ext):
removeMetaData(imageFilename, itemIDfile + '.' + ext)
processMetaData(baseDir, nickname, domain,
imageFilename, itemIDfile + '.' + ext,
city)
if moveImage:
os.remove(imageFilename)
imageUrl = \
@ -512,7 +514,7 @@ def sendUndoShareViaServer(baseDir: str, session,
def outboxShareUpload(baseDir: str, httpPrefix: str,
nickname: str, domain: str, port: int,
messageJson: {}, debug: bool) -> None:
messageJson: {}, debug: bool, city: str) -> None:
""" When a shared item is received by the outbox from c2s
"""
if not messageJson.get('type'):
@ -564,7 +566,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str,
messageJson['object']['itemCategory'],
messageJson['object']['location'],
messageJson['object']['duration'],
debug)
debug, city)
if debug:
print('DEBUG: shared item received via c2s')

128
tests.py
View File

@ -10,6 +10,8 @@ import time
import os
import shutil
import json
import datetime
from random import randint
from time import gmtime, strftime
from pprint import pprint
from httpsig import signPostHeaders
@ -75,6 +77,7 @@ from like import likePost
from like import sendLikeViaServer
from announce import announcePublic
from announce import sendAnnounceViaServer
from media import spoofGeolocation
from media import getMediaPath
from media import getAttachmentMediaType
from delete import sendDeleteViaServer
@ -463,6 +466,7 @@ def createServerAlice(path: str, domain: str, port: int,
testAttachImageFilename = None
testMediaType = None
testImageDescription = None
testCity = 'London, England'
createPublicPost(path, nickname, domain, port, httpPrefix,
"No wise fish would go anywhere without a porpoise",
testFollowersOnly,
@ -471,7 +475,7 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
createPublicPost(path, nickname, domain, port, httpPrefix,
"Curiouser and curiouser!",
testFollowersOnly,
@ -480,7 +484,7 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
createPublicPost(path, nickname, domain, port, httpPrefix,
"In the gardens of memory, in the palace " +
"of dreams, that is where you and I shall meet",
@ -490,7 +494,7 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
global testServerAliceRunning
testServerAliceRunning = True
maxMentions = 10
@ -506,8 +510,10 @@ def createServerAlice(path: str, domain: str, port: int,
brochMode = False
showNodeInfoAccounts = True
showNodeInfoVersion = True
city = 'London, England'
print('Server running: Alice')
runDaemon(showNodeInfoAccounts,
runDaemon(city,
showNodeInfoAccounts,
showNodeInfoVersion,
brochMode,
verifyAllSignatures,
@ -564,6 +570,7 @@ def createServerBob(path: str, domain: str, port: int,
testAttachImageFilename = None
testImageDescription = None
testMediaType = None
testCity = 'London, England'
createPublicPost(path, nickname, domain, port, httpPrefix,
"It's your life, live it your way.",
testFollowersOnly,
@ -572,7 +579,7 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
createPublicPost(path, nickname, domain, port, httpPrefix,
"One of the things I've realised is that " +
"I am very simple",
@ -582,7 +589,7 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
createPublicPost(path, nickname, domain, port, httpPrefix,
"Quantum physics is a bit of a passion of mine",
testFollowersOnly,
@ -591,7 +598,7 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled,
testAttachImageFilename,
testMediaType,
testImageDescription)
testImageDescription, testCity)
global testServerBobRunning
testServerBobRunning = True
maxMentions = 10
@ -607,8 +614,10 @@ def createServerBob(path: str, domain: str, port: int,
brochMode = False
showNodeInfoAccounts = True
showNodeInfoVersion = True
city = 'London, England'
print('Server running: Bob')
runDaemon(showNodeInfoAccounts,
runDaemon(city,
showNodeInfoAccounts,
showNodeInfoVersion,
brochMode,
verifyAllSignatures,
@ -662,8 +671,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
brochMode = False
showNodeInfoAccounts = True
showNodeInfoVersion = True
city = 'London, England'
print('Server running: Eve')
runDaemon(showNodeInfoAccounts,
runDaemon(city,
showNodeInfoAccounts,
showNodeInfoVersion,
brochMode,
verifyAllSignatures,
@ -768,6 +779,7 @@ def testPostMessageBetweenServers():
mediaType = getAttachmentMediaType(attachedImageFilename)
attachedImageDescription = 'Logo'
isArticle = False
city = 'London, England'
# nothing in Alice's outbox
outboxPath = aliceDir + '/accounts/alice@' + aliceDomain + '/outbox'
assert len([name for name in os.listdir(outboxPath)
@ -782,7 +794,7 @@ def testPostMessageBetweenServers():
followersOnly,
saveToFile, clientToServer, True,
attachedImageFilename, mediaType,
attachedImageDescription, federationList,
attachedImageDescription, city, federationList,
aliceSendThreads, alicePostLog, aliceCachedWebfingers,
alicePersonCache, isArticle, inReplyTo,
inReplyToAtomUri, subject)
@ -1085,13 +1097,14 @@ def testFollowBetweenServers():
aliceCachedWebfingers = {}
alicePostLog = []
isArticle = False
city = 'London, England'
sendResult = \
sendPost(__version__,
sessionAlice, aliceDir, 'alice', aliceDomain, alicePort,
'bob', bobDomain, bobPort, ccUrl,
httpPrefix, 'Alice message', followersOnly, saveToFile,
clientToServer, True,
None, None, None, federationList,
None, None, None, city, federationList,
aliceSendThreads, alicePostLog, aliceCachedWebfingers,
alicePersonCache, isArticle, inReplyTo,
inReplyToAtomUri, subject)
@ -1390,7 +1403,7 @@ def testCreatePerson():
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"G'day world!", False, True, clientToServer,
True, None, None, None, None,
'Not suitable for Vogons')
'Not suitable for Vogons', 'London, England')
os.chdir(currDir)
shutil.rmtree(baseDir)
@ -1593,6 +1606,7 @@ def testClientToServer():
attachedImageFilename = baseDir + '/img/logo.png'
mediaType = getAttachmentMediaType(attachedImageFilename)
attachedImageDescription = 'Logo'
city = 'London, England'
isArticle = False
cachedWebfingers = {}
personCache = {}
@ -1611,7 +1625,7 @@ def testClientToServer():
httpPrefix, 'Sent from my ActivityPub client',
followersOnly, True,
attachedImageFilename, mediaType,
attachedImageDescription,
attachedImageDescription, city,
cachedWebfingers, personCache, isArticle,
True, None, None, None)
print('sendResult: ' + str(sendResult))
@ -2822,11 +2836,21 @@ def testReplyToPublicPost() -> None:
port = 443
httpPrefix = 'https'
postId = httpPrefix + '://rat.site/users/ninjarodent/statuses/63746173435'
content = "@ninjarodent@rat.site This is a test."
followersOnly = False
saveToFile = False
clientToServer = False
commentsEnabled = True
attachImageFilename = None
mediaType = None
imageDescription = 'Some description'
city = 'London, England'
reply = \
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"@ninjarodent@rat.site This is a test.",
False, False, False, True,
None, None, False, postId)
content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription, city, postId)
# print(str(reply))
assert reply['object']['content'] == \
'<p><span class=\"h-card\">' + \
@ -3244,11 +3268,20 @@ def testLinksWithinPost() -> None:
httpPrefix = 'https'
content = 'This is a test post with links.\n\n' + \
'ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/v4/\n\nhttps://freedombone.net'
followersOnly = False
saveToFile = False
clientToServer = False
commentsEnabled = True
mediaType = None
imageDescription = None
city = 'London, England'
postJsonObject = \
createPublicPost(baseDir, nickname, domain, port, httpPrefix,
content,
False, False, False, True,
None, None, False, None)
content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
mediaType, imageDescription, city,
False, None)
assert postJsonObject['object']['content'] == \
'<p>This is a test post with links.<br><br>' + \
'<a href="ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/v4/" ' + \
@ -3636,9 +3669,66 @@ def testRemovePostInteractions() -> None:
assert not removePostInteractions(postJsonObject, False)
def testSpoofGeolocation() -> None:
print('testSpoofGeolocation')
citiesList = [
'NEW YORK, USA:40.6397:W73.7789',
'LOS ANGELES, USA:33.9425:W118.408',
'HOUSTON, USA:29.9803:W95.3397',
'MANCHESTER, ENGLAND:53.4794892:W2.2451148'
]
currTime = datetime.datetime.utcnow()
decoySeed = 7634681
cityRadius = 0.1
coords = spoofGeolocation('', 'los angeles', currTime,
decoySeed, citiesList)
assert coords[0] >= 33.9425 - cityRadius
assert coords[0] <= 33.9425 + cityRadius
assert coords[1] >= 118.408 - cityRadius
assert coords[1] <= 118.408 + cityRadius
assert coords[2] == 'N'
assert coords[3] == 'W'
coords = spoofGeolocation('', 'unknown', currTime,
decoySeed, citiesList)
assert coords[0] >= 51.8744 - cityRadius
assert coords[0] <= 51.8744 + cityRadius
assert coords[1] >= 0.368333 - cityRadius
assert coords[1] <= 0.368333 + cityRadius
assert coords[2] == 'N'
assert coords[3] == 'W'
kmlStr = '<?xml version="1.0" encoding="UTF-8"?>\n'
kmlStr += '<kml xmlns="http://www.opengis.net/kml/2.2">\n'
kmlStr += '<Document>\n'
for i in range(1000):
dayNumber = randint(10, 30)
hour = randint(1, 23)
hourStr = str(hour)
if hour < 10:
hourStr = '0' + hourStr
currTime = datetime.datetime.strptime("2021-05-" + str(dayNumber) +
" " + hourStr + ":14",
"%Y-%m-%d %H:%M")
coords = spoofGeolocation('', 'manchester, england', currTime,
decoySeed, citiesList)
kmlStr += '<Placemark id="' + str(i) + '">\n'
kmlStr += ' <name>' + str(i) + '</name>\n'
kmlStr += ' <Point>\n'
kmlStr += ' <coordinates>' + str(-coords[1]) + ',' + \
str(coords[0]) + ',0</coordinates>\n'
kmlStr += ' </Point>\n'
kmlStr += '</Placemark>\n'
kmlStr += '</Document>\n'
kmlStr += '</kml>'
kmlFile = open('unittest_decoy.kml', 'w+')
if kmlFile:
kmlFile.write(kmlStr)
kmlFile.close()
def runAllTests():
print('Running tests...')
testFunctions()
testSpoofGeolocation()
testRemovePostInteractions()
testExtractPGPPublicKey()
testEmojiImages()

View File

@ -440,5 +440,6 @@
"These access keys may be used": "قد يتم استخدام مفاتيح الوصول هذه، عادة مع مفتاح ALT + SHIFT + مفتاح ALT +",
"Show numbers of accounts within instance metadata": "إظهار عدد الحسابات داخل البيانات الوصفية للمثيلات",
"Show version number within instance metadata": "إظهار رقم الإصدار داخل البيانات الوصفية للمثيل",
"Joined": "تاريخ الانضمام"
"Joined": "تاريخ الانضمام",
"City for spoofed GPS image metadata": "مدينة للبيانات الوصفية لصور GPS المخادعة"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Es poden utilitzar aquestes tecles d'accés, típicament amb Alt + Maj + tecla o Alt + clau",
"Show numbers of accounts within instance metadata": "Mostra el nombre de comptes a les metadades de la instància",
"Show version number within instance metadata": "Mostra el número de versió a les metadades de la instància",
"Joined": "Data d'unió"
"Joined": "Data d'unió",
"City for spoofed GPS image metadata": "Ciutat per a metadades d'imatges GPS falsificades"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Gellir defnyddio'r allweddi mynediad hyn, fel arfer gyda ALT + Shift + Allwedd Allwedd neu ALT +",
"Show numbers of accounts within instance metadata": "Dangos nifer y cyfrifon o fewn metadata",
"Show version number within instance metadata": "Dangos rhif y fersiwn o fewn metadata",
"Joined": "Dyddiad ymuno"
"Joined": "Dyddiad ymuno",
"City for spoofed GPS image metadata": "Dinas ar gyfer metadata delwedd GPS spoofed"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Diese Zugriffstasten können verwendet werden, typischerweise mit ALT + SHIFT + -Taste oder ALT + -Taste",
"Show numbers of accounts within instance metadata": "Anzahl der Konten in Instanzmetadaten anzeigen",
"Show version number within instance metadata": "Versionsnummer in Instanzmetadaten anzeigen",
"Joined": "Verbundenes Datum"
"Joined": "Verbundenes Datum",
"City for spoofed GPS image metadata": "Stadt für gefälschte GPS-Bildmetadaten"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "These access keys may be used, typically with ALT + SHIFT + key or ALT + key",
"Show numbers of accounts within instance metadata": "Show numbers of accounts within instance metadata",
"Show version number within instance metadata": "Show version number within instance metadata",
"Joined": "Joined"
"Joined": "Joined",
"City for spoofed GPS image metadata": "City for spoofed GPS image metadata"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Se pueden usar estas teclas de acceso, típicamente con teclas ALT + MAYÚS + teclas o ALT +",
"Show numbers of accounts within instance metadata": "Muestra el número de cuentas dentro de los metadatos de la instancia.",
"Show version number within instance metadata": "Mostrar el número de versión dentro de los metadatos de la instancia",
"Joined": "Fecha unida"
"Joined": "Fecha unida",
"City for spoofed GPS image metadata": "Ciudad para metadatos de imagen GPS falsificados"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Ces touches d'accès peuvent être utilisées typiquement avec une touche Alt + Maj + ou Alt +",
"Show numbers of accounts within instance metadata": "Afficher le nombre de comptes dans les métadonnées de l'instance",
"Show version number within instance metadata": "Afficher le numéro de version dans les métadonnées de l'instance",
"Joined": "Joint"
"Joined": "Joint",
"City for spoofed GPS image metadata": "Ville pour les métadonnées d'image GPS falsifiées"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Is féidir na heochracha rochtana seo a úsáid, de ghnáth le Alt + Shift + Eochair nó Alt + Eochair",
"Show numbers of accounts within instance metadata": "Taispeáin líon na gcuntas laistigh de mheiteashonraí",
"Show version number within instance metadata": "Taispeáin uimhir an leagain laistigh de mheiteashonraí",
"Joined": "Dáta comhcheangailte"
"Joined": "Dáta comhcheangailte",
"City for spoofed GPS image metadata": "Cathair le haghaidh meiteashonraí íomhá GPS spoofed"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "इन एक्सेस कुंजियों का उपयोग किया जा सकता है, आमतौर पर Alt + Shift + कुंजी या Alt + कुंजी के साथ",
"Show numbers of accounts within instance metadata": "उदाहरण मेटाडेटा के भीतर खातों की संख्या दिखाएं",
"Show version number within instance metadata": "उदाहरण मेटाडेटा के भीतर संस्करण संख्या दिखाएं",
"Joined": "दिनांक"
"Joined": "दिनांक",
"City for spoofed GPS image metadata": "स्पूफ जीपीएस जीपीएस मेटाडेटा के लिए शहर"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Questi tasti di accesso possono essere utilizzati, in genere con tasto ALT + MAIUSC + o ALT + Key",
"Show numbers of accounts within instance metadata": "Mostra il numero di account all'interno dei metadati dell'istanza",
"Show version number within instance metadata": "Mostra il numero di versione nei metadati dell'istanza",
"Joined": "Unito"
"Joined": "Unito",
"City for spoofed GPS image metadata": "Città per metadati di immagini GPS falsificate"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "これらのアクセスキーは、通常はAlt + Shift +キーまたはAlt +キーを使用して使用できます。",
"Show numbers of accounts within instance metadata": "インスタンスメタデータ内のアカウント数を表示する",
"Show version number within instance metadata": "インスタンスメタデータ内にバージョン番号を表示する",
"Joined": "参加日"
"Joined": "参加日",
"City for spoofed GPS image metadata": "なりすましGPS画像メタデータの都市"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Dibe ku ev keysên gihîştinê bikar bînin, bi gelemperî bi alt + shift + key an alt + key",
"Show numbers of accounts within instance metadata": "Di nav metadata mînakê de hejmarên hesaban nîşan bidin",
"Show version number within instance metadata": "Di nav metadata mînakê de nimreya guhertoyê nîşan bide",
"Joined": "Beşdarbûna Dîrokê"
"Joined": "Beşdarbûna Dîrokê",
"City for spoofed GPS image metadata": "Bajar ji bo metadata wêneya GPS ya xapînok"
}

View File

@ -436,5 +436,6 @@
"These access keys may be used": "These access keys may be used, typically with ALT + SHIFT + key or ALT + key",
"Show numbers of accounts within instance metadata": "Show numbers of accounts within instance metadata",
"Show version number within instance metadata": "Show version number within instance metadata",
"Joined": "Joined"
"Joined": "Joined",
"City for spoofed GPS image metadata": "City for spoofed GPS image metadata"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Essas teclas de acesso podem ser usadas, normalmente com tecla Alt + Shift + Key ou Alt +",
"Show numbers of accounts within instance metadata": "Mostra o número de contas nos metadados da instância",
"Show version number within instance metadata": "Mostrar o número da versão nos metadados da instância",
"Joined": "Data juntada"
"Joined": "Data juntada",
"City for spoofed GPS image metadata": "Cidade para metadados de imagem GPS falsificados"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "Эти ключевые ключи доступа могут быть использованы, обычно с ALT + Shift + Key или Alt + Key",
"Show numbers of accounts within instance metadata": "Показать количество учетных записей в метаданных экземпляра",
"Show version number within instance metadata": "Показать номер версии в метаданных экземпляра",
"Joined": "Присоединенная дата"
"Joined": "Присоединенная дата",
"City for spoofed GPS image metadata": "Город для поддельных метаданных изображения GPS"
}

View File

@ -440,5 +440,6 @@
"These access keys may be used": "可以使用这些访问密钥通常使用Alt + Shift +键或ALT +键",
"Show numbers of accounts within instance metadata": "显示实例元数据中的帐户数",
"Show version number within instance metadata": "在实例元数据中显示版本号",
"Joined": "加入日期"
"Joined": "加入日期",
"City for spoofed GPS image metadata": "欺骗性GPS影像元数据的城市"
}

View File

@ -1068,7 +1068,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
domain: str, port: int, httpPrefix: str,
defaultTimeline: str, theme: str,
peertubeInstances: [],
textModeBanner: str) -> str:
textModeBanner: str, city: str) -> str:
"""Shows the edit profile screen
"""
imageFormats = getImageFormats()
@ -1770,6 +1770,41 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
editProfileForm += '<details><summary class="cw">' + \
translate['Filtering and Blocking'] + '</summary>\n'
editProfileForm += ' <div class="container">\n'
editProfileForm += \
'<label class="labels">' + \
translate['City for spoofed GPS image metadata'] + \
'</label><br>\n'
cityFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/city.txt'
if os.path.isfile(cityFilename):
with open(cityFilename, 'r') as fp:
city = fp.read().replace('\n', '')
locationsFilename = baseDir + '/custom_locations.txt'
if not os.path.isfile(locationsFilename):
locationsFilename = baseDir + '/locations.txt'
cities = []
with open(locationsFilename, "r") as f:
cities = f.readlines()
cities.sort()
editProfileForm += ' <select id="cityDropdown" ' + \
'name="cityDropdown" class="theme">\n'
city = city.lower()
for cityName in cities:
if ':' not in cityName:
continue
citySelected = ''
cityName = cityName.split(':')[0]
cityName = cityName.lower()
if city in cityName:
citySelected = ' selected'
editProfileForm += \
' <option value="' + cityName + \
'"' + citySelected.title() + '>' + \
cityName + '</option>\n'
editProfileForm += ' </select><br>\n'
editProfileForm += \
' <b><label class="labels">' + \
translate['Filtered words'] + '</label></b>\n'

View File

@ -543,6 +543,8 @@ def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {},
return None
# get from locally stored image
if not personJson.get('id'):
return None
actorStr = personJson['id'].replace('/', '-')
avatarImagePath = baseDir + '/cache/avatars/' + actorStr