mirror of https://gitlab.com/bashrc2/epicyon
Add city model
parent
45ff85c6de
commit
0493405f26
|
|
@ -4098,7 +4098,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
city = self._getSpoofedCity(baseDir, nickname, domain)
|
city = self._getSpoofedCity(baseDir, nickname, domain)
|
||||||
|
|
||||||
processMetaData(baseDir, nickname,
|
processMetaData(baseDir, nickname, domain,
|
||||||
filename, postImageFilename, city)
|
filename, postImageFilename, city)
|
||||||
if os.path.isfile(postImageFilename):
|
if os.path.isfile(postImageFilename):
|
||||||
print('profile update POST ' + mType +
|
print('profile update POST ' + mType +
|
||||||
|
|
@ -13094,8 +13094,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
city = self._getSpoofedCity(self.server.baseDir,
|
city = self._getSpoofedCity(self.server.baseDir,
|
||||||
nickname, self.server.domain)
|
nickname, self.server.domain)
|
||||||
processMetaData(self.server.baseDir,
|
processMetaData(self.server.baseDir,
|
||||||
nickname, filename, postImageFilename,
|
nickname, self.server.domain,
|
||||||
city)
|
filename, postImageFilename, city)
|
||||||
if os.path.isfile(postImageFilename):
|
if os.path.isfile(postImageFilename):
|
||||||
print('POST media saved to ' + postImageFilename)
|
print('POST media saved to ' + postImageFilename)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
108
media.py
108
media.py
|
|
@ -8,6 +8,8 @@ __status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
import random
|
||||||
|
import math
|
||||||
from random import randint
|
from random import randint
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from auth import createPassword
|
from auth import createPassword
|
||||||
|
|
@ -54,8 +56,59 @@ def _removeMetaData(imageFilename: str, outputFilename: str) -> None:
|
||||||
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
|
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
|
||||||
|
|
||||||
|
|
||||||
|
def _getCityPulse(currTimeOfDay, doppelgangerSeed: int) -> float:
|
||||||
|
"""The data doppelganger
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
randgen = random.Random(doppelgangerSeed)
|
||||||
|
variance = 3
|
||||||
|
busyStates = ("work", "shop", "play", "party")
|
||||||
|
dataDoppelgangerState = "sleep"
|
||||||
|
dataDoppelgangerIndex = 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:
|
||||||
|
dataDoppelgangerState = "work"
|
||||||
|
dataDoppelgangerIndex = 1
|
||||||
|
elif weekday == 5:
|
||||||
|
dataDoppelgangerState = "shop"
|
||||||
|
dataDoppelgangerIndex = 2
|
||||||
|
else:
|
||||||
|
dataDoppelgangerState = "play"
|
||||||
|
dataDoppelgangerIndex = 3
|
||||||
|
else:
|
||||||
|
if weekday < 5:
|
||||||
|
dataDoppelgangerState = "evening"
|
||||||
|
dataDoppelgangerIndex = 4
|
||||||
|
else:
|
||||||
|
dataDoppelgangerState = "party"
|
||||||
|
dataDoppelgangerIndex = 5
|
||||||
|
random.seed(doppelgangerSeed)
|
||||||
|
# what consitutes the central district is fuzzy
|
||||||
|
centralDistrictFuzz = (randgen.randint(0, 100000) / 100000) * 0.1
|
||||||
|
busyRadius = 0.3 + centralDistrictFuzz
|
||||||
|
if dataDoppelgangerState 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))
|
||||||
|
angleRadians = \
|
||||||
|
(randgen.randint(0, 100000 - 5 + dataDoppelgangerIndex) / 100000) * \
|
||||||
|
2 * math.pi
|
||||||
|
return distanceFromCityCenter, angleRadians
|
||||||
|
|
||||||
|
|
||||||
def spoofGeolocation(baseDir: str,
|
def spoofGeolocation(baseDir: str,
|
||||||
city: str, currTime,
|
city: str, currTime, doppelgangerSeed: int,
|
||||||
citiesList: []) -> (float, float, str, str):
|
citiesList: []) -> (float, float, str, str):
|
||||||
"""Given a city and the current time spoofs the location
|
"""Given a city and the current time spoofs the location
|
||||||
for an image
|
for an image
|
||||||
|
|
@ -64,7 +117,8 @@ def spoofGeolocation(baseDir: str,
|
||||||
locationsFilename = baseDir + '/custom_locations.txt'
|
locationsFilename = baseDir + '/custom_locations.txt'
|
||||||
if not os.path.isfile(locationsFilename):
|
if not os.path.isfile(locationsFilename):
|
||||||
locationsFilename = baseDir + '/locations.txt'
|
locationsFilename = baseDir + '/locations.txt'
|
||||||
variance = 0.2
|
cityRadius = 0.1
|
||||||
|
variance = 0.01
|
||||||
default_latitude = 51.8744
|
default_latitude = 51.8744
|
||||||
default_longitude = 0.368333
|
default_longitude = 0.368333
|
||||||
default_latdirection = 'N'
|
default_latdirection = 'N'
|
||||||
|
|
@ -93,16 +147,30 @@ def spoofGeolocation(baseDir: str,
|
||||||
if 'W' in longitude:
|
if 'W' in longitude:
|
||||||
longdirection = 'W'
|
longdirection = 'W'
|
||||||
longitude = longitude.replace('W', '')
|
longitude = longitude.replace('W', '')
|
||||||
# add some randomness
|
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
|
||||||
|
newSeed = randint(10000000, 10000000000000)
|
||||||
|
(distanceFromCityCenter, angleRadians) = \
|
||||||
|
_getCityPulse(currTimeAdjusted, doppelgangerSeed)
|
||||||
|
random.seed(newSeed)
|
||||||
|
# Get the position within the city, with some randomness added
|
||||||
fraction = randint(0, 100000) / 100000
|
fraction = randint(0, 100000) / 100000
|
||||||
fraction = fraction * fraction
|
latitude += \
|
||||||
latitude = float(latitude) + \
|
distanceFromCityCenter * cityRadius * math.cos(angleRadians)
|
||||||
(fraction * variance) - (variance / 2.0)
|
latitude += (fraction * fraction * variance) - (variance / 2.0)
|
||||||
latitude = int(latitude * 10000) / 10000.0
|
latitude = int(latitude * 10000) / 10000.0
|
||||||
|
|
||||||
fraction = randint(0, 100000) / 100000
|
fraction = randint(0, 100000) / 100000
|
||||||
fraction = fraction * fraction
|
longitude += \
|
||||||
longitude = float(longitude) + \
|
distanceFromCityCenter * cityRadius * math.sin(angleRadians)
|
||||||
(fraction * variance) - (variance / 2.0)
|
longitude += (fraction * fraction * variance) - (variance / 2.0)
|
||||||
longitude = int(longitude * 10000) / 10000.0
|
longitude = int(longitude * 10000) / 10000.0
|
||||||
return latitude, longitude, latdirection, longdirection
|
return latitude, longitude, latdirection, longdirection
|
||||||
|
|
||||||
|
|
@ -110,13 +178,24 @@ def spoofGeolocation(baseDir: str,
|
||||||
default_latdirection, default_longdirection)
|
default_latdirection, default_longdirection)
|
||||||
|
|
||||||
|
|
||||||
def _spoofMetaData(baseDir: str, nickname: str,
|
def _spoofMetaData(baseDir: str, nickname: str, domain: str,
|
||||||
outputFilename: str, spoofCity: str) -> None:
|
outputFilename: str, spoofCity: str) -> None:
|
||||||
"""Use reference images to spoof the metadata
|
"""Use reference images to spoof the metadata
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(outputFilename):
|
if not os.path.isfile(outputFilename):
|
||||||
print('ERROR: unable to spoof metadata within ' + outputFilename)
|
print('ERROR: unable to spoof metadata within ' + outputFilename)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# get the random seed used to generate a unique pattern for this account
|
||||||
|
doppelgangerSeedFilename = \
|
||||||
|
baseDir + '/accounts/' + nickname + '@' + domain + '/doppelgangerseed'
|
||||||
|
doppelgangerSeed = 63725
|
||||||
|
if os.path.isfile(doppelgangerSeedFilename):
|
||||||
|
with open(doppelgangerSeedFilename, 'r') as fp:
|
||||||
|
doppelgangerSeed = int(fp.read())
|
||||||
|
else:
|
||||||
|
doppelgangerSeed = randint(10000, 10000000000000)
|
||||||
|
|
||||||
if os.path.isfile('/usr/bin/exiftool'):
|
if os.path.isfile('/usr/bin/exiftool'):
|
||||||
print('Spoofing metadata in ' + outputFilename + ' using exiftool')
|
print('Spoofing metadata in ' + outputFilename + ' using exiftool')
|
||||||
currTimeAdjusted = \
|
currTimeAdjusted = \
|
||||||
|
|
@ -124,7 +203,8 @@ def _spoofMetaData(baseDir: str, nickname: str,
|
||||||
datetime.timedelta(minutes=randint(2, 120))
|
datetime.timedelta(minutes=randint(2, 120))
|
||||||
published = currTimeAdjusted.strftime("%Y:%m:%d %H:%M:%S+00:00")
|
published = currTimeAdjusted.strftime("%Y:%m:%d %H:%M:%S+00:00")
|
||||||
(latitude, longitude, latitudeRef, longitudeRef) = \
|
(latitude, longitude, latitudeRef, longitudeRef) = \
|
||||||
spoofGeolocation(baseDir, spoofCity, currTimeAdjusted, None)
|
spoofGeolocation(baseDir, spoofCity, currTimeAdjusted,
|
||||||
|
doppelgangerSeed, None)
|
||||||
os.system('exiftool -artist="' + nickname + '" ' +
|
os.system('exiftool -artist="' + nickname + '" ' +
|
||||||
'-DateTimeOriginal="' + published + '" ' +
|
'-DateTimeOriginal="' + published + '" ' +
|
||||||
'-FileModifyDate="' + published + '" ' +
|
'-FileModifyDate="' + published + '" ' +
|
||||||
|
|
@ -141,7 +221,7 @@ def _spoofMetaData(baseDir: str, nickname: str,
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def processMetaData(baseDir: str, nickname: str,
|
def processMetaData(baseDir: str, nickname: str, domain: str,
|
||||||
imageFilename: str, outputFilename: str,
|
imageFilename: str, outputFilename: str,
|
||||||
city: str) -> None:
|
city: str) -> None:
|
||||||
"""Handles image metadata. This tries to spoof the metadata
|
"""Handles image metadata. This tries to spoof the metadata
|
||||||
|
|
@ -151,7 +231,7 @@ def processMetaData(baseDir: str, nickname: str,
|
||||||
_removeMetaData(imageFilename, outputFilename)
|
_removeMetaData(imageFilename, outputFilename)
|
||||||
|
|
||||||
# now add some spoofed data to misdirect surveillance capitalists
|
# now add some spoofed data to misdirect surveillance capitalists
|
||||||
_spoofMetaData(baseDir, nickname, outputFilename, city)
|
_spoofMetaData(baseDir, nickname, domain, outputFilename, city)
|
||||||
|
|
||||||
|
|
||||||
def _isMedia(imageFilename: str) -> bool:
|
def _isMedia(imageFilename: str) -> bool:
|
||||||
|
|
@ -282,7 +362,7 @@ def attachMedia(baseDir: str, httpPrefix: str,
|
||||||
|
|
||||||
if baseDir:
|
if baseDir:
|
||||||
if mediaType.startswith('image/'):
|
if mediaType.startswith('image/'):
|
||||||
processMetaData(baseDir, nickname,
|
processMetaData(baseDir, nickname, domain,
|
||||||
imageFilename, mediaFilename, city)
|
imageFilename, mediaFilename, city)
|
||||||
else:
|
else:
|
||||||
copyfile(imageFilename, mediaFilename)
|
copyfile(imageFilename, mediaFilename)
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
|
||||||
'/usr/bin/convert ' + imageFilename + ' -size ' + \
|
'/usr/bin/convert ' + imageFilename + ' -size ' + \
|
||||||
resolution + ' -quality 50 ' + profileFilename
|
resolution + ' -quality 50 ' + profileFilename
|
||||||
subprocess.call(cmd, shell=True)
|
subprocess.call(cmd, shell=True)
|
||||||
processMetaData(baseDir, nickname,
|
processMetaData(baseDir, nickname, domain,
|
||||||
profileFilename, profileFilename, city)
|
profileFilename, profileFilename, city)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ def addShare(baseDir: str,
|
||||||
formats = getImageExtensions()
|
formats = getImageExtensions()
|
||||||
for ext in formats:
|
for ext in formats:
|
||||||
if imageFilename.endswith('.' + ext):
|
if imageFilename.endswith('.' + ext):
|
||||||
processMetaData(baseDir, nickname,
|
processMetaData(baseDir, nickname, domain,
|
||||||
imageFilename, itemIDfile + '.' + ext,
|
imageFilename, itemIDfile + '.' + ext,
|
||||||
city)
|
city)
|
||||||
if moveImage:
|
if moveImage:
|
||||||
|
|
|
||||||
24
tests.py
24
tests.py
|
|
@ -3676,18 +3676,22 @@ def testSpoofGeolocation() -> None:
|
||||||
'HOUSTON, USA:29.9803:W95.3397'
|
'HOUSTON, USA:29.9803:W95.3397'
|
||||||
]
|
]
|
||||||
currTime = datetime.datetime.utcnow()
|
currTime = datetime.datetime.utcnow()
|
||||||
coords = spoofGeolocation('', 'los angeles', currTime, citiesList)
|
doppelgangerSeed = 7634682
|
||||||
assert coords[0] >= 33.9425 - 0.1
|
cityRadius = 0.1
|
||||||
assert coords[0] <= 33.9425 + 0.1
|
coords = spoofGeolocation('', 'los angeles', currTime,
|
||||||
assert coords[1] >= 118.408 - 0.1
|
doppelgangerSeed, citiesList)
|
||||||
assert coords[1] <= 118.408 + 0.1
|
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[2] == 'N'
|
||||||
assert coords[3] == 'W'
|
assert coords[3] == 'W'
|
||||||
coords = spoofGeolocation('', 'unknown', currTime, citiesList)
|
coords = spoofGeolocation('', 'unknown', currTime,
|
||||||
assert coords[0] >= 51.8744 - 0.1
|
doppelgangerSeed, citiesList)
|
||||||
assert coords[0] <= 51.8744 + 0.1
|
assert coords[0] >= 51.8744 - cityRadius
|
||||||
assert coords[1] >= 0.368333 - 0.1
|
assert coords[0] <= 51.8744 + cityRadius
|
||||||
assert coords[1] <= 0.368333 + 0.1
|
assert coords[1] >= 0.368333 - cityRadius
|
||||||
|
assert coords[1] <= 0.368333 + cityRadius
|
||||||
assert coords[2] == 'N'
|
assert coords[2] == 'N'
|
||||||
assert coords[3] == 'W'
|
assert coords[3] == 'W'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue