2020-05-02 10:07:50 +00:00
|
|
|
__filename__ = "git.py"
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
2021-01-26 10:07:42 +00:00
|
|
|
__version__ = "1.2.0"
|
2020-05-02 10:07:50 +00:00
|
|
|
__maintainer__ = "Bob Mottram"
|
|
|
|
__email__ = "bob@freedombone.net"
|
|
|
|
__status__ = "Production"
|
2021-06-26 11:16:41 +00:00
|
|
|
__module_group__ = "Profile Metadata"
|
2020-05-02 10:07:50 +00:00
|
|
|
|
|
|
|
import os
|
2020-05-02 18:09:54 +00:00
|
|
|
import html
|
2021-06-22 15:45:59 +00:00
|
|
|
from utils import hasObjectDict
|
2021-07-13 21:59:53 +00:00
|
|
|
from utils import acctDir
|
2020-05-02 10:07:50 +00:00
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _gitFormatContent(content: str) -> str:
|
2020-05-02 17:06:13 +00:00
|
|
|
""" replace html formatting, so that it's more
|
|
|
|
like the original patch file
|
|
|
|
"""
|
2020-05-03 11:39:44 +00:00
|
|
|
patchStr = content.replace('<br>', '\n').replace('<br />', '\n')
|
|
|
|
patchStr = patchStr.replace('<p>', '').replace('</p>', '\n')
|
|
|
|
patchStr = html.unescape(patchStr)
|
|
|
|
if 'From ' in patchStr:
|
|
|
|
patchStr = 'From ' + patchStr.split('From ', 1)[1]
|
|
|
|
return patchStr
|
2020-05-02 17:06:13 +00:00
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _getGitProjectName(baseDir: str, nickname: str, domain: str,
|
|
|
|
subject: str) -> str:
|
2020-05-02 11:08:38 +00:00
|
|
|
"""Returns the project name for a git patch
|
|
|
|
The project name should be contained within the subject line
|
|
|
|
and should match against a list of projects which the account
|
|
|
|
holder wants to receive
|
|
|
|
"""
|
|
|
|
gitProjectsFilename = \
|
2021-07-13 21:59:53 +00:00
|
|
|
acctDir(baseDir, nickname, domain) + '/gitprojects.txt'
|
2020-05-02 11:08:38 +00:00
|
|
|
if not os.path.isfile(gitProjectsFilename):
|
|
|
|
return None
|
2020-05-02 16:05:28 +00:00
|
|
|
subjectLineWords = subject.lower().split(' ')
|
|
|
|
for word in subjectLineWords:
|
2020-05-02 16:29:43 +00:00
|
|
|
if word in open(gitProjectsFilename).read():
|
2020-05-02 11:08:38 +00:00
|
|
|
return word
|
2020-05-02 16:24:17 +00:00
|
|
|
return None
|
2020-05-02 11:08:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
def isGitPatch(baseDir: str, nickname: str, domain: str,
|
2020-05-03 10:56:29 +00:00
|
|
|
messageType: str,
|
2020-05-03 12:39:54 +00:00
|
|
|
subject: str, content: str,
|
2021-06-20 11:28:35 +00:00
|
|
|
checkProjectName: bool = True) -> bool:
|
2020-05-02 10:07:50 +00:00
|
|
|
"""Is the given post content a git patch?
|
|
|
|
"""
|
2020-05-03 10:56:29 +00:00
|
|
|
if messageType != 'Note' and \
|
2020-05-03 12:52:13 +00:00
|
|
|
messageType != 'Patch':
|
2020-05-03 10:56:29 +00:00
|
|
|
return False
|
2020-05-02 10:07:50 +00:00
|
|
|
# must have a subject line
|
2020-05-02 10:19:24 +00:00
|
|
|
if not subject:
|
2020-05-02 10:07:50 +00:00
|
|
|
return False
|
|
|
|
if '[PATCH]' not in content:
|
|
|
|
return False
|
|
|
|
if '---' not in content:
|
|
|
|
return False
|
|
|
|
if 'diff ' not in content:
|
|
|
|
return False
|
2020-05-03 12:39:54 +00:00
|
|
|
if 'From ' not in content:
|
|
|
|
return False
|
2020-05-02 10:07:50 +00:00
|
|
|
if 'From:' not in content:
|
|
|
|
return False
|
|
|
|
if 'Date:' not in content:
|
|
|
|
return False
|
|
|
|
if 'Subject:' not in content:
|
|
|
|
return False
|
2020-05-02 14:47:30 +00:00
|
|
|
if '<br>' not in content:
|
|
|
|
if '<br />' not in content:
|
|
|
|
return False
|
2020-05-03 12:39:54 +00:00
|
|
|
if checkProjectName:
|
|
|
|
projectName = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_getGitProjectName(baseDir, nickname, domain, subject)
|
2020-05-03 12:39:54 +00:00
|
|
|
if not projectName:
|
|
|
|
return False
|
2020-05-02 11:08:38 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _getGitHash(patchStr: str) -> str:
|
2020-05-03 11:38:09 +00:00
|
|
|
"""Returns the commit hash from a given patch
|
|
|
|
"""
|
2020-05-03 11:39:44 +00:00
|
|
|
patchLines = patchStr.split('\n')
|
2020-05-03 11:38:09 +00:00
|
|
|
for line in patchLines:
|
|
|
|
if line.startswith('From '):
|
|
|
|
words = line.split(' ')
|
|
|
|
if len(words) > 1:
|
|
|
|
if len(words[1]) > 20:
|
|
|
|
return words[1]
|
|
|
|
break
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _getPatchDescription(patchStr: str) -> str:
|
2020-05-03 13:50:01 +00:00
|
|
|
"""Returns the description from a given patch
|
|
|
|
"""
|
|
|
|
patchLines = patchStr.split('\n')
|
|
|
|
description = ''
|
|
|
|
started = False
|
|
|
|
for line in patchLines:
|
|
|
|
if started:
|
|
|
|
if line.strip() == '---':
|
|
|
|
break
|
|
|
|
description += line + '\n'
|
|
|
|
if line.startswith('Subject:'):
|
|
|
|
started = True
|
|
|
|
return description
|
|
|
|
|
|
|
|
|
2020-05-03 12:52:13 +00:00
|
|
|
def convertPostToPatch(baseDir: str, nickname: str, domain: str,
|
|
|
|
postJsonObject: {}) -> bool:
|
2020-05-03 12:39:54 +00:00
|
|
|
"""Detects whether the given post contains a patch
|
2020-05-03 12:52:13 +00:00
|
|
|
and if so then converts it to a Patch ActivityPub type
|
2020-05-03 12:39:54 +00:00
|
|
|
"""
|
2021-06-22 15:45:59 +00:00
|
|
|
if not hasObjectDict(postJsonObject):
|
2020-05-03 12:39:54 +00:00
|
|
|
return False
|
|
|
|
if not postJsonObject['object'].get('type'):
|
|
|
|
return False
|
2020-05-03 12:52:13 +00:00
|
|
|
if postJsonObject['object']['type'] == 'Patch':
|
2020-05-03 12:39:54 +00:00
|
|
|
return True
|
|
|
|
if not postJsonObject['object'].get('summary'):
|
|
|
|
return False
|
|
|
|
if not postJsonObject['object'].get('content'):
|
|
|
|
return False
|
|
|
|
if not postJsonObject['object'].get('attributedTo'):
|
|
|
|
return False
|
2020-08-06 16:21:46 +00:00
|
|
|
if not isinstance(postJsonObject['object']['attributedTo'], str):
|
|
|
|
return False
|
2020-05-03 12:39:54 +00:00
|
|
|
if not isGitPatch(baseDir, nickname, domain,
|
|
|
|
postJsonObject['object']['type'],
|
|
|
|
postJsonObject['object']['summary'],
|
|
|
|
postJsonObject['object']['content'],
|
|
|
|
False):
|
|
|
|
return False
|
2020-12-22 18:06:23 +00:00
|
|
|
patchStr = _gitFormatContent(postJsonObject['object']['content'])
|
|
|
|
commitHash = _getGitHash(patchStr)
|
2020-05-03 12:41:05 +00:00
|
|
|
if not commitHash:
|
|
|
|
return False
|
2020-05-03 12:52:13 +00:00
|
|
|
postJsonObject['object']['type'] = 'Patch'
|
2020-05-03 12:39:54 +00:00
|
|
|
# add a commitedBy parameter
|
|
|
|
if not postJsonObject['object'].get('committedBy'):
|
|
|
|
postJsonObject['object']['committedBy'] = \
|
|
|
|
postJsonObject['object']['attributedTo']
|
2020-05-03 12:41:05 +00:00
|
|
|
postJsonObject['object']['hash'] = commitHash
|
2020-05-03 12:39:54 +00:00
|
|
|
postJsonObject['object']['description'] = {
|
|
|
|
"mediaType": "text/plain",
|
2020-12-22 18:06:23 +00:00
|
|
|
"content": _getPatchDescription(patchStr)
|
2020-05-03 12:39:54 +00:00
|
|
|
}
|
2020-05-03 12:52:13 +00:00
|
|
|
# remove content map
|
|
|
|
if postJsonObject['object'].get('contentMap'):
|
|
|
|
del postJsonObject['object']['contentMap']
|
|
|
|
print('Converted post to Patch ActivityPub type')
|
2020-05-03 12:39:54 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
def _gitAddFromHandle(patchStr: str, handle: str) -> str:
|
2020-05-03 09:48:12 +00:00
|
|
|
"""Adds the activitypub handle of the sender to the patch
|
|
|
|
"""
|
|
|
|
fromStr = 'AP-signed-off-by: '
|
2020-05-03 11:39:44 +00:00
|
|
|
if fromStr in patchStr:
|
|
|
|
return patchStr
|
2020-05-03 09:48:12 +00:00
|
|
|
|
2020-05-03 11:42:18 +00:00
|
|
|
patchLines = patchStr.split('\n')
|
2020-05-03 11:39:44 +00:00
|
|
|
patchStr = ''
|
2020-05-03 09:48:12 +00:00
|
|
|
for line in patchLines:
|
2020-05-03 11:39:44 +00:00
|
|
|
patchStr += line + '\n'
|
2020-05-03 09:48:12 +00:00
|
|
|
if line.startswith('From:'):
|
2020-05-03 11:39:44 +00:00
|
|
|
if fromStr not in patchStr:
|
|
|
|
patchStr += fromStr + handle + '\n'
|
|
|
|
return patchStr
|
2020-05-03 09:48:12 +00:00
|
|
|
|
|
|
|
|
2020-05-02 11:08:38 +00:00
|
|
|
def receiveGitPatch(baseDir: str, nickname: str, domain: str,
|
2020-05-03 13:12:52 +00:00
|
|
|
messageType: str, subject: str, content: str,
|
2020-05-03 09:48:12 +00:00
|
|
|
fromNickname: str, fromDomain: str) -> bool:
|
2020-05-02 11:08:38 +00:00
|
|
|
"""Receive a git patch
|
|
|
|
"""
|
|
|
|
if not isGitPatch(baseDir, nickname, domain,
|
2020-05-03 13:12:52 +00:00
|
|
|
messageType, subject, content):
|
2020-05-02 11:08:38 +00:00
|
|
|
return False
|
2020-05-02 16:05:28 +00:00
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
patchStr = _gitFormatContent(content)
|
2020-05-02 16:05:28 +00:00
|
|
|
|
2020-05-03 11:39:44 +00:00
|
|
|
patchLines = patchStr.split('\n')
|
2020-05-02 10:07:50 +00:00
|
|
|
patchFilename = None
|
2020-05-02 16:53:20 +00:00
|
|
|
projectDir = None
|
2021-07-13 21:59:53 +00:00
|
|
|
patchesDir = acctDir(baseDir, nickname, domain) + '/patches'
|
2020-05-02 10:07:50 +00:00
|
|
|
# get the subject line and turn it into a filename
|
|
|
|
for line in patchLines:
|
|
|
|
if line.startswith('Subject:'):
|
|
|
|
patchSubject = \
|
2020-05-02 16:44:46 +00:00
|
|
|
line.replace('Subject:', '').replace('/', '|')
|
|
|
|
patchSubject = patchSubject.replace('[PATCH]', '').strip()
|
|
|
|
patchSubject = patchSubject.replace(' ', '_')
|
2020-05-02 11:08:38 +00:00
|
|
|
projectName = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_getGitProjectName(baseDir, nickname, domain, subject)
|
2020-05-02 16:53:20 +00:00
|
|
|
if not os.path.isdir(patchesDir):
|
|
|
|
os.mkdir(patchesDir)
|
|
|
|
projectDir = patchesDir + '/' + projectName
|
|
|
|
if not os.path.isdir(projectDir):
|
|
|
|
os.mkdir(projectDir)
|
2020-05-02 10:07:50 +00:00
|
|
|
patchFilename = \
|
2020-05-02 16:53:20 +00:00
|
|
|
projectDir + '/' + patchSubject + '.patch'
|
2020-05-02 10:07:50 +00:00
|
|
|
break
|
|
|
|
if not patchFilename:
|
|
|
|
return False
|
2020-05-03 11:39:44 +00:00
|
|
|
patchStr = \
|
2020-12-22 18:06:23 +00:00
|
|
|
_gitAddFromHandle(patchStr, '@' + fromNickname + '@' + fromDomain)
|
2021-06-21 22:53:04 +00:00
|
|
|
with open(patchFilename, 'w+') as patchFile:
|
|
|
|
patchFile.write(patchStr)
|
2020-05-02 17:36:22 +00:00
|
|
|
patchNotifyFilename = \
|
2021-07-13 21:59:53 +00:00
|
|
|
acctDir(baseDir, nickname, domain) + '/.newPatchContent'
|
2021-06-21 22:53:04 +00:00
|
|
|
with open(patchNotifyFilename, 'w+') as patchFile:
|
|
|
|
patchFile.write(patchStr)
|
2020-05-02 17:42:51 +00:00
|
|
|
return True
|
2020-05-02 16:44:46 +00:00
|
|
|
return False
|