mirror of https://gitlab.com/bashrc2/epicyon
Option to spoof image metadata
parent
572d338943
commit
d8b882c10f
10
daemon.py
10
daemon.py
|
@ -239,7 +239,7 @@ from content import addHtmlTags
|
||||||
from content import extractMediaInFormPOST
|
from content import extractMediaInFormPOST
|
||||||
from content import saveMediaInFormPOST
|
from content import saveMediaInFormPOST
|
||||||
from content import extractTextFieldsInPOST
|
from content import extractTextFieldsInPOST
|
||||||
from media import removeMetaData
|
from media import processMetaData
|
||||||
from cache import checkForChangedActor
|
from cache import checkForChangedActor
|
||||||
from cache import storePersonInCache
|
from cache import storePersonInCache
|
||||||
from cache import getPersonFromCache
|
from cache import getPersonFromCache
|
||||||
|
@ -4075,7 +4075,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
os.remove(postImageFilename + '.etag')
|
os.remove(postImageFilename + '.etag')
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
removeMetaData(filename, postImageFilename)
|
processMetaData(baseDir, nickname, domain,
|
||||||
|
filename, postImageFilename)
|
||||||
if os.path.isfile(postImageFilename):
|
if os.path.isfile(postImageFilename):
|
||||||
print('profile update POST ' + mType +
|
print('profile update POST ' + mType +
|
||||||
' image or font saved to ' + postImageFilename)
|
' image or font saved to ' + postImageFilename)
|
||||||
|
@ -13053,7 +13054,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
filename.endswith('.gif'):
|
filename.endswith('.gif'):
|
||||||
postImageFilename = filename.replace('.temp', '')
|
postImageFilename = filename.replace('.temp', '')
|
||||||
print('Removing metadata from ' + postImageFilename)
|
print('Removing metadata from ' + postImageFilename)
|
||||||
removeMetaData(filename, postImageFilename)
|
processMetaData(self.server.baseDir,
|
||||||
|
nickname, self.server.domain,
|
||||||
|
filename, postImageFilename)
|
||||||
if os.path.isfile(postImageFilename):
|
if os.path.isfile(postImageFilename):
|
||||||
print('POST media saved to ' + postImageFilename)
|
print('POST media saved to ' + postImageFilename)
|
||||||
else:
|
else:
|
||||||
|
@ -13307,6 +13310,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
postJsonObject['object'] = \
|
postJsonObject['object'] = \
|
||||||
attachMedia(self.server.baseDir,
|
attachMedia(self.server.baseDir,
|
||||||
self.server.httpPrefix,
|
self.server.httpPrefix,
|
||||||
|
nickname,
|
||||||
self.server.domain,
|
self.server.domain,
|
||||||
self.server.port,
|
self.server.port,
|
||||||
postJsonObject['object'],
|
postJsonObject['object'],
|
||||||
|
|
71
media.py
71
media.py
|
@ -8,6 +8,7 @@ __status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
from random import randint
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from auth import createPassword
|
from auth import createPassword
|
||||||
from utils import getFullDomain
|
from utils import getFullDomain
|
||||||
|
@ -37,7 +38,7 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None:
|
||||||
replacementDomain)
|
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,
|
"""Attempts to do this with pure python didn't work well,
|
||||||
so better to use a dedicated tool if one is installed
|
so better to use a dedicated tool if one is installed
|
||||||
"""
|
"""
|
||||||
|
@ -53,6 +54,68 @@ def removeMetaData(imageFilename: str, outputFilename: str) -> None:
|
||||||
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
|
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
|
||||||
|
|
||||||
|
|
||||||
|
def _spoofMetaData(imageFilename: str, outputFilename: str,
|
||||||
|
spoofFilename: str) -> None:
|
||||||
|
"""Use reference images to spoof the metadata
|
||||||
|
"""
|
||||||
|
copyfile(imageFilename, outputFilename)
|
||||||
|
if not os.path.isfile(outputFilename):
|
||||||
|
print('ERROR: unable to spoof metadata from ' + imageFilename)
|
||||||
|
return
|
||||||
|
if not os.path.isfile(spoofFilename):
|
||||||
|
print('ERROR: No spoof reference image ' + spoofFilename)
|
||||||
|
return
|
||||||
|
if os.path.isfile('/usr/bin/exiftool'):
|
||||||
|
print('Spoofing metadata in ' + outputFilename + ' using exiftool')
|
||||||
|
os.system('exiftool -TagsFromFile ' +
|
||||||
|
spoofFilename + ' ' + outputFilename) # nosec
|
||||||
|
else:
|
||||||
|
print('ERROR: exiftool is not installed')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def processMetaData(baseDir: str, nickname: str, domain: str,
|
||||||
|
imageFilename: str, outputFilename: str) -> None:
|
||||||
|
"""Handles image metadata. This tries to spoof the metadata
|
||||||
|
if possible, but otherwise just removes it
|
||||||
|
"""
|
||||||
|
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
|
||||||
|
spoofImagesDir = accountDir + '/ref/images'
|
||||||
|
if os.path.isdir(spoofImagesDir):
|
||||||
|
imageTypes = getImageExtensions()
|
||||||
|
# get the format of the target image
|
||||||
|
ext = None
|
||||||
|
for mType in imageTypes:
|
||||||
|
if outputFilename.endswith('.' + mType):
|
||||||
|
ext = mType
|
||||||
|
break
|
||||||
|
if ext:
|
||||||
|
spoofList = []
|
||||||
|
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
|
||||||
|
for f in files:
|
||||||
|
filename = os.path.join(spoofImagesDir, f)
|
||||||
|
# what is the format of this file?
|
||||||
|
currExt = None
|
||||||
|
for mType in imageTypes:
|
||||||
|
if filename.endswith('.' + mType):
|
||||||
|
currExt = mType
|
||||||
|
break
|
||||||
|
# if this the same format as the target?
|
||||||
|
if currExt:
|
||||||
|
if currExt == ext:
|
||||||
|
spoofList.append(filename)
|
||||||
|
break
|
||||||
|
if spoofList:
|
||||||
|
# choose a reference at random
|
||||||
|
index = randint(0, len(spoofList))
|
||||||
|
spoofFilename = spoofList[index]
|
||||||
|
_spoofMetaData(imageFilename, outputFilename,
|
||||||
|
spoofFilename)
|
||||||
|
return
|
||||||
|
# if we can't spoof then just remove metadata
|
||||||
|
_removeMetaData(imageFilename, outputFilename)
|
||||||
|
|
||||||
|
|
||||||
def _isMedia(imageFilename: str) -> bool:
|
def _isMedia(imageFilename: str) -> bool:
|
||||||
"""Is the given file a media file?
|
"""Is the given file a media file?
|
||||||
"""
|
"""
|
||||||
|
@ -131,7 +194,8 @@ def _updateEtag(mediaFilename: str) -> None:
|
||||||
pass
|
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,
|
postJson: {}, imageFilename: str,
|
||||||
mediaType: str, description: str) -> {}:
|
mediaType: str, description: str) -> {}:
|
||||||
"""Attaches media to a json object post
|
"""Attaches media to a json object post
|
||||||
|
@ -179,7 +243,8 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
|
||||||
|
|
||||||
if baseDir:
|
if baseDir:
|
||||||
if mediaType.startswith('image/'):
|
if mediaType.startswith('image/'):
|
||||||
removeMetaData(imageFilename, mediaFilename)
|
processMetaData(baseDir, nickname, domain,
|
||||||
|
imageFilename, mediaFilename)
|
||||||
else:
|
else:
|
||||||
copyfile(imageFilename, mediaFilename)
|
copyfile(imageFilename, mediaFilename)
|
||||||
_updateEtag(mediaFilename)
|
_updateEtag(mediaFilename)
|
||||||
|
|
|
@ -34,7 +34,7 @@ from posts import createModeration
|
||||||
from auth import storeBasicCredentials
|
from auth import storeBasicCredentials
|
||||||
from auth import removePassword
|
from auth import removePassword
|
||||||
from roles import setRole
|
from roles import setRole
|
||||||
from media import removeMetaData
|
from media import processMetaData
|
||||||
from utils import getStatusNumber
|
from utils import getStatusNumber
|
||||||
from utils import getFullDomain
|
from utils import getFullDomain
|
||||||
from utils import validNickname
|
from utils import validNickname
|
||||||
|
@ -135,7 +135,8 @@ 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)
|
||||||
removeMetaData(profileFilename, profileFilename)
|
processMetaData(baseDir, nickname, domain,
|
||||||
|
profileFilename, profileFilename)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
18
posts.py
18
posts.py
|
@ -1059,6 +1059,10 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
idStr = \
|
idStr = \
|
||||||
httpPrefix + '://' + domain + '/users/' + nickname + \
|
httpPrefix + '://' + domain + '/users/' + nickname + \
|
||||||
'/statuses/' + statusNumber + '/replies'
|
'/statuses/' + statusNumber + '/replies'
|
||||||
|
newPostUrl = \
|
||||||
|
httpPrefix + '://' + domain + '/@' + nickname + '/'+statusNumber
|
||||||
|
newPostAttributedTo = \
|
||||||
|
httpPrefix + '://' + domain + '/users/' + nickname
|
||||||
newPost = {
|
newPost = {
|
||||||
'@context': postContext,
|
'@context': postContext,
|
||||||
'id': newPostId + '/activity',
|
'id': newPostId + '/activity',
|
||||||
|
@ -1073,8 +1077,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
'summary': summary,
|
'summary': summary,
|
||||||
'inReplyTo': inReplyTo,
|
'inReplyTo': inReplyTo,
|
||||||
'published': published,
|
'published': published,
|
||||||
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber,
|
'url': newPostUrl,
|
||||||
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname,
|
'attributedTo': newPostAttributedTo,
|
||||||
'to': toRecipients,
|
'to': toRecipients,
|
||||||
'cc': toCC,
|
'cc': toCC,
|
||||||
'sensitive': sensitive,
|
'sensitive': sensitive,
|
||||||
|
@ -1101,7 +1105,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
}
|
}
|
||||||
if attachImageFilename:
|
if attachImageFilename:
|
||||||
newPost['object'] = \
|
newPost['object'] = \
|
||||||
attachMedia(baseDir, httpPrefix, domain, port,
|
attachMedia(baseDir, httpPrefix, nickname, domain, port,
|
||||||
newPost['object'], attachImageFilename,
|
newPost['object'], attachImageFilename,
|
||||||
mediaType, imageDescription)
|
mediaType, imageDescription)
|
||||||
_appendEventFields(newPost['object'], eventUUID, eventStatus,
|
_appendEventFields(newPost['object'], eventUUID, eventStatus,
|
||||||
|
@ -1115,6 +1119,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
idStr = \
|
idStr = \
|
||||||
httpPrefix + '://' + domain + '/users/' + nickname + \
|
httpPrefix + '://' + domain + '/users/' + nickname + \
|
||||||
'/statuses/' + statusNumber + '/replies'
|
'/statuses/' + statusNumber + '/replies'
|
||||||
|
newPostUrl = \
|
||||||
|
httpPrefix + '://' + domain + '/@' + nickname+'/' + statusNumber
|
||||||
newPost = {
|
newPost = {
|
||||||
"@context": postContext,
|
"@context": postContext,
|
||||||
'id': newPostId,
|
'id': newPostId,
|
||||||
|
@ -1122,8 +1128,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
'summary': summary,
|
'summary': summary,
|
||||||
'inReplyTo': inReplyTo,
|
'inReplyTo': inReplyTo,
|
||||||
'published': published,
|
'published': published,
|
||||||
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber,
|
'url': newPostUrl,
|
||||||
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname,
|
'attributedTo': httpPrefix + '://' + domain + '/users/' + nickname,
|
||||||
'to': toRecipients,
|
'to': toRecipients,
|
||||||
'cc': toCC,
|
'cc': toCC,
|
||||||
'sensitive': sensitive,
|
'sensitive': sensitive,
|
||||||
|
@ -1149,7 +1155,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
}
|
}
|
||||||
if attachImageFilename:
|
if attachImageFilename:
|
||||||
newPost = \
|
newPost = \
|
||||||
attachMedia(baseDir, httpPrefix, domain, port,
|
attachMedia(baseDir, httpPrefix, nickname, domain, port,
|
||||||
newPost, attachImageFilename,
|
newPost, attachImageFilename,
|
||||||
mediaType, imageDescription)
|
mediaType, imageDescription)
|
||||||
_appendEventFields(newPost, eventUUID, eventStatus,
|
_appendEventFields(newPost, eventUUID, eventStatus,
|
||||||
|
|
|
@ -18,7 +18,7 @@ from utils import validNickname
|
||||||
from utils import loadJson
|
from utils import loadJson
|
||||||
from utils import saveJson
|
from utils import saveJson
|
||||||
from utils import getImageExtensions
|
from utils import getImageExtensions
|
||||||
from media import removeMetaData
|
from media import processMetaData
|
||||||
|
|
||||||
|
|
||||||
def getValidSharedItemID(displayName: str) -> str:
|
def getValidSharedItemID(displayName: str) -> str:
|
||||||
|
@ -129,7 +129,8 @@ def addShare(baseDir: str,
|
||||||
formats = getImageExtensions()
|
formats = getImageExtensions()
|
||||||
for ext in formats:
|
for ext in formats:
|
||||||
if imageFilename.endswith('.' + ext):
|
if imageFilename.endswith('.' + ext):
|
||||||
removeMetaData(imageFilename, itemIDfile + '.' + ext)
|
processMetaData(baseDir, nickname, domain,
|
||||||
|
imageFilename, itemIDfile + '.' + ext)
|
||||||
if moveImage:
|
if moveImage:
|
||||||
os.remove(imageFilename)
|
os.remove(imageFilename)
|
||||||
imageUrl = \
|
imageUrl = \
|
||||||
|
|
Loading…
Reference in New Issue