Remove trailing whitespace

merge-requests/30/head
Bob Mottram 2020-03-22 21:16:02 +00:00
parent 42c2021831
commit 23bb250deb
39 changed files with 654 additions and 657 deletions

View File

@ -85,7 +85,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
if not messageJson.get('object'):
return
if not messageJson['object'].get('type'):
return
return
if not messageJson['object']['type']=='Follow':
return
if debug:
@ -96,7 +96,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
# no, this isn't a mistake
if not messageJson['object'].get('object'):
print('DEBUG: no object within Follow activity')
return
return
if not messageJson.get('to'):
if debug:
print('DEBUG: No "to" parameter in follow Accept')
@ -115,7 +115,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
return
if not nickname:
if debug:
print('DEBUG: nickname not found in '+thisActor)
print('DEBUG: nickname not found in '+thisActor)
return
if acceptedPort:
if '/'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname not in thisActor:
@ -132,7 +132,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
print('Expected: /'+acceptedDomain+'/users/'+nickname)
print('Actual: '+thisActor)
print('DEBUG: unrecognized actor '+thisActor)
return
return
followedActor=messageJson['object']['object']
followedDomain,port=getDomainFromActor(followedActor)
if not followedDomain:
@ -166,7 +166,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
nickname+'@'+acceptedDomainFull+ \
' from '+followedNickname+'@'+followedDomainFull+ \
' but they have been unfollowed')
return
return
if followPerson(baseDir, \
nickname,acceptedDomainFull, \

View File

@ -78,7 +78,7 @@ def undoAnnounceCollectionEntry(recentPostsCache: {}, \
baseDir: str,postFilename: str, \
actor: str,domain: str,debug: bool) -> None:
"""Undoes an announce for a particular actor by removing it from
the "shares" collection within a post. Note that the "shares"
the "shares" collection within a post. Note that the "shares"
collection has no relation to shared items in shares.py. It's
shares of posts, not shares of physical objects.
"""
@ -169,7 +169,7 @@ def updateAnnounceCollection(recentPostsCache: {}, \
"totalItems": 1,
'items': [{
'type': 'Announce',
'actor': actor
'actor': actor
}]
}
postJsonObject['object']['shares']=announcementsJson
@ -274,7 +274,7 @@ def createAnnounce(session,baseDir: str,federationList: [], \
httpPrefix,True,clientToServer,federationList, \
sendThreads,postLog,cachedWebfingers,personCache, \
debug,projectVersion)
return newAnnounce
def announcePublic(session,baseDir: str,federationList: [], \
@ -386,7 +386,7 @@ def undoAnnounce(session,baseDir: str,federationList: [], \
'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix,True,clientToServer,federationList, \
sendThreads,postLog,cachedWebfingers,personCache,debug)
return newUndoAnnounce
def undoAnnouncePublic(session,baseDir: str,federationList: [], \
@ -495,7 +495,7 @@ def sendAnnounceViaServer(baseDir: str,session, \
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname,fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -504,9 +504,9 @@ def sendAnnounceViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \

View File

@ -22,7 +22,7 @@ def hashPassword(password: str) -> str:
salt, 100000)
pwdhash=binascii.hexlify(pwdhash)
return (salt+pwdhash).decode('ascii')
def verifyPassword(storedPassword: str,providedPassword: str) -> bool:
"""Verify a stored password against one provided by user
"""

View File

@ -88,7 +88,7 @@ def sendAvailabilityViaServer(baseDir: str,session, \
if port!=80 and port!=443:
if ':' not in domain:
domainFull=domain+':'+str(port)
toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname
ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers'
@ -117,7 +117,7 @@ def sendAvailabilityViaServer(baseDir: str,session, \
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,nickname,domain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -126,9 +126,9 @@ def sendAvailabilityViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(Nickname,password)
headers={
'host': domain, \
'Content-type': 'application/json', \

View File

@ -14,7 +14,7 @@ def addGlobalBlock(baseDir: str, \
"""Global block which applies to all accounts
"""
blockingFilename=baseDir+'/accounts/blocking.txt'
if not blockNickname.startswith('#'):
if not blockNickname.startswith('#'):
blockHandle=blockNickname+'@'+blockDomain
if os.path.isfile(blockingFilename):
if blockHandle in open(blockingFilename).read():
@ -54,7 +54,7 @@ def removeGlobalBlock(baseDir: str, \
"""Unblock the given global block
"""
unblockingFilename=baseDir+'/accounts/blocking.txt'
if not unblockNickname.startswith('#'):
if not unblockNickname.startswith('#'):
unblockHandle=unblockNickname+'@'+unblockDomain
if os.path.isfile(unblockingFilename):
if unblockHandle in open(unblockingFilename).read():

12
blog.py
View File

@ -171,7 +171,7 @@ def htmlBlogPostContent(authorized: bool, \
# posts from the domain are expected to have an attributedTo field
if restrictToDomain:
return ''
if postJsonObject['object'].get('published'):
if 'T' in postJsonObject['object']['published']:
blogStr+='<h3>'+postJsonObject['object']['published'].split('T')[0]
@ -312,7 +312,7 @@ def htmlBlogPage(authorized: bool, session, \
"""
if ' ' in nickname or '@' in nickname or '\n' in nickname:
return None
blogStr=''
blogStr=''
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
@ -424,7 +424,7 @@ def htmlBlogPageRSS(authorized: bool, session, \
if not timelineJson:
return blogRSS+rssFooter()
if pageNumber!=None:
if pageNumber!=None:
for item in timelineJson['orderedItems']:
if item['type']!='Create':
continue
@ -556,11 +556,11 @@ def htmlEditBlog(mediaInstance: bool,translate: {}, \
if os.path.isfile(baseDir+'/accounts/newpost.txt'):
with open(baseDir+'/accounts/newpost.txt', 'r') as file:
editBlogText='<p class="new-post-text">'+file.read()+'</p>'
editBlogText='<p class="new-post-text">'+file.read()+'</p>'
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
editBlogCSS=cssFile.read()
if httpPrefix!='https':
@ -606,7 +606,7 @@ def htmlEditBlog(mediaInstance: bool,translate: {}, \
editBlogForm=htmlHeader(cssFilename,editBlogCSS)
mentionsStr=''
editBlogForm+= \
'<form enctype="multipart/form-data" method="POST" accept-charset="UTF-8" action="'+ \
pathBase+'?'+endpoint+'?page='+str(pageNumber)+'">'

View File

@ -10,7 +10,7 @@ furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
* You and any organization you work for may not promote white supremacy, hate
* You and any organization you work for may not promote white supremacy, hate
speech and homo- or transphobia - this license is void if you do.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
@ -23,7 +23,7 @@ SOFTWARE.
https://github.com/halcy/blurhash-python
Pure python blurhash decoder with no additional dependencies, for
Pure python blurhash decoder with no additional dependencies, for
both de- and encoding.
Very close port of the original Swift implementation by Dag Ågren.
@ -47,13 +47,13 @@ def base83_decode(base83_str):
def base83_encode(value, length):
"""
Decodes an integer to a base83 string, as used in blurhash.
Length is how long the resulting string should be. Will complain
if the specified length is too short.
"""
if int(value) // (83 ** (length)) != 0:
raise ValueError("Specified length is too short to encode given value.")
result=""
for i in range(1,length+1):
digit=int(value) // (83 ** (length - i)) % 83
@ -90,44 +90,44 @@ def blurhash_components(blurhash):
"""
if len(blurhash) < 6:
raise ValueError("BlurHash must be at least 6 characters long.")
# Decode metadata
size_info=base83_decode(blurhash[0])
size_y=int(size_info / 9)+1
size_x=(size_info % 9)+1
return size_x, size_y
def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
"""
Decodes the given blurhash to an image of the specified size.
Returns the resulting image a list of lists of 3-value sRGB 8 bit integer
lists. Set linear to True if you would prefer to get linear floating point
lists. Set linear to True if you would prefer to get linear floating point
RGB back.
The punch parameter can be used to de- or increase the contrast of the
resulting image.
As per the original implementation it is suggested to only decode
to a relatively small size and then scale the result up, as it
basically looks the same anyways.
"""
if len(blurhash) < 6:
raise ValueError("BlurHash must be at least 6 characters long.")
# Decode metadata
size_info=base83_decode(blurhash[0])
size_y=int(size_info / 9)+1
size_x=(size_info % 9)+1
quant_max_value=base83_decode(blurhash[1])
real_max_value=(float(quant_max_value+1)/166.0)*punch
# Make sure we at least have the right number of characters
if len(blurhash) != 4+2*size_x*size_y:
raise ValueError("Invalid BlurHash length.")
# Decode DC component
dc_value=base83_decode(blurhash[2:6])
colours=[(
@ -135,7 +135,7 @@ def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
srgb_to_linear((dc_value >> 8) & 255),
srgb_to_linear(dc_value & 255)
)]
# Decode AC components
for component in range(1, size_x * size_y):
ac_value=base83_decode(blurhash[4+component*2:4+(component+1)*2])
@ -144,8 +144,8 @@ def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
sign_pow((float(int(ac_value / 19) % 19) - 9.0) / 9.0, 2.0) * real_max_value,
sign_pow((float(ac_value % 19) - 9.0) / 9.0, 2.0) * real_max_value
))
# Return image RGB values, as a list of lists of lists,
# Return image RGB values, as a list of lists of lists,
# consumable by something like numpy or PIL.
pixels=[]
for y in range(height):
@ -172,24 +172,24 @@ def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
pixel_row.append(pixel)
pixels.append(pixel_row)
return pixels
def blurhash_encode(image,components_x=4,components_y=4,linear=False):
"""
Calculates the blurhash for an image using the given x and y component counts.
Image should be a 3-dimensional array, with the first dimension being y, the second
being x, and the third being the three rgb components that are assumed to be 0-255
being x, and the third being the three rgb components that are assumed to be 0-255
srgb integers (incidentally, this is the format you will get from a PIL RGB image).
You can also pass in already linear data - to do this, set linear to True. This is
useful if you want to encode a version of your image resized to a smaller size (which
you should ideally do in linear colour).
"""
if components_x < 1 or components_x > 9 or components_y < 1 or components_y > 9:
if components_x < 1 or components_x > 9 or components_y < 1 or components_y > 9:
raise ValueError("x and y component counts must be between 1 and 9 inclusive.")
height=float(len(image))
width=float(len(image[0]))
# Convert to linear if neeeded
image_linear=[]
if linear==False:
@ -204,7 +204,7 @@ def blurhash_encode(image,components_x=4,components_y=4,linear=False):
image_linear.append(image_linear_line)
else:
image_linear=image
# Calculate components
components=[]
max_ac_component=0.0
@ -220,28 +220,28 @@ def blurhash_encode(image,components_x=4,components_y=4,linear=False):
component[0] += basis * image_linear[y][x][0]
component[1] += basis * image_linear[y][x][1]
component[2] += basis * image_linear[y][x][2]
component[0] /= (width * height)
component[1] /= (width * height)
component[2] /= (width * height)
components.append(component)
if not (i==0 and j==0):
max_ac_component= \
max(max_ac_component,abs(component[0]), \
abs(component[1]),abs(component[2]))
# Encode components
dc_value= \
(linear_to_srgb(components[0][0]) << 16)+ \
(linear_to_srgb(components[0][1]) << 8)+ \
linear_to_srgb(components[0][2])
quant_max_ac_component= \
int(max(0, min(82, math.floor(max_ac_component * 166 - 0.5))))
ac_component_norm_factor= \
float(quant_max_ac_component+1) / 166.0
ac_values=[]
for r, g, b in components[1:]:
ac_values.append(
@ -249,7 +249,7 @@ def blurhash_encode(image,components_x=4,components_y=4,linear=False):
int(max(0.0,min(18.0, math.floor(sign_pow(g / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 + \
int(max(0.0,min(18.0, math.floor(sign_pow(b / ac_component_norm_factor, 0.5) * 9.0 + 9.5))))
)
# Build final blurhash
blurhash=""
blurhash += base83_encode((components_x - 1) + (components_y - 1) * 9, 1)
@ -257,5 +257,5 @@ def blurhash_encode(image,components_x=4,components_y=4,linear=False):
blurhash += base83_encode(dc_value, 4)
for ac_value in ac_values:
blurhash += base83_encode(ac_value, 2)
return blurhash

View File

@ -167,7 +167,7 @@ def updateBookmarksCollection(recentPostsCache: {}, \
'items': [{
'type': 'Bookmark',
'actor': actor
}]
}]
}
postJsonObject['object']['bookmarks']=bookmarksJson
else:
@ -271,11 +271,11 @@ def bookmark(recentPostsCache: {}, \
print('DEBUG: bookmark domain: '+domain)
print('DEBUG: bookmark objectUrl: '+objectUrl)
return None
updateBookmarksCollection(recentPostsCache, \
baseDir,postFilename,objectUrl, \
newBookmarkJson['actor'],domain,debug)
sendSignedJson(newBookmarkJson,session,baseDir, \
nickname,domain,port, \
bookmarkedPostNickname,bookmarkedPostDomain,bookmarkedPostPort, \
@ -385,7 +385,7 @@ def undoBookmark(recentPostsCache: {}, \
undoBookmarksCollectionEntry(recentPostsCache, \
baseDir,postFilename,objectUrl, \
newBookmarkJson['actor'],domain,debug)
sendSignedJson(newUndoBookmarkJson,session,baseDir, \
nickname,domain,port, \
bookmarkedPostNickname,bookmarkedPostDomain,bookmarkedPostPort, \
@ -425,7 +425,7 @@ def undoBookmarkPost(session,baseDir: str,federationList: [], \
ccUrl= \
httpPrefix+'://'+bookmarkedomain+':'+ \
str(bookmarkPort)+'/users/'+bookmarkNickname
return undoBookmark(session,baseDir,federationList,nickname,domain,port, \
ccList,httpPrefix,objectUrl,clientToServer, \
sendThreads,postLog,personCache,cachedWebfingers,debug)
@ -453,7 +453,7 @@ def sendBookmarkViaServer(baseDir: str,session, \
if '/statuses/' in bookmarkUrl:
toUrl=[bookmarkUrl.split('/statuses/')[0]]
newBookmarkJson={
"@context": "https://www.w3.org/ns/activitystreams",
'type': 'Bookmark',
@ -478,7 +478,7 @@ def sendBookmarkViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -487,9 +487,9 @@ def sendBookmarkViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -559,7 +559,7 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -568,9 +568,9 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -618,7 +618,7 @@ def outboxBookmark(recentPostsCache: {}, \
return
if messageJson['to'][0]!=messageJson['actor']:
print('WARN: Bookmark should be addressed to the same actor')
return
return
if debug:
print('DEBUG: c2s bookmark request arrived in outbox')
@ -652,7 +652,7 @@ def outboxUndoBookmark(recentPostsCache: {}, \
if not isinstance(messageJson['object'], dict):
if debug:
print('DEBUG: undo bookmark object is not dict')
return
return
if not messageJson['object'].get('type'):
if debug:
print('DEBUG: undo bookmark - no type')
@ -677,7 +677,7 @@ def outboxUndoBookmark(recentPostsCache: {}, \
return
if messageJson['to'][0]!=messageJson['actor']:
print('WARN: Bookmark should be addressed to the same actor')
return
return
if debug:
print('DEBUG: c2s undo bookmark request arrived in outbox')

View File

@ -11,7 +11,7 @@ import time
import datetime
from utils import loadJson
from utils import saveJson
def storePersonInCache(baseDir: str,personUrl: str,personJson: {},personCache: {}) -> None:
"""Store an actor in the cache
"""
@ -41,7 +41,7 @@ def getPersonFromCache(baseDir: str,personUrl: str,personCache: {}) -> {}:
if personJson:
storePersonInCache(baseDir,personUrl,personJson,personCache)
loadedFromFile=True
if personCache.get(personUrl):
if not loadedFromFile:
# update the timestamp for the last time the actor was retrieved

View File

@ -82,7 +82,7 @@ def CapablePost(postJson: {}, capabilityList: [], debug :bool) -> bool:
if 'inbox:cw' in capabilityList:
if debug:
print('DEBUG: inbox post rejected because inbox:cw, summary missing')
return False
return False
if 'inbox:write' in capabilityList:
return True
return True
@ -102,7 +102,7 @@ def capabilitiesRequest(baseDir: str,httpPrefix: str,domain: str, \
"actor": requestedActor
}
return ocapRequest
def capabilitiesAccept(baseDir: str,httpPrefix: str, \
nickname: str,domain: str, port: int, \
acceptedActor: str, saveToFile: bool, \
@ -119,7 +119,7 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
if port!=80 and port !=443:
if ':' not in domain:
fullDomain=domain+':'+str(port)
# make directories to store capabilities
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,acceptedActor,'accept')
if not ocapFilename:
@ -129,14 +129,14 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
# if the capability already exists then load it from file
if os.path.isfile(ocapFilename):
ocapAccept=loadJson(ocapFilename)
# otherwise create a new capability
# otherwise create a new capability
if not ocapAccept:
acceptedActorNickname=getNicknameFromActor(acceptedActor)
if not acceptedActorNickname:
print('WARN: unable to find nickname in '+acceptedActor)
return None
acceptedActorDomain,acceptedActorPort=getDomainFromActor(acceptedActor)
if acceptedActorPort:
if acceptedActorPort:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+str(acceptedActorPort)+'#'+createPassword(32)
else:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+createPassword(32)
@ -185,7 +185,7 @@ def capabilitiesUpdate(baseDir: str,httpPrefix: str, \
if port!=80 and port !=443:
if ':' not in domain:
fullDomain=domain+':'+str(port)
# Get the filename of the capability
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept')
if not ocapFilename:

View File

@ -92,7 +92,7 @@ def replaceEmojiFromTags(content: str,tag: [],messageType: str) -> str:
htmlClass='emoji'
if messageType=='post header':
htmlClass='emojiheader'
htmlClass='emojiheader'
if messageType=='profile':
htmlClass='emojiprofile'
emojiHtml="<img src=\""+tagItem['icon']['url']+"\" alt=\""+tagItem['name'].replace(':','')+"\" align=\"middle\" class=\""+htmlClass+"\"/>"
@ -298,7 +298,7 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
return True
# @nick@domain
if not (possibleDomain=='localhost' or '.' in possibleDomain):
return False
return False
recipientActor=httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname
if recipientActor not in recipients:
recipients.append(recipientActor)
@ -389,7 +389,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
content=content.replace('\n',' --linebreak-- ')
content=addMusicTag(content,'nowplaying')
words=content.replace(',',' ').replace(';',' ').split(' ')
# remove . for words which are not mentions
wordCtr=0
newWords=[]
@ -461,7 +461,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
content=removeLongWords(content,maxWordLength,longWordsList)
content=content.replace(' --linebreak-- ','</p><p>')
return '<p>'+content+'</p>'
def getMentionsFromHtml(htmlText: str,matchStr="<span class=\"h-card\"><a href=\"") -> []:
"""Extracts mentioned actors from the given html content string
"""
@ -521,7 +521,7 @@ def saveMediaInFormPOST(mediaBytes,debug: bool, \
mediaLocation=-1
searchStr=''
filename=None
# directly search the binary array for the beginning
# of an image
extensionList= {
@ -579,7 +579,7 @@ def saveMediaInFormPOST(mediaBytes,debug: bool, \
def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
"""Returns a dictionary containing the text fields of a http form POST
The boundary argument comes from the http header
"""
"""
msg=email.parser.BytesParser().parsebytes(postBytes)
if debug:
print('DEBUG: POST arriving '+msg.get_payload(decode=True).decode('utf-8'))
@ -590,7 +590,7 @@ def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
if f=='--':
continue
if ' name="' not in f:
continue
continue
postStr=f.split(' name="',1)[1]
if '"' not in postStr:
continue
@ -600,7 +600,7 @@ def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
continue
if '\r\n' not in postValueStr:
continue
postLines=postValueStr.split('\r\n')
postLines=postValueStr.split('\r\n')
postValue=''
if len(postLines)>2:
for line in range(2,len(postLines)-1):

266
daemon.py

File diff suppressed because it is too large Load Diff

View File

@ -80,7 +80,7 @@ def createDelete(session,baseDir: str,federationList: [], \
'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix,True,clientToServer,federationList, \
sendThreads,postLog,cachedWebfingers,personCache,debug)
return newDelete
def sendDeleteViaServer(baseDir: str,session, \
@ -131,7 +131,7 @@ def sendDeleteViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -140,9 +140,9 @@ def sendDeleteViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -257,14 +257,14 @@ def outboxDelete(baseDir: str,httpPrefix: str, \
if deleteNickname!=nickname:
if debug:
print("DEBUG: you can't delete a post which wasn't created by you (nickname does not match)")
return
return
deleteDomain,deletePort=getDomainFromActor(messageId)
if ':' in domain:
domain=domain.split(':')[0]
if deleteDomain!=domain:
if debug:
print("DEBUG: you can't delete a post which wasn't created by you (domain does not match)")
return
return
removeModerationPostFromIndex(baseDir,messageId,debug)
postFilename=locatePost(baseDir,deleteNickname,deleteDomain,messageId)
if not postFilename:

View File

@ -12,7 +12,7 @@ def getDonationTypes() -> str:
return ('patreon','paypal','gofundme','liberapay', \
'kickstarter','indiegogo','crowdsupply', \
'subscribestar')
def getDonationUrl(actorJson: {}) -> str:
"""Returns a link used for donations
"""

View File

@ -354,7 +354,7 @@ if args.posts:
if args.postsraw:
if '@' not in args.postsraw:
print('Syntax: --postsraw nickname@domain')
sys.exit()
sys.exit()
if not args.http:
args.port=443
nickname=args.postsraw.split('@')[0]
@ -398,7 +398,7 @@ if not args.blogsinstance:
blogsInstance=getConfigParam(baseDir,'blogsInstance')
if blogsInstance!=None:
args.blogsinstance=blogsInstance
# set the instance title in config.json
title=getConfigParam(baseDir,'instanceTitle')
if not title:
@ -450,13 +450,13 @@ if not getConfigParam(baseDir,'registration'):
setConfigParam(baseDir,'maxRegistrations',str(maxRegistrations))
setConfigParam(baseDir,'registrationsRemaining',str(maxRegistrations))
if args.resetregistrations:
if args.resetregistrations:
setConfigParam(baseDir,'registrationsRemaining',str(maxRegistrations))
print('Number of new registrations reset to '+str(maxRegistrations))
# whether new registrations are open or closed
if args.registration:
if args.registration.lower()=='open':
if args.registration.lower()=='open':
registration=getConfigParam(baseDir,'registration')
if not registration:
setConfigParam(baseDir,'registrationsRemaining',str(maxRegistrations))
@ -468,7 +468,7 @@ if args.registration:
else:
setConfigParam(baseDir,'registration','closed')
print('New registrations closed')
# unique ID for the instance
instanceId=getConfigParam(baseDir,'instanceId')
if not instanceId:
@ -535,7 +535,7 @@ if args.approve:
if '@' not in args.approve:
print('syntax: --approve nick@domain')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
sendThreads=[]
postLog=[]
cachedWebfingers={}
@ -559,7 +559,7 @@ if args.deny:
if '@' not in args.deny:
print('syntax: --deny nick@domain')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
sendThreads=[]
postLog=[]
cachedWebfingers={}
@ -590,21 +590,21 @@ if args.followerspending:
if approveCtr==0:
print('There are no follow requests pending approval.')
sys.exit()
if args.message:
if not args.nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
if not args.sendto:
print('Specify an account to sent to: --sendto [nickname@domain]')
sys.exit()
sys.exit()
if '@' not in args.sendto and \
not args.sendto.lower().endswith('public') and \
not args.sendto.lower().endswith('followers'):
@ -628,7 +628,7 @@ if args.message:
toNickname=None
toDomain='public'
toPort=port
#ccUrl=httpPrefix+'://'+domain+'/users/'+nickname+'/followers'
ccUrl=None
sendMessage=args.message
@ -668,12 +668,12 @@ if args.announce:
if not args.nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending announce/repeat of '+args.announce)
@ -717,7 +717,7 @@ if args.itemName:
print('Specify a duration to share the object with the --duration option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending shared item: '+args.itemName)
@ -749,7 +749,7 @@ if args.undoItemName:
print('Specify a nickname with the --nickname option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending undo of shared item: '+args.undoItemName)
@ -770,12 +770,12 @@ if args.like:
if not args.nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending like of '+args.like)
@ -795,12 +795,12 @@ if args.undolike:
if not args.nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending undo like of '+args.undolike)
@ -820,12 +820,12 @@ if args.delete:
if not args.nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending delete request of '+args.delete)
@ -852,11 +852,11 @@ if args.follow:
if not args.password:
print('Please specify the password for '+args.nickname+' on '+domain)
sys.exit()
followNickname=getNicknameFromActor(args.follow)
if not followNickname:
print('Unable to find nickname in '+args.follow)
sys.exit()
sys.exit()
followDomain,followPort=getDomainFromActor(args.follow)
session=createSession(useTor)
@ -890,11 +890,11 @@ if args.unfollow:
if not args.password:
print('Please specify the password for '+args.nickname+' on '+domain)
sys.exit()
followNickname=getNicknameFromActor(args.unfollow)
if not followNickname:
print('WARN: unable to find nickname in '+args.unfollow)
sys.exit()
sys.exit()
followDomain,followPort=getDomainFromActor(args.unfollow)
session=createSession(useTor)
@ -927,7 +927,7 @@ if args.port:
if args.proxyPort:
proxyPort=args.proxyPort
setConfigParam(baseDir,'proxyPort',proxyPort)
ocapAlways=False
ocapAlways=False
if args.ocap:
ocapAlways=args.ocap
if args.dat:
@ -995,7 +995,7 @@ if args.actor:
personUrl=originalActor
else:
sys.exit()
asHeader={
'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'
}
@ -1041,13 +1041,13 @@ if args.addaccount:
sys.exit()
if not validNickname(domain,nickname):
print(nickname+' is a reserved name. Use something different.')
sys.exit()
sys.exit()
if not args.password:
print('Use the --password option to set the password for '+nickname)
sys.exit()
if len(args.password.strip())<8:
print('Password should be at least 8 characters')
sys.exit()
sys.exit()
if os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
print('Account already exists')
sys.exit()
@ -1198,7 +1198,7 @@ if args.avatar:
print('Avatar added for '+args.nickname)
else:
print('Avatar was not added for '+args.nickname)
sys.exit()
sys.exit()
if args.backgroundImage:
if not os.path.isfile(args.backgroundImage):
@ -1212,14 +1212,14 @@ if args.backgroundImage:
print('Background image added for '+args.nickname)
else:
print('Background image was not added for '+args.nickname)
sys.exit()
sys.exit()
if args.project:
if not args.delegate and not args.undelegate:
if not args.delegate and not args.undelegate:
if not nickname:
print('No nickname given')
sys.exit()
if args.role.lower()=='none' or \
args.role.lower()=='remove' or \
args.role.lower()=='delete':
@ -1236,7 +1236,7 @@ if args.skill:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
@ -1249,7 +1249,7 @@ if args.skill:
print('Skill level should be a percentage in the range 0-100')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending '+args.skill+' skill level '+str(args.skillLevelPercent)+' for '+nickname)
@ -1270,12 +1270,12 @@ if args.availability:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending availability status of '+nickname+' as '+args.availability)
@ -1305,7 +1305,7 @@ if args.block:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
@ -1320,7 +1320,7 @@ if args.block:
print(args.block+' does not look like an actor url')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending block of '+args.block)
@ -1339,7 +1339,7 @@ if args.delegate:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
@ -1356,7 +1356,7 @@ if args.delegate:
delegatedNickname=args.delegate.split('@')[0]
args.delegate=blockedActor
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending delegation for '+args.delegate+' with role '+args.role+' in project '+args.project)
@ -1377,7 +1377,7 @@ if args.undelegate:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
@ -1390,7 +1390,7 @@ if args.undelegate:
delegatedNickname=args.undelegate.split('@')[0]
args.undelegate=blockedActor
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending delegation removal for '+args.undelegate+' from role '+args.role+' in project '+args.project)
@ -1411,7 +1411,7 @@ if args.unblock:
if not nickname:
print('Specify a nickname with the --nickname option')
sys.exit()
if not args.password:
print('Specify a password with the --password option')
sys.exit()
@ -1426,7 +1426,7 @@ if args.unblock:
print(args.unblock+' does not look like an actor url')
sys.exit()
session=createSession(useTor)
session=createSession(useTor)
personCache={}
cachedWebfingers={}
print('Sending undo block of '+args.unblock)
@ -1458,7 +1458,7 @@ if args.unfilterStr:
sys.exit()
if args.testdata:
useBlurhash=False
useBlurhash=False
nickname='testuser567'
password='boringpassword'
print('Generating some test data for user: '+nickname)
@ -1475,7 +1475,7 @@ if args.testdata:
shutil.rmtree(baseDir+'/sharefiles')
if os.path.isdir(baseDir+'/wfendpoints'):
shutil.rmtree(baseDir+'/wfendpoints')
setConfigParam(baseDir,'registrationsRemaining',str(maxRegistrations))
createPerson(baseDir,'maxboardroom',domain,port,httpPrefix,True,password)
@ -1511,7 +1511,7 @@ if args.testdata:
"City", \
"3 months",
debug)
deleteAllPosts(baseDir,nickname,domain,'inbox')
deleteAllPosts(baseDir,nickname,domain,'outbox')
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a #test, man",False,True,False,None,None,useBlurhash)

View File

@ -74,4 +74,3 @@ def isFiltered(baseDir: str,nickname: str,domain: str,content: str) -> bool:
return False
return True
return False

View File

@ -88,7 +88,7 @@ def isFollowingActor(baseDir: str,nickname: str,domain: str,actor: str) -> bool:
handle=nickname+'@'+domain
if not os.path.isdir(baseDir+'/accounts/'+handle):
return False
followingFile=baseDir+'/accounts/'+handle+'/following.txt'
followingFile=baseDir+'/accounts/'+handle+'/following.txt'
if not os.path.isfile(followingFile):
return False
if actor in open(followingFile).read():
@ -284,7 +284,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \
return None
# handle page numbers
headerOnly=True
pageNumber=None
pageNumber=None
if '?page=' in path:
pageNumber=path.split('?page=')[1]
if pageNumber=='true' or not authenticated:
@ -296,7 +296,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \
pass
path=path.split('?page=')[0]
headerOnly=False
if not path.endswith('/'+followFile):
return None
nickname=None
@ -495,7 +495,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if debug:
print('DEBUG: "users" or "profile" missing from actor')
print('DEBUG: "users" or "profile" missing from actor')
return False
domain,tempPort=getDomainFromActor(messageJson['actor'])
fromPort=port
@ -534,7 +534,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
if tempPort:
if tempPort!=80 and tempPort!=443:
if ':' not in domainToFollow:
domainToFollowFull=domainToFollow+':'+str(tempPort)
domainToFollowFull=domainToFollow+':'+str(tempPort)
nicknameToFollow=getNicknameFromActor(messageJson['object'])
if not nicknameToFollow:
if debug:
@ -547,7 +547,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
print('DEBUG: followed account not found - '+ \
baseDir+'/accounts/'+handleToFollow)
return True
if isFollowerOfPerson(baseDir, \
nicknameToFollow,domainToFollowFull, \
nickname,domainFull):
@ -556,13 +556,13 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
' is already a follower of '+ \
nicknameToFollow+'@'+domainToFollow)
return True
# what is the followers policy?
approveHandle=nickname+'@'+domainFull
approveHandle=nickname+'@'+domainFull
if followApprovalRequired(baseDir,nicknameToFollow, \
domainToFollow,debug,approveHandle):
print('Follow approval is required')
if not domain.endswith('.onion'):
if not domain.endswith('.onion'):
if noOfFollowRequests(baseDir, \
nicknameToFollow,domainToFollow, \
nickname,domain,fromPort, \
@ -730,10 +730,10 @@ def sendFollowRequest(session,baseDir: str, \
personCache: {},debug : bool, \
projectVersion: str) -> {}:
"""Gets the json object for sending a follow request
"""
"""
if not domainPermitted(followDomain,federationList):
return None
fullDomain=domain
followActor=httpPrefix+'://'+domain+'/users/'+nickname
if port:
@ -749,7 +749,7 @@ def sendFollowRequest(session,baseDir: str, \
requestDomain=followDomain+':'+str(followPort)
statusNumber,published=getStatusNumber()
if followNickname:
followedId=followHttpPrefix+'://'+requestDomain+'/users/'+followNickname
followHandle=followNickname+'@'+requestDomain
@ -812,7 +812,7 @@ def sendFollowRequestViaServer(baseDir: str,session, \
if ':' not in followDomain:
followDomainFull=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname
statusNumber,published=getStatusNumber()
@ -842,7 +842,7 @@ def sendFollowRequestViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -851,9 +851,9 @@ def sendFollowRequestViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -895,7 +895,7 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
if ':' not in followDomain:
followDomainFull=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname
statusNumber,published=getStatusNumber()
@ -930,7 +930,7 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -939,9 +939,9 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -1008,7 +1008,7 @@ def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}:
if ocapJson.get('id'):
if debug:
print('DEBUG: capabilities id found for '+account)
recipientsDict[account]=ocapJson['id']
else:
if debug:
@ -1058,7 +1058,7 @@ def outboxUndoFollow(baseDir: str,messageJson: {},debug: bool) -> None:
if portFollower!=80 and portFollower!=443:
if ':' not in domainFollower:
domainFollowerFull=domainFollower+':'+str(portFollower)
nicknameFollowing=getNicknameFromActor(messageJson['object']['object'])
if not nicknameFollowing:
print('WARN: unable to find nickname in '+messageJson['object']['object'])

View File

@ -67,7 +67,7 @@ def getTodaysEvents(baseDir: str,nickname: str,domain: str, \
calendarPostIds=[]
recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile:
with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile:
postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId)
@ -134,7 +134,7 @@ def todaysEventsCheck(baseDir: str,nickname: str,domain: str) -> bool:
return False
eventsExist=False
with open(calendarFilename,'r') as eventsFile:
with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile:
postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId)
@ -180,7 +180,7 @@ def thisWeeksEventsCheck(baseDir: str,nickname: str,domain: str) -> bool:
return False
eventsExist=False
with open(calendarFilename,'r') as eventsFile:
with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile:
postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId)
@ -207,7 +207,7 @@ def thisWeeksEventsCheck(baseDir: str,nickname: str,domain: str) -> bool:
int(eventTime.strftime("%m"))==monthNumber and \
(int(eventTime.strftime("%d"))>dayNumber and \
int(eventTime.strftime("%d"))<=dayNumber+6):
eventsExist=True
eventsExist=True
break
return eventsExist
@ -233,7 +233,7 @@ def getThisWeeksEvents(baseDir: str,nickname: str,domain: str) -> {}:
calendarPostIds=[]
recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile:
with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile:
postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId)
@ -317,7 +317,7 @@ def getCalendarEvents(baseDir: str,nickname: str,domain: str, \
calendarPostIds=[]
recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile:
with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile:
postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId)
@ -362,7 +362,7 @@ def getCalendarEvents(baseDir: str,nickname: str,domain: str, \
for postId in calendarPostIds:
calendarFile.write(postId+'\n')
calendarFile.close()
return events
def removeCalendarEvent(baseDir: str,nickname: str,domain: str, \

View File

@ -9,7 +9,7 @@ __status__="Production"
# see https://tools.ietf.org/html/draft-cavage-http-signatures-06
try:
try:
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256
from Cryptodome.Signature import pkcs1_15
@ -175,7 +175,7 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
if debug:
print('DEBUG: verifyPostHeaders '+method)
publicKeyPem=RSA.import_key(publicKeyPem)
# Build a dictionary of the signature values
signatureHeader=headers['signature']

View File

@ -79,7 +79,7 @@ def storeHashTags(baseDir: str,nickname: str,postJsonObject: {}) -> None:
return
if not isinstance(postJsonObject['object']['tag'], list):
return
tagsDir=baseDir+'/tags'
tagsDir=baseDir+'/tags'
for tag in postJsonObject['object']['tag']:
if not tag.get('type'):
continue
@ -149,7 +149,7 @@ def validInbox(baseDir: str,nickname: str,domain: str) -> bool:
if 'postNickname' in open(filename).read():
print('queue file incorrectly saved to '+filename)
return False
return True
return True
def validInboxFilenames(baseDir: str,nickname: str,domain: str, \
expectedDomain: str,expectedPort: int) -> bool:
@ -172,7 +172,7 @@ def validInboxFilenames(baseDir: str,nickname: str,domain: str, \
print('Expected: '+expectedStr)
print('Invalid filename: '+filename)
return False
return True
return True
def getPersonPubKey(baseDir: str,session,personUrl: str, \
personCache: {},debug: bool, \
@ -184,7 +184,7 @@ def getPersonPubKey(baseDir: str,session,personUrl: str, \
if personUrl.endswith('/users/inbox'):
if debug:
print('DEBUG: Obtaining public key for shared inbox')
personUrl=personUrl.replace('/users/inbox','/inbox')
personUrl=personUrl.replace('/users/inbox','/inbox')
personJson=getPersonFromCache(baseDir,personUrl,personCache)
if not personJson:
if debug:
@ -345,9 +345,9 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str, \
postId=actor+'/statuses/'+statusNumber
else:
postId=httpPrefix+'://'+originalDomain+'/users/'+nickname+'/statuses/'+statusNumber
# NOTE: don't change postJsonObject['id'] before signature check
inboxQueueDir=createInboxQueueDir(nickname,domain,baseDir)
handle=nickname+'@'+domain
@ -418,7 +418,7 @@ def inboxCheckCapabilities(baseDir :str,nickname :str,domain :str, \
return False
oc=loadJson(ocapFilename)
if not oc:
if not oc:
return False
if not oc.get('id'):
@ -533,7 +533,7 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{}, \
actor=postJsonObject['actor']
# first get any specific people which the post is addressed to
followerRecipients=False
if postJsonObject.get('object'):
if isinstance(postJsonObject['object'], dict):
@ -649,7 +649,7 @@ def receiveUndoFollow(session,baseDir: str,httpPrefix: str, \
if portFollower!=80 and portFollower!=443:
if ':' not in domainFollower:
domainFollowerFull=domainFollower+':'+str(portFollower)
nicknameFollowing=getNicknameFromActor(messageJson['object']['object'])
if not nicknameFollowing:
print('WARN: unable to find nickname in '+messageJson['object']['object'])
@ -668,7 +668,7 @@ def receiveUndoFollow(session,baseDir: str,httpPrefix: str, \
if debug:
print('DEBUG: Follower '+nicknameFollower+'@'+domainFollowerFull+' was removed')
return True
if debug:
print('DEBUG: Follower '+nicknameFollower+'@'+domainFollowerFull+' was not removed')
return False
@ -693,7 +693,7 @@ def receiveUndo(session,baseDir: str,httpPrefix: str, \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if debug:
print('DEBUG: "users" or "profile" missing from actor')
print('DEBUG: "users" or "profile" missing from actor')
return False
if not messageJson.get('object'):
if debug:
@ -755,11 +755,11 @@ def personReceiveUpdate(baseDir: str, \
return False
if not personJson.get('publicKey'):
if debug:
print('DEBUG: actor update does not contain a public key')
print('DEBUG: actor update does not contain a public key')
return False
if not personJson['publicKey'].get('publicKeyPem'):
if debug:
print('DEBUG: actor update does not contain a public key Pem')
print('DEBUG: actor update does not contain a public key Pem')
return False
actorFilename=baseDir+'/cache/actors/'+personJson['id'].replace('/','#')+'.json'
# check that the public keys match.
@ -824,7 +824,7 @@ def receiveUpdateToQuestion(recentPostsCache: {},messageJson: {}, \
os.remove(cachedPostFilename)
# remove from memory cache
removePostFromCache(messageJson,recentPostsCache)
def receiveUpdate(recentPostsCache: {},session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \
@ -878,7 +878,7 @@ def receiveUpdate(recentPostsCache: {},session,baseDir: str, \
if debug:
print('DEBUG: Unwrapped profile update was received for '+messageJson['url'])
return True
if messageJson['object']['type']=='Person' or \
messageJson['object']['type']=='Application' or \
messageJson['object']['type']=='Group' or \
@ -1056,11 +1056,11 @@ def receiveBookmark(recentPostsCache: {}, \
if domain not in handle.split('@')[1]:
if debug:
print('DEBUG: unrecognized domain '+handle)
return False
return False
domainFull=domain
if port:
if port!=80 and port!=443:
domainFull=domain+':'+str(port)
domainFull=domain+':'+str(port)
nickname=handle.split('@')[0]
if not messageJson['actor'].endswith(domainFull+'/users/'+nickname):
if debug:
@ -1121,12 +1121,12 @@ def receiveUndoBookmark(recentPostsCache: {}, \
domainFull=domain
if port:
if port!=80 and port!=443:
domainFull=domain+':'+str(port)
domainFull=domain+':'+str(port)
nickname=handle.split('@')[0]
if domain not in handle.split('@')[1]:
if debug:
print('DEBUG: unrecognized bookmark domain '+handle)
return False
return False
if not messageJson['actor'].endswith(domainFull+'/users/'+nickname):
if debug:
print('DEBUG: bookmark actor should be the same as the handle sent to '+handle+' != '+messageJson['actor'])
@ -1180,7 +1180,7 @@ def receiveDelete(session,handle: str,isGroup: bool,baseDir: str, \
not messageJson['actor'].startswith(deletePrefix)):
if debug:
print('DEBUG: delete not permitted from other instances')
return False
return False
if not messageJson.get('to'):
if debug:
print('DEBUG: '+messageJson['type']+' has no "to" list')
@ -1197,9 +1197,9 @@ def receiveDelete(session,handle: str,isGroup: bool,baseDir: str, \
return False
if messageJson['actor'] not in messageJson['object']:
if debug:
print('DEBUG: actor is not the owner of the post to be deleted')
print('DEBUG: actor is not the owner of the post to be deleted')
if not os.path.isdir(baseDir+'/accounts/'+handle):
print('DEBUG: unknown recipient of like - '+handle)
print('DEBUG: unknown recipient of like - '+handle)
# if this post in the outbox of the person?
messageId=messageJson['object'].replace('/activity','').replace('/undo','')
removeModerationPostFromIndex(baseDir,messageId,debug)
@ -1227,7 +1227,7 @@ def receiveAnnounce(recentPostsCache: {}, \
if '@' not in handle:
if debug:
print('DEBUG: bad handle '+handle)
return False
return False
if not messageJson.get('actor'):
if debug:
print('DEBUG: '+messageJson['type']+' has no actor')
@ -1308,7 +1308,7 @@ def receiveAnnounce(recentPostsCache: {}, \
getPersonPubKey(baseDir,session,lookupActor, \
personCache,debug, \
__version__,httpPrefix, \
domain,onionDomain)
domain,onionDomain)
if pubKey:
print('DEBUG: public key obtained for announce: '+lookupActor)
break
@ -1316,7 +1316,7 @@ def receiveAnnounce(recentPostsCache: {}, \
if debug:
print('DEBUG: Retry '+str(tries+1)+ \
' obtaining actor for '+lookupActor)
time.sleep(5)
time.sleep(5)
if debug:
print('DEBUG: announced/repeated post arrived in inbox')
return True
@ -1342,7 +1342,7 @@ def receiveUndoAnnounce(recentPostsCache: {}, \
if not isinstance(messageJson['object']['object'], str):
return False
if messageJson['object']['type']!='Announce':
return False
return False
if '/users/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
@ -1367,7 +1367,7 @@ def receiveUndoAnnounce(recentPostsCache: {}, \
if postJsonObject['type']!='Announce':
if debug:
print("DEBUG: Attempt to undo something which isn't an announcement")
return False
return False
undoAnnounceCollectionEntry(recentPostsCache,baseDir,postFilename, \
messageJson['actor'],domain,debug)
if os.path.isfile(postFilename):
@ -1376,7 +1376,7 @@ def receiveUndoAnnounce(recentPostsCache: {}, \
def populateReplies(baseDir :str,httpPrefix :str,domain :str, \
messageJson :{},maxReplies: int,debug :bool) -> bool:
"""Updates the list of replies for a post on this domain if
"""Updates the list of replies for a post on this domain if
a reply to it arrives
"""
if not messageJson.get('id'):
@ -1412,7 +1412,7 @@ def populateReplies(baseDir :str,httpPrefix :str,domain :str, \
if not postFilename:
if debug:
print('DEBUG: post may have expired - '+replyTo)
return False
return False
# populate a text file containing the ids of replies
postRepliesFilename=postFilename.replace('.json','.replies')
messageId=messageJson['id'].replace('/activity','').replace('/undo','')
@ -1461,7 +1461,7 @@ def validPostContent(baseDir: str,nickname: str,domain: str, \
if 'Z' not in messageJson['object']['published']:
return False
# check for bad html
invalidStrings=['<script>','<canvas>','<style>','</html>','</body>','<br>','<hr>']
invalidStrings=['<script>','<canvas>','<style>','</html>','</body>','<br>','<hr>']
for badStr in invalidStrings:
if badStr in messageJson['object']['content']:
if messageJson['object'].get('id'):
@ -1504,7 +1504,7 @@ def obtainAvatarForReplyPost(session,baseDir: str,httpPrefix: str, \
"""
if not postJsonObject.get('object'):
return
if not isinstance(postJsonObject['object'], dict):
return
@ -1522,7 +1522,7 @@ def obtainAvatarForReplyPost(session,baseDir: str,httpPrefix: str, \
if '/statuses/' in lookupActor:
lookupActor=lookupActor.split('/statuses/')[0]
if debug:
print('DEBUG: Obtaining actor for reply post '+lookupActor)
@ -1531,7 +1531,7 @@ def obtainAvatarForReplyPost(session,baseDir: str,httpPrefix: str, \
getPersonPubKey(baseDir,session,lookupActor, \
personCache,debug, \
__version__,httpPrefix, \
domain,onionDomain)
domain,onionDomain)
if pubKey:
print('DEBUG: public key obtained for reply: '+lookupActor)
break
@ -1539,7 +1539,7 @@ def obtainAvatarForReplyPost(session,baseDir: str,httpPrefix: str, \
if debug:
print('DEBUG: Retry '+str(tries+1)+ \
' obtaining actor for '+lookupActor)
time.sleep(5)
time.sleep(5)
def dmNotify(baseDir: str,handle: str,url: str) -> None:
"""Creates a notification that a new DM has arrived
@ -1687,7 +1687,7 @@ def inboxUpdateCalendar(baseDir: str,handle: str,postJsonObject: {}) -> None:
if not tagDict.get('startTime'):
continue
# get the year and month from the event
eventTime=datetime.datetime.strptime(tagDict['startTime'],"%Y-%m-%dT%H:%M:%S%z")
eventTime=datetime.datetime.strptime(tagDict['startTime'],"%Y-%m-%dT%H:%M:%S%z")
eventYear=int(eventTime.strftime("%Y"))
eventMonthNumber=int(eventTime.strftime("%m"))
eventDayOfMonth=int(eventTime.strftime("%d"))
@ -1819,7 +1819,7 @@ def inboxAfterCapabilities(recentPostsCache: {},maxRecentPosts: int, \
if debug:
print('DEBUG: Undo bookmark accepted from '+actor)
return False
if receiveAnnounce(recentPostsCache, \
session,handle,isGroup, \
baseDir,httpPrefix, \
@ -2004,7 +2004,7 @@ def runInboxQueueWatchdog(projectVersion: str,httpd) -> None:
#httpd.thrInboxQueue=inboxQueueOriginal
httpd.thrInboxQueue.start()
while True:
time.sleep(20)
time.sleep(20)
if not httpd.thrInboxQueue.isAlive():
httpd.thrInboxQueue.kill()
httpd.thrInboxQueue=inboxQueueOriginal.clone(runInboxQueue)
@ -2068,7 +2068,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
if not session or currTime-sessionLastUpdate>1200:
print('Creating inbox session')
session=createSession(useTor)
sessionLastUpdate=currTime
sessionLastUpdate=currTime
# oldest item first
queue.sort()
@ -2081,7 +2081,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
continue
print('Loading queue item '+queueFilename)
# Load the queue json
queueJson=loadJson(queueFilename,1)
if not queueJson:
@ -2096,14 +2096,14 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
except:
pass
continue
# clear the daily quotas for maximum numbers of received posts
if currTime-quotasLastUpdate>60*60*24:
quotas={
'domains': {},
'accounts': {}
}
quotasLastUpdate=currTime
quotasLastUpdate=currTime
# limit the number of posts which can arrive per domain per day
postDomain=queueJson['postDomain']
@ -2138,7 +2138,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
pprint(quotas)
print('Obtaining public key for actor '+queueJson['actor'])
# Try a few times to obtain the public key
pubKey=None
keyId=None
@ -2169,7 +2169,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
if debug:
print('DEBUG: public key: '+str(pubKey))
break
if debug:
print('DEBUG: Retry '+str(tries+1)+ \
' obtaining public key for '+keyId)
@ -2210,7 +2210,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
# This makes the filename and the id consistent
#if queueJson['post'].get('id'):
# queueJson['post']['id']=queueJson['id']
if receiveUndo(session, \
baseDir,httpPrefix,port, \
sendThreads,postLog, \
@ -2344,7 +2344,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
saveJson(queueJson['post'],sharedInboxPostFilename)
# for posts addressed to specific accounts
for handle,capsId in recipientsDict.items():
for handle,capsId in recipientsDict.items():
destination=queueJson['destination'].replace(inboxHandle,handle)
# check that capabilities are accepted
if queueJson['post'].get('capability'):
@ -2395,7 +2395,7 @@ def runInboxQueue(recentPostsCache: {},maxRecentPosts: int, \
print('No capability list within post')
print('ocapAlways: '+str(ocapAlways))
print('DEBUG: object capabilities check failed')
if debug:
print('DEBUG: Queue post accepted')
if os.path.isfile(queueFilename):

26
like.py
View File

@ -143,7 +143,7 @@ def updateLikesCollection(recentPostsCache: {}, \
'items': [{
'type': 'Like',
'actor': actor
}]
}]
}
postJsonObject['object']['likes']=likesJson
else:
@ -225,11 +225,11 @@ def like(recentPostsCache: {}, \
print('DEBUG: like domain: '+domain)
print('DEBUG: like objectUrl: '+objectUrl)
return None
updateLikesCollection(recentPostsCache, \
baseDir,postFilename,objectUrl, \
newLikeJson['actor'],domain,debug)
sendSignedJson(newLikeJson,session,baseDir, \
nickname,domain,port, \
likedPostNickname,likedPostDomain,likedPostPort, \
@ -338,7 +338,7 @@ def undolike(recentPostsCache: {}, \
undoLikesCollectionEntry(baseDir,postFilename,objectUrl, \
newLikeJson['actor'],domain,debug)
sendSignedJson(newUndoLikeJson,session,baseDir, \
nickname,domain,port, \
likedPostNickname,likedPostDomain,likedPostPort, \
@ -379,7 +379,7 @@ def undoLikePost(recentPostsCache: {}, \
ccUrl= \
httpPrefix+'://'+likeDomain+':'+ \
str(likePort)+'/users/'+likeNickname
return undoLike(recentPostsCache, \
session,baseDir,federationList,nickname,domain,port, \
ccList,httpPrefix,objectUrl,clientToServer, \
@ -408,7 +408,7 @@ def sendLikeViaServer(baseDir: str,session, \
if '/statuses/' in likeUrl:
toUrl=[likeUrl.split('/statuses/')[0]]
newLikeJson={
"@context": "https://www.w3.org/ns/activitystreams",
'type': 'Like',
@ -433,7 +433,7 @@ def sendLikeViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -442,9 +442,9 @@ def sendLikeViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -515,7 +515,7 @@ def sendUndoLikeViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -524,9 +524,9 @@ def sendUndoLikeViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -598,7 +598,7 @@ def outboxUndoLike(baseDir: str,httpPrefix: str, \
if not isinstance(messageJson['object'], dict):
if debug:
print('DEBUG: undo like object is not dict')
return
return
if not messageJson['object'].get('type'):
if debug:
print('DEBUG: undo like - no type')

View File

@ -33,17 +33,17 @@ def manualDenyFollowRequest(session,baseDir: str, \
rejectedFollowsFilename=accountsDir+'/followrejects.txt'
if os.path.isfile(rejectedFollowsFilename):
if denyHandle in open(rejectedFollowsFilename).read():
removeFromFollowRequests(baseDir,nickname,domain,denyHandle,debug)
removeFromFollowRequests(baseDir,nickname,domain,denyHandle,debug)
print(denyHandle+' has already been rejected as a follower of '+nickname)
return
removeFromFollowRequests(baseDir,nickname,domain,denyHandle,debug)
removeFromFollowRequests(baseDir,nickname,domain,denyHandle,debug)
# Store rejected follows
rejectsFile=open(rejectedFollowsFilename, "a+")
rejectsFile.write(denyHandle+'\n')
rejectsFile.close()
denyNickname=denyHandle.split('@')[0]
denyDomain=denyHandle.split('@')[1].replace('\n','')
denyPort=port
@ -59,11 +59,11 @@ def manualDenyFollowRequest(session,baseDir: str, \
debug,projectVersion)
print('Follow request from '+denyHandle+' was denied.')
def approveFollowerHandle(accountDir: str,approveHandle: str) -> None:
""" Record manually approved handles so that if they unfollow and then
re-follow later then they don't need to be manually approved again
"""
"""
approvedFilename=accountDir+'/approved.txt'
if os.path.isfile(approvedFilename):
if approveHandle not in open(approvedFilename).read():
@ -73,8 +73,8 @@ def approveFollowerHandle(accountDir: str,approveHandle: str) -> None:
else:
approvedFile=open(approvedFilename, "w+")
approvedFile.write(approveHandle+'\n')
approvedFile.close()
approvedFile.close()
def manualApproveFollowRequest(session,baseDir: str, \
httpPrefix: str,
nickname: str,domain: str,port: int, \
@ -98,7 +98,7 @@ def manualApproveFollowRequest(session,baseDir: str, \
if approveHandle not in open(approveFollowsFilename).read():
print('Manual follow accept: '+approveHandle+' not in requests file '+approveFollowsFilename)
return
approvefilenew=open(approveFollowsFilename+'.new', 'w+')
updateApprovedFollowers=False
followActivityfilename=None

View File

@ -49,13 +49,13 @@ def getImageHash(imageFilename: str) -> str:
def isMedia(imageFilename: str) -> bool:
permittedMedia=['png','jpg','gif','webp','mp4','ogv','mp3','ogg']
for m in permittedMedia:
for m in permittedMedia:
if imageFilename.endswith('.'+m):
return True
print('WARN: '+imageFilename+' is not a permitted media type')
return False
def createMediaDirs(baseDir: str,mediaPath: str) -> None:
def createMediaDirs(baseDir: str,mediaPath: str) -> None:
if not os.path.isdir(baseDir+'/media'):
os.mkdir(baseDir+'/media')
if not os.path.isdir(baseDir+'/'+mediaPath):
@ -100,7 +100,7 @@ def updateEtag(mediaFilename: str) -> None:
data=None
try:
with open(mediaFilename, 'rb') as mediaFile:
data=mediaFile.read()
data=mediaFile.read()
except:
pass
@ -125,7 +125,7 @@ def attachMedia(baseDir: str,httpPrefix: str,domain: str,port: int, \
"""
if not isMedia(imageFilename):
return postJson
fileExtension=None
acceptedTypes=['png','jpg','gif','webp','mp4','webm','ogv','mp3','ogg']
for mType in acceptedTypes:
@ -135,7 +135,7 @@ def attachMedia(baseDir: str,httpPrefix: str,domain: str,port: int, \
if mType=='mp3':
mType='mpeg'
fileExtension=mType
if not fileExtension:
if not fileExtension:
return postJson
mediaType=mediaType+'/'+fileExtension
print('Attached media type: '+mediaType)
@ -189,7 +189,7 @@ def archiveMedia(baseDir: str,archiveDirectory: str,maxWeeks=4) -> None:
os.mkdir(archiveDirectory)
if not os.path.isdir(archiveDirectory+'/media'):
os.mkdir(archiveDirectory+'/media')
for subdir, dirs, files in os.walk(baseDir+'/media'):
for weekDir in dirs:
if int(weekDir)<minWeek:

View File

@ -58,7 +58,7 @@ def metaDataInstance(instanceTitle: str, \
isBot=False
if adminActor['type']!='Person':
isBot=True
instance={
'approval_required': False,
'contact_account': {
@ -98,5 +98,5 @@ def metaDataInstance(instanceTitle: str, \
'urls': {},
'version': version
}
return instance

View File

@ -272,7 +272,7 @@ def postMessageToOutbox(messageJson: {},postToNickname: str, \
messageJson,debug)
if debug:
print('DEBUG: handle delete requests')
print('DEBUG: handle delete requests')
outboxDelete(baseDir,httpPrefix, \
postToNickname,domain, \
messageJson,debug, \

View File

@ -39,7 +39,7 @@ from utils import loadJson
from utils import saveJson
from auth import createPassword
from config import setConfigParam
from config import getConfigParam
from config import getConfigParam
def generateRSAKey() -> (str,str):
key=RSA.generate(2048)
@ -85,7 +85,7 @@ def setProfileImage(baseDir: str,httpPrefix :str,nickname: str,domain: str, \
iconFilenameBase='icon'
else:
iconFilenameBase='image'
mediaType='image/png'
iconFilename=iconFilenameBase+'.png'
if imageFilename.endswith('.jpg') or \
@ -103,7 +103,7 @@ def setProfileImage(baseDir: str,httpPrefix :str,nickname: str,domain: str, \
personJson[iconFilenameBase]['url']= \
httpPrefix+'://'+fullDomain+'/users/'+nickname+'/'+iconFilename
saveJson(personJson,personFilename)
cmd= \
'/usr/bin/convert '+imageFilename+' -size '+ \
resolution+' -quality 50 '+profileFilename
@ -365,20 +365,20 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \
os.mkdir(baseDir+'/accounts')
if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
os.mkdir(baseDir+'/accounts/'+nickname+'@'+domain)
if os.path.isfile(baseDir+'/img/default-avatar.png'):
copyfile(baseDir+'/img/default-avatar.png',baseDir+'/accounts/'+nickname+'@'+domain+'/avatar.png')
theme=getConfigParam(baseDir,'theme')
defaultProfileImageFilename=baseDir+'/img/image.png'
if theme:
if os.path.isfile(baseDir+'/img/image_'+theme+'.png'):
defaultBannerFilename=baseDir+'/img/image_'+theme+'.png'
defaultBannerFilename=baseDir+'/img/image_'+theme+'.png'
if os.path.isfile(defaultProfileImageFilename):
copyfile(defaultProfileImageFilename,baseDir+'/accounts/'+nickname+'@'+domain+'/image.png')
defaultBannerFilename=baseDir+'/img/banner.png'
if theme:
if os.path.isfile(baseDir+'/img/banner_'+theme+'.png'):
defaultBannerFilename=baseDir+'/img/banner_'+theme+'.png'
defaultBannerFilename=baseDir+'/img/banner_'+theme+'.png'
if os.path.isfile(defaultBannerFilename):
copyfile(defaultBannerFilename,baseDir+'/accounts/'+nickname+'@'+domain+'/banner.png')
if remainingConfigExists:
@ -405,7 +405,7 @@ def personUpgradeActor(baseDir: str,personJson: {},handle: str,filename: str) ->
if not os.path.isfile(filename):
print('WARN: actor file not found '+filename)
return
if not personJson:
if not personJson:
personJson=loadJson(filename)
if not personJson.get('nomadicLocations'):
personJson['nomadicLocations']=[{
@ -415,7 +415,7 @@ def personUpgradeActor(baseDir: str,personJson: {},handle: str,filename: str) ->
'locationPrimary':True,
'locationDeleted':False
}]
print('Nomadic locations added to to actor '+handle)
print('Nomadic locations added to to actor '+handle)
updateActor=True
if updateActor:
@ -450,7 +450,7 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}:
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
'/followers','/following','/featured', \
'.png','.jpg','.gif','.mpv']
for ending in notPersonLookup:
for ending in notPersonLookup:
if path.endswith(ending):
return None
nickname=None
@ -494,7 +494,7 @@ def personBoxJson(recentPostsCache: {}, \
headerOnly=True
# handle page numbers
pageNumber=None
pageNumber=None
if '?page=' in path:
pageNumber=path.split('?page=')[1]
if pageNumber=='true':
@ -558,7 +558,7 @@ def personInboxJson(recentPostsCache: {}, \
headerOnly=True
# handle page numbers
pageNumber=None
pageNumber=None
if '?page=' in path:
pageNumber=path.split('?page=')[1]
if pageNumber=='true':
@ -594,7 +594,7 @@ def setDisplayNickname(baseDir: str,nickname: str, domain: str, \
if not os.path.isfile(filename):
return False
personJson=loadJson(filename)
personJson=loadJson(filename)
if not personJson:
return False
personJson['name']=displayName
@ -616,7 +616,7 @@ def setBio(baseDir: str,nickname: str, domain: str, bio: str) -> bool:
return False
personJson['summary']=bio
saveJson(personJson,filename)
saveJson(personJson,filename)
return True
def isSuspended(baseDir: str,nickname: str) -> bool:
@ -672,7 +672,7 @@ def suspendAccount(baseDir: str,nickname: str,domain: str) -> None:
tokenFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/.token'
if os.path.isfile(tokenFilename):
os.remove(tokenFilename)
suspendedFilename=baseDir+'/accounts/suspended.txt'
if os.path.isfile(suspendedFilename):
with open(suspendedFilename, "r") as f:
@ -746,11 +746,11 @@ def removeTagsForNickname(baseDir: str,nickname: str,domain: str,port: int) -> N
for tagline in lines:
if matchStr not in tagline:
tagFile.write(tagline)
tagFile.close()
tagFile.close()
def removeAccount(baseDir: str,nickname: str,domain: str,port: int) -> bool:
"""Removes an account
"""
"""
# Don't remove the admin
adminNickname=getConfigParam(baseDir,'admin')
if nickname==adminNickname:

122
posts.py
View File

@ -52,7 +52,7 @@ from auth import createBasicAuthHeader
from config import getConfigParam
from blocking import isBlocked
from filters import isFiltered
#try:
#try:
# from BeautifulSoup import BeautifulSoup
#except ImportError:
# from bs4 import BeautifulSoup
@ -114,7 +114,7 @@ def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public', \
print('DEBUG: private key was too short: '+keyPem)
return ''
return keyPem
def cleanHtml(rawHtml: str) -> str:
#text=BeautifulSoup(rawHtml, 'html.parser').get_text()
text=rawHtml
@ -159,8 +159,8 @@ def parseUserFeed(session,feedUrl: str,asHeader: {}, \
userFeed=nextUrl
if userFeed.get('orderedItems'):
for item in userFeed['orderedItems']:
yield item
yield item
def getPersonBox(baseDir: str,session,wfRequest: {},personCache: {}, \
projectVersion: str,httpPrefix: str, \
nickname: str,domain: str, \
@ -251,7 +251,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \
projectVersion: str,httpPrefix: str,domain: str) -> {}:
"""Gets public posts from an outbox
"""
personPosts={}
personPosts={}
if not outboxUrl:
return personPosts
asHeader={
@ -315,7 +315,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \
break
if not isPublic:
continue
content=item['object']['content'].replace('&apos;',"'")
mentions=[]
@ -371,7 +371,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \
if item['object']['conversation']:
# no conversations originated in non-permitted domains
if urlPermitted(item['object']['conversation'], \
federationList,"objects:read"):
federationList,"objects:read"):
conversation=item['object']['conversation']
attachment=[]
@ -438,7 +438,7 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \
if boxname!='inbox' and boxname!='outbox' and \
boxname!='tlblogs' and boxname!='scheduled':
return None
originalDomain=domain
originalDomain=domain
if ':' in domain:
domain=domain.split(':')[0]
@ -452,7 +452,7 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \
if isinstance(postJsonObject['object'], dict):
postJsonObject['object']['id']=postId
postJsonObject['object']['atomUri']=postId
boxDir=createPersonDir(nickname,domain,baseDir,boxname)
filename=boxDir+'/'+postId.replace('/','#')+'.json'
saveJson(postJsonObject,filename)
@ -465,7 +465,7 @@ def updateHashtagsIndex(baseDir: str,tag: {},newPostId: str) -> None:
if tag['type']!='Hashtag':
return
# create hashtags directory
# create hashtags directory
tagsDir=baseDir+'/tags'
if not os.path.isdir(tagsDir):
os.mkdir(tagsDir)
@ -514,7 +514,7 @@ def addSchedulePost(baseDir: str,nickname: str,domain: str, \
scheduleFile=open(scheduleIndexFilename,'w')
if scheduleFile:
scheduleFile.write(indexStr+'\n')
scheduleFile.close()
scheduleFile.close()
def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str,content: str, \
@ -583,7 +583,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
if not isinstance(toUrl, str):
print('ERROR: toUrl is not a string')
return None
toRecipients=[toUrl]
toRecipients=[toUrl]
# who to send to
if mentionedRecipients:
@ -656,7 +656,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
'votersCount': 'toot:votersCount'
}
]
if not clientToServer:
actorUrl=httpPrefix+'://'+domain+'/users/'+nickname
@ -714,7 +714,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
newPost['object']= \
attachMedia(baseDir,httpPrefix,domain,port, \
newPost['object'],attachImageFilename, \
mediaType,imageDescription,useBlurhash)
mediaType,imageDescription,useBlurhash)
else:
newPost={
"@context": postContext,
@ -750,7 +750,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
newPost= \
attachMedia(baseDir,httpPrefix,domain,port, \
newPost,attachImageFilename, \
mediaType,imageDescription,useBlurhash)
mediaType,imageDescription,useBlurhash)
if ccUrl:
if len(ccUrl)>0:
newPost['cc']=[ccUrl]
@ -772,7 +772,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \
modFile.close()
if schedulePost:
if eventDate and eventTime:
if eventDate and eventTime:
# add an item to the scheduled post index file
addSchedulePost(baseDir,nickname,domain,eventDateStr,newPostId)
savePostToBox(baseDir,httpPrefix,newPostId, \
@ -853,7 +853,7 @@ def postIsAddressedToFollowers(baseDir: str,
toList=postJsonObject['to']
if postJsonObject.get('cc'):
ccList=postJsonObject['cc']
followersUrl=httpPrefix+'://'+domain+'/users/'+nickname+'/followers'
# does the followers url exist in 'to' or 'cc' lists?
@ -871,7 +871,7 @@ def postIsAddressedToPublic(baseDir: str,postJsonObject: {}) -> bool:
return False
if not postJsonObject['object'].get('to'):
return False
publicUrl='https://www.w3.org/ns/activitystreams#Public'
# does the public url exist in 'to' or 'cc' lists?
@ -932,7 +932,7 @@ def createBlogPost(baseDir: str, \
eventDate,eventTime,location)
blog['object']['type']='Article'
return blog
def createQuestionPost(baseDir: str,
nickname: str,domain: str,port: int,httpPrefix: str, \
@ -1017,7 +1017,7 @@ def createFollowersOnlyPost(baseDir: str,
"""
domainFull=domain
if port:
if port!=80 and port!=443:
if port!=80 and port!=443:
if ':' not in domain:
domainFull=domain+':'+str(port)
return createPostBase(baseDir,nickname,domain,port, \
@ -1167,7 +1167,7 @@ def createReportPost(baseDir: str,
postTo=moderatorsList
postCc=None
postJsonObject=None
for toUrl in postTo:
for toUrl in postTo:
# who is this report going to?
toNickname=toUrl.split('/users/')[1]
handle=toNickname+'@'+domain
@ -1187,7 +1187,7 @@ def createReportPost(baseDir: str,
# update the inbox index with the report filename
#indexFilename=baseDir+'/accounts/'+handle+'/inbox.index'
#indexEntry=postJsonObject['id'].replace('/activity','').replace('/','#')+'.json'
#if indexEntry not in open(indexFilename).read():
#if indexEntry not in open(indexFilename).read():
# try:
# with open(indexFilename, 'a+') as fp:
# fp.write(indexEntry)
@ -1255,7 +1255,7 @@ def threadSendPost(session,postJsonStr: str,federationList: [],\
str(sendIntervalSec)+' seconds.')
time.sleep(sendIntervalSec)
tries+=1
def sendPost(projectVersion: str, \
session,baseDir: str,nickname: str, domain: str, port: int, \
toNickname: str, toDomain: str, toPort: int, cc: str, \
@ -1279,7 +1279,7 @@ def sendPost(projectVersion: str, \
if toPort:
if toPort!=80 and toPort!=443:
if ':' not in toDomain:
toDomain=toDomain+':'+str(toPort)
toDomain=toDomain+':'+str(toPort)
handle=httpPrefix+'://'+toDomain+'/@'+toNickname
@ -1308,7 +1308,7 @@ def sendPost(projectVersion: str, \
inboxUrl=capabilityAcquisition
if not capabilityAcquisition:
return 2
if not inboxUrl:
return 3
if not pubKey:
@ -1316,7 +1316,7 @@ def sendPost(projectVersion: str, \
if not toPersonId:
return 5
# sharedInbox and capabilities are optional
postJsonObject= \
createPostBase(baseDir,nickname,domain,port, \
toPersonId,cc,httpPrefix,content, \
@ -1407,7 +1407,7 @@ def sendPostViaServer(projectVersion: str, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -1427,7 +1427,7 @@ def sendPostViaServer(projectVersion: str, \
if fromPort:
if fromPort!=80 and fromPort!=443:
if ':' not in fromDomain:
fromDomainFull=fromDomain+':'+str(fromPort)
fromDomainFull=fromDomain+':'+str(fromPort)
cc=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers'
else:
if toDomain.lower().endswith('followers') or \
@ -1440,7 +1440,7 @@ def sendPostViaServer(projectVersion: str, \
if toPort:
if toPort!=80 and toPort!=443:
if ':' not in toDomain:
toDomainFull=toDomain+':'+str(toPort)
toDomainFull=toDomain+':'+str(toPort)
toPersonId=httpPrefix+'://'+toDomainFull+'/users/'+toNickname
postJsonObject= \
@ -1452,7 +1452,7 @@ def sendPostViaServer(projectVersion: str, \
imageDescription,useBlurhash, \
False,isArticle,inReplyTo,inReplyToAtomUri,subject, \
False,None,None,None)
authHeader=createBasicAuthHeader(fromNickname,password)
if attachImageFilename:
@ -1467,7 +1467,7 @@ def sendPostViaServer(projectVersion: str, \
# if debug:
# print('DEBUG: Failed to upload image')
# return 9
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -1553,7 +1553,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
if toDomain.endswith('.onion'):
httpPrefix='http'
sharedInbox=False
if toNickname=='inbox':
# shared inbox actor on @domain@domain
@ -1564,7 +1564,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
if toPort:
if toPort!=80 and toPort!=443:
if ':' not in toDomain:
toDomain=toDomain+':'+str(toPort)
toDomain=toDomain+':'+str(toPort)
handleBase=httpPrefix+'://'+toDomain+'/@'
if toNickname:
@ -1572,7 +1572,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
else:
singleUserInstanceNickname='dev'
handle=handleBase+singleUserInstanceNickname
if debug:
print('DEBUG: handle - '+handle+' toPort '+str(toPort))
@ -1587,7 +1587,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
if wfRequest.get('errors'):
if debug:
print('DEBUG: webfinger for '+handle+' failed with errors '+str(wfRequest['errors']))
if not clientToServer:
postToBox='inbox'
else:
@ -1617,7 +1617,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
if debug:
print('DEBUG: Sending to endpoint '+inboxUrl)
if not pubKey:
if debug:
print('DEBUG: missing pubkey')
@ -1643,7 +1643,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
postPath=inboxUrl.split(toDomain,1)[1]
addFollowersToPublicPost(postJsonObject)
# convert json to string so that there are no
# subsequent conversions after creating message body digest
postJsonStr=json.dumps(postJsonObject)
@ -1653,7 +1653,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
createSignedHeader(privateKeyPem,nickname,domain,port, \
toDomain,toPort, \
postPath,httpPrefix,withDigest,postJsonStr)
# Keep the number of threads being used small
while len(sendThreads)>1000:
print('WARN: Maximum threads reached - killing send thread')
@ -1684,7 +1684,7 @@ def addToField(activityType: str,postJsonObject: {},debug: bool) -> ({},bool):
"""
if postJsonObject.get('to'):
return postJsonObject,True
if debug:
pprint(postJsonObject)
print('DEBUG: no "to" field when sending to named addresses 2')
@ -1751,13 +1751,13 @@ def sendToNamedAddresses(session,baseDir: str, \
# use the original object, which has a 'to'
recipientsObject=postJsonObject
isProfileUpdate=True
if not isProfileUpdate:
if not postJsonObject['object'].get('to'):
if debug:
pprint(postJsonObject)
print('DEBUG: no "to" field when sending to named addresses')
if postJsonObject['object'].get('type'):
if postJsonObject['object'].get('type'):
if postJsonObject['object']['type']=='Follow':
if isinstance(postJsonObject['object']['object'], str):
if debug:
@ -1767,7 +1767,7 @@ def sendToNamedAddresses(session,baseDir: str, \
if not postJsonObject['object'].get('to'):
return
recipientsObject=postJsonObject['object']
else:
else:
postJsonObject,fieldAdded=addToField('Follow',postJsonObject,debug)
if not fieldAdded:
return
@ -1881,7 +1881,7 @@ def sendToFollowers(session,baseDir: str, \
print('Post is not addressed to followers')
return
print('Post is addressed to followers')
grouped=groupFollowersByDomain(baseDir,nickname,domain)
if not grouped:
if debug:
@ -1960,7 +1960,7 @@ def sendToFollowers(session,baseDir: str, \
if debug:
print('DEBUG: Sending to '+handle)
toNickname=handle.split('@')[0]
if debug:
if postJsonObject['type']!='Update':
print('DEBUG: Sending from '+ \
@ -2133,7 +2133,7 @@ def isDM(postJsonObject: {}) -> bool:
if postJsonObject['object'].get('moderationStatus'):
return False
fields=['to','cc']
for f in fields:
for f in fields:
if not postJsonObject['object'].get(f):
continue
for toAddress in postJsonObject['object'][f]:
@ -2192,7 +2192,7 @@ def isReply(postJsonObject: {},actor: str) -> bool:
return False
if postJsonObject['object'].get('inReplyTo'):
if postJsonObject['object']['inReplyTo'].startswith(actor):
return True
return True
if not postJsonObject['object'].get('tag'):
return False
if not isinstance(postJsonObject['object']['tag'], list):
@ -2233,14 +2233,14 @@ def createSharedInboxIndex(baseDir: str,sharedBoxDir: str, \
followingFilename=baseDir+'/accounts/'+handle+'/following.txt'
postsInSharedInbox=os.scandir(sharedBoxDir)
followingHandles=None
for postFilename in postsInSharedInbox:
for postFilename in postsInSharedInbox:
postFilename=postFilename.name
if not postFilename.endswith('.json'):
continue
statusNumber=getStatusNumberFromPostFilename(postFilename)
if not statusNumber:
continue
sharedInboxFilename=os.path.join(sharedBoxDir, postFilename)
# get the actor from the shared post
postJsonObject=loadJson(sharedInboxFilename,0)
@ -2266,7 +2266,7 @@ def createSharedInboxIndex(baseDir: str,sharedBoxDir: str, \
capsList=None
# Note: should this be in the Create or the object of a post?
if postJsonObject.get('capability'):
if isinstance(postJsonObject['capability'], list):
if isinstance(postJsonObject['capability'], list):
capsList=postJsonObject['capability']
# Have capabilities been granted for the sender?
@ -2282,7 +2282,7 @@ def createSharedInboxIndex(baseDir: str,sharedBoxDir: str, \
print('WARN: json load exception createSharedInboxIndex')
else:
if ocapJson.get('id'):
if ocapJson['id'] in capsList:
if ocapJson['id'] in capsList:
postsInBoxDict[statusNumber]=sharedInboxFilename
postsCtr+=1
else:
@ -2418,7 +2418,7 @@ def createBoxIndexed(recentPostsCache: {}, \
continue
# Skip through any posts previous to the current page
if postsCtr<int((pageNumber-1)*itemsPerPage):
if postsCtr<int((pageNumber-1)*itemsPerPage):
postsCtr+=1
continue
@ -2485,7 +2485,7 @@ def createBoxIndexed(recentPostsCache: {}, \
# Don't show likes, replies or shares (announces) to unauthorized viewers
if not authorized:
if p.get('object'):
if isinstance(p['object'], dict):
if isinstance(p['object'], dict):
if p['object'].get('likes'):
p['likes']={'items': []}
if p['object'].get('replies'):
@ -2528,9 +2528,9 @@ def archivePosts(baseDir: str,httpPrefix: str,archiveDir: str, \
archiveSubdir=None
if archiveDir:
if not os.path.isdir(archiveDir+'/accounts/'+handle):
os.mkdir(archiveDir+'/accounts/'+handle)
os.mkdir(archiveDir+'/accounts/'+handle)
if not os.path.isdir(archiveDir+'/accounts/'+handle+'/inbox'):
os.mkdir(archiveDir+'/accounts/'+handle+'/inbox')
os.mkdir(archiveDir+'/accounts/'+handle+'/inbox')
if not os.path.isdir(archiveDir+'/accounts/'+handle+'/outbox'):
os.mkdir(archiveDir+'/accounts/'+handle+'/outbox')
archiveSubdir=archiveDir+'/accounts/'+handle+'/inbox'
@ -2552,7 +2552,7 @@ def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str,baseDir: str
return
if archiveDir:
if not os.path.isdir(archiveDir):
os.mkdir(archiveDir)
os.mkdir(archiveDir)
boxDir=createPersonDir(nickname,domain,baseDir,boxname)
postsInBox=os.scandir(boxDir)
noOfPosts=0
@ -2615,7 +2615,7 @@ def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str,baseDir: str
removeCtr=0
for publishedStr,postFilename in postsInBoxSorted.items():
filePath=os.path.join(boxDir,postFilename)
filePath=os.path.join(boxDir,postFilename)
if not os.path.isfile(filePath):
continue
if archiveDir:
@ -2724,7 +2724,7 @@ def populateRepliesJson(baseDir: str,nickname: str,domain: str, \
repliesJson: {}) -> None:
# populate the items list with replies
repliesBoxes=['outbox','inbox']
with open(postRepliesFilename,'r') as repliesFile:
with open(postRepliesFilename,'r') as repliesFile:
for messageId in repliesFile:
replyFound=False
# examine inbox and outbox
@ -2740,7 +2740,7 @@ def populateRepliesJson(baseDir: str,nickname: str,domain: str, \
'https://www.w3.org/ns/activitystreams#Public' in open(searchFilename).read():
postJsonObject=loadJson(searchFilename)
if postJsonObject:
if postJsonObject['object'].get('cc'):
if postJsonObject['object'].get('cc'):
if authorized or \
('https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to'] or \
'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['cc']):
@ -2765,7 +2765,7 @@ def populateRepliesJson(baseDir: str,nickname: str,domain: str, \
# get the json of the reply and append it to the collection
postJsonObject=loadJson(searchFilename)
if postJsonObject:
if postJsonObject['object'].get('cc'):
if postJsonObject['object'].get('cc'):
if authorized or \
('https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to'] or \
'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['cc']):
@ -2844,7 +2844,7 @@ def downloadAnnounce(session,baseDir: str,httpPrefix: str, \
if not isinstance(announcedJson, dict):
print('WARN: announce json is not a dict - '+postJsonObject['object'])
rejectAnnounce(announceFilename)
return None
return None
if not announcedJson.get('id'):
rejectAnnounce(announceFilename)
return None
@ -2867,7 +2867,7 @@ def downloadAnnounce(session,baseDir: str,httpPrefix: str, \
return None
if not announcedJson.get('content'):
rejectAnnounce(announceFilename)
return None
return None
if isFiltered(baseDir,nickname,domain,announcedJson['content']):
rejectAnnounce(announceFilename)
return None

View File

@ -104,7 +104,7 @@ def questionUpdateVotes(baseDir: str,nickname: str,domain: str,replyJson: {}) ->
lines=votersFile.readlines()
for voteLine in lines:
if voteLine.endswith(votersFileSeparator+possibleAnswer['name']+'\n'):
totalItems+=1
totalItems+=1
if possibleAnswer['replies']['totalItems']!=totalItems:
possibleAnswer['replies']['totalItems']=totalItems
questionTotalsChanged=True

View File

@ -27,7 +27,7 @@ def clearModeratorStatus(baseDir: str) -> None:
for f in os.scandir(directory):
f=f.name
filename=os.fsdecode(f)
if filename.endswith(".json") and '@' in filename:
if filename.endswith(".json") and '@' in filename:
filename=os.path.join(baseDir+'/accounts/', filename)
if '"moderator"' in open(filename).read():
actorJson=loadJson(filename)
@ -62,7 +62,7 @@ def addModerator(baseDir: str,nickname: str,domain: str) -> None:
with open(moderatorsFile, "w+") as f:
if os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
f.write(nickname+'\n')
def removeModerator(baseDir: str,nickname: str):
"""Removes a moderator nickname from the file
"""
@ -90,7 +90,7 @@ def setRole(baseDir: str,nickname: str,domain: str, \
return False
actorJson=loadJson(actorFilename)
if actorJson:
if actorJson:
if role:
# add the role
if project=='instance' and 'role'=='moderator':
@ -196,13 +196,13 @@ def outboxDelegate(baseDir: str,authenticatedNickname: str,messageJson: {},debug
if not role:
setRole(baseDir,nickname,domain,project,None)
return True
# what roles is this person already assigned to?
existingRoles=getRoles(baseDir,nickname,domain,project)
if existingRoles:
if role in existingRoles:
if debug:
print(nickname+'@'+domain+' is already assigned to the role '+role+' within the project '+project)
print(nickname+'@'+domain+' is already assigned to the role '+role+' within the project '+project)
return False
setRole(baseDir,nickname,domain,project,role)
if debug:
@ -228,7 +228,7 @@ def sendRoleViaServer(baseDir: str,session, \
if fromPort!=80 and fromPort!=443:
if ':' not in delegatorDomain:
delegatorDomainFull=delegatorDomain+':'+str(fromPort)
toUrl= \
httpPrefix+'://'+delegatorDomainFull+'/users/'+nickname
ccUrl= \
@ -247,7 +247,7 @@ def sendRoleViaServer(baseDir: str,session, \
'actor': httpPrefix+'://'+delegatorDomainFull+'/users/'+nickname,
'object': roleStr,
'to': [toUrl],
'cc': [ccUrl]
'cc': [ccUrl]
},
'to': [toUrl],
'cc': [ccUrl]
@ -270,7 +270,7 @@ def sendRoleViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix, \
delegatorNickname,delegatorDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -279,9 +279,9 @@ def sendRoleViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(delegatorNickname,password)
headers={
'host': delegatorDomain, \
'Content-type': 'application/json', \

View File

@ -147,7 +147,7 @@ def runPostScheduleWatchdog(projectVersion: str,httpd) -> None:
httpd.thrPostSchedule.clone(runPostSchedule)
httpd.thrPostSchedule.start()
while True:
time.sleep(20)
time.sleep(20)
if not httpd.thrPostSchedule.isAlive():
httpd.thrPostSchedule.kill()
httpd.thrPostSchedule= \

View File

@ -59,7 +59,7 @@ def postJson(session,postJsonObject: {},federationList: [], \
"""
# always allow capability requests
if not capability.startswith('cap'):
if not capability.startswith('cap'):
# check that we are posting to a permitted domain
if not urlPermitted(inboxUrl,federationList,capability):
print('postJson: '+inboxUrl+' not permitted')
@ -88,7 +88,7 @@ def postJsonString(session,postJsonStr: str, \
"""
# always allow capability requests
if not capability.startswith('cap'):
if not capability.startswith('cap'):
# check that we are posting to a permitted domain
if not urlPermitted(inboxUrl,federationList,capability):
print('postJson: '+inboxUrl+' not permitted by capabilities')
@ -118,7 +118,7 @@ def postImage(session,attachImageFilename: str,federationList: [], \
Supplying a capability, such as "inbox:write"
"""
# always allow capability requests
if not capability.startswith('cap'):
if not capability.startswith('cap'):
# check that we are posting to a permitted domain
if not urlPermitted(inboxUrl,federationList,capability):
print('postJson: '+inboxUrl+' not permitted')

View File

@ -187,7 +187,7 @@ def expireSharesForAccount(baseDir: str,nickname: str,domain: str) -> None:
if ':' in handleDomain:
handleDomain=domain.split(':')[0]
handle=nickname+'@'+handleDomain
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
if os.path.isfile(sharesFilename):
sharesJson=loadJson(sharesFilename)
if sharesJson:
@ -219,7 +219,7 @@ def getSharesFeedForPerson(baseDir: str, \
return None
# handle page numbers
headerOnly=True
pageNumber=None
pageNumber=None
if '?page=' in path:
pageNumber=path.split('?page=')[1]
if pageNumber=='true':
@ -231,7 +231,7 @@ def getSharesFeedForPerson(baseDir: str, \
pass
path=path.split('?page=')[0]
headerOnly=False
if not path.endswith('/shares'):
return None
nickname=None
@ -253,7 +253,7 @@ def getSharesFeedForPerson(baseDir: str, \
if ':' in handleDomain:
handleDomain=domain.split(':')[0]
handle=nickname+'@'+handleDomain
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
sharesFilename=baseDir+'/accounts/'+handle+'/shares.json'
if headerOnly:
noOfShares=0
@ -374,7 +374,7 @@ def sendShareViaServer(baseDir,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix, \
fromNickname,fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -383,7 +383,7 @@ def sendShareViaServer(baseDir,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
if imageFilename:
@ -393,7 +393,7 @@ def sendShareViaServer(baseDir,session, \
}
postResult= \
postImage(session,imageFilename,[],inboxUrl.replace('/'+postToBox,'/shares'),headers,"inbox:write")
headers={
'host': fromDomain, \
'Content-type': 'application/json', \
@ -465,7 +465,7 @@ def sendUndoShareViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix, \
fromNickname,fromDomain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -474,9 +474,9 @@ def sendUndoShareViaServer(baseDir: str,session, \
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers={
'host': fromDomain, \
'Content-type': 'application/json', \

View File

@ -108,7 +108,7 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str,
if port!=80 and port!=443:
if ':' not in domain:
domainFull=domain+':'+str(port)
toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname
ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers'
@ -140,7 +140,7 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str,
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \
getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,nickname,domain,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
@ -149,9 +149,9 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str,
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(Nickname,password)
headers={
'host': domain, \
'Content-type': 'application/json', \

View File

@ -383,7 +383,7 @@ def testPostMessageBetweenServers():
os.mkdir(baseDir+'/.tests')
ocapAlways=False
# create the servers
aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.50'
@ -432,7 +432,7 @@ def testPostMessageBetweenServers():
# wait for both servers to be running
while not (testServerAliceRunning and testServerBobRunning):
time.sleep(1)
time.sleep(1)
print('\n\n*******************************************************')
@ -483,7 +483,7 @@ def testPostMessageBetweenServers():
time.sleep(1)
# Image attachment created
assert len([name for name in os.listdir(mediaPath) if os.path.isfile(os.path.join(mediaPath, name))])>0
assert len([name for name in os.listdir(mediaPath) if os.path.isfile(os.path.join(mediaPath, name))])>0
# inbox item created
assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
# queue item removed
@ -539,7 +539,7 @@ def testPostMessageBetweenServers():
alicePostJson=loadJson(outboxPostFilename,0)
if alicePostJson:
pprint(alicePostJson)
assert 'likes' in open(outboxPostFilename).read()
print('\n\n*******************************************************')
@ -714,7 +714,7 @@ def testFollowBetweenServers():
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
aliceMessageArrived=False
aliceMessageArrived=False
for i in range(20):
time.sleep(1)
if os.path.isdir(inboxPath):
@ -734,14 +734,14 @@ def testFollowBetweenServers():
thrBob.kill()
thrBob.join()
assert thrBob.isAlive()==False
assert 'alice@'+aliceDomain in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read()
assert 'bob@'+bobDomain in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read()
# queue item removed
time.sleep(4)
assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0
os.chdir(baseDir)
shutil.rmtree(baseDir+'/.tests')
@ -758,7 +758,7 @@ def testFollowersOfPerson():
if os.path.isdir(baseDir):
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
os.chdir(baseDir)
createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
createPerson(baseDir,'maxboardroom',domain,port,httpPrefix,True,password)
createPerson(baseDir,'ultrapancake',domain,port,httpPrefix,True,password)
@ -796,7 +796,7 @@ def testNoOfFollowersOnDomain():
if os.path.isdir(baseDir):
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
os.chdir(baseDir)
createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
createPerson(baseDir,'maxboardroom',otherdomain,port,httpPrefix,True,password)
createPerson(baseDir,'ultrapancake',otherdomain,port,httpPrefix,True,password)
@ -806,7 +806,7 @@ def testNoOfFollowersOnDomain():
followPerson(baseDir,'drokk',otherdomain,nickname,domain,federationList,False)
followPerson(baseDir,'sausagedog',otherdomain,nickname,domain,federationList,False)
followPerson(baseDir,'maxboardroom',otherdomain,nickname,domain,federationList,False)
followerOfPerson(baseDir,nickname,domain,'cucumber','sandwiches.party',federationList,False)
followerOfPerson(baseDir,nickname,domain,'captainsensible','damned.zone',federationList,False)
followerOfPerson(baseDir,nickname,domain,'pilchard','zombies.attack',federationList,False)
@ -820,7 +820,7 @@ def testNoOfFollowersOnDomain():
unfollowerOfPerson(baseDir,nickname,domain,'sausagedog',otherdomain)
followersOnOtherDomain=noOfFollowersOnDomain(baseDir,nickname+'@'+domain, otherdomain)
assert followersOnOtherDomain==2
os.chdir(currDir)
shutil.rmtree(baseDir)
@ -838,7 +838,7 @@ def testGroupFollowers():
if os.path.isdir(baseDir):
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
os.chdir(baseDir)
createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
clearFollowers(baseDir,nickname,domain)
@ -857,11 +857,11 @@ def testGroupFollowers():
assert len(grouped['zzz.domain'])==2
assert len(grouped['wild.domain'])==3
assert len(grouped['clutterly.domain'])==1
os.chdir(currDir)
shutil.rmtree(baseDir)
def testFollows():
print('testFollows')
currDir=os.getcwd()
@ -875,7 +875,7 @@ def testFollows():
if os.path.isdir(baseDir):
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
os.chdir(baseDir)
createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
clearFollows(baseDir,nickname,domain)
@ -937,7 +937,7 @@ def testCreatePerson():
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
assert os.path.isfile(baseDir+'/accounts/passwords')
deleteAllPosts(baseDir,nickname,domain,'inbox')
@ -967,7 +967,7 @@ def testDelegateRoles():
shutil.rmtree(baseDir)
os.mkdir(baseDir)
os.chdir(baseDir)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nicknameDelegated,domain,port,httpPrefix,True,'insecure')
@ -982,7 +982,7 @@ def testDelegateRoles():
'actor': httpPrefix+'://'+domain+'/users/'+nicknameDelegated,
'object': project+';'+role,
'to': [],
'cc': []
'cc': []
},
'to': [],
'cc': []
@ -994,7 +994,7 @@ def testDelegateRoles():
assert '"delegator"' in open(baseDir+'/accounts/'+nickname+'@'+domain+'.json').read()
assert '"delegator"' in open(baseDir+'/accounts/'+nicknameDelegated+'@'+domain+'.json').read()
newRoleJson={
'type': 'Delegate',
'actor': httpPrefix+'://'+domain+'/users/'+nicknameDelegated,
@ -1003,7 +1003,7 @@ def testDelegateRoles():
'actor': httpPrefix+'://'+domain+'/users/'+nickname,
'object': 'otherproject;otherrole',
'to': [],
'cc': []
'cc': []
},
'to': [],
'cc': []
@ -1094,7 +1094,7 @@ def testClientToServer():
args=(aliceDir,aliceDomain,alicePort,bobAddress, \
federationList,False,False, \
ocapAlways,aliceSendThreads),daemon=True)
global thrBob
if thrBob:
while thrBob.isAlive():
@ -1124,7 +1124,7 @@ def testClientToServer():
print('Bob online: '+str(testServerBobRunning))
time.sleep(1)
print('\n\n*******************************************************')
print('Alice sends to Bob via c2s')
@ -1162,7 +1162,7 @@ def testClientToServer():
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1
print(">>> c2s post arrived in Alice's outbox")
for i in range(30):
if os.path.isdir(inboxPath):
if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1:
@ -1196,7 +1196,7 @@ def testClientToServer():
'bob',bobDomain,bobPort, \
httpPrefix, \
cachedWebfingers,personCache, \
True,__version__)
True,__version__)
aliceFollowingFilename=aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'
bobFollowersFilename=bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'
for t in range(10):
@ -1254,7 +1254,7 @@ def testClientToServer():
cachedWebfingers,personCache, \
True,__version__)
for i in range(20):
if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2:
if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1:
break
@ -1262,7 +1262,7 @@ def testClientToServer():
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2
assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
print('Post liked')
print('\n\nBob repeats the post')
print(str(len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])))
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2
@ -1274,7 +1274,7 @@ def testClientToServer():
cachedWebfingers, \
personCache,True,__version__)
for i in range(20):
if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==3:
if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==2:
break
@ -1306,7 +1306,7 @@ def testClientToServer():
assert validInbox(bobDir,'bob',bobDomain)
assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
print('\n\nAlice unfollows Bob')
password='alicepass'
sendUnfollowRequestViaServer(baseDir,sessionAlice, \
@ -1411,12 +1411,12 @@ def testAddEmoji():
path=baseDir+'/.tests/emoji'
if os.path.isdir(path):
shutil.rmtree(path)
os.mkdir(path)
os.mkdir(path)
baseDir=path
path=baseDir+'/emoji'
if os.path.isdir(path):
shutil.rmtree(path)
os.mkdir(path)
os.mkdir(path)
copytree(baseDirOriginal+'/emoji',baseDir+'/emoji')
os.chdir(baseDir)
privateKeyPem,publicKeyPem,person,wfEndpoint= \
@ -1528,4 +1528,4 @@ def runAllTests():
testFollows()
testGroupFollowers()
testDelegateRoles()
print('Tests succeeded\n')
print('Tests succeeded\n')

View File

@ -18,7 +18,7 @@ def setThemeInConfig(baseDir: str,name: str) -> bool:
if not configJson:
return False
configJson['theme']=name
return saveJson(configJson,configFilename)
return saveJson(configJson,configFilename)
def removeTheme(baseDir: str):
themeFiles=('epicyon.css','login.css','follow.css','suspended.css','calendar.css','blog.css')
@ -60,7 +60,7 @@ def setCSSparam(css: str,param: str,value: str) -> str:
else:
newcss+=searchStr+' '+sectionStr
return newcss.strip()
def setThemeFromDict(baseDir: str,name: str,themeParams: {}):
"""Uses a dictionary to set a theme
"""
@ -197,7 +197,7 @@ def setThemeLight(baseDir: str):
"gallery-text-color": "black"
}
setThemeFromDict(baseDir,'light',themeParams)
def setTheme(baseDir: str,name: str) -> bool:
if name=='default':
setThemeDefault(baseDir)
@ -215,4 +215,3 @@ def setTheme(baseDir: str,name: str) -> bool:
setThemeHighVis(baseDir)
return True
return False

View File

@ -21,20 +21,20 @@ class threadWithTrace(threading.Thread):
while tries<3:
try:
self._args,self._keywords=args,keywords
threading.Thread.__init__(self,*self._args,**self._keywords)
threading.Thread.__init__(self,*self._args,**self._keywords)
self.killed=False
break
except Exception as e:
print('ERROR: threads.py/__init__ failed - '+str(e))
time.sleep(1)
tries+=1
def start(self):
def start(self):
tries=0
while tries<3:
try:
self.__run_backup=self.run
self.run=self.__run
self.__run_backup=self.run
self.run=self.__run
threading.Thread.start(self)
break
except Exception as e:
@ -49,25 +49,25 @@ class threadWithTrace(threading.Thread):
self.__run_backup()
self.run=self.__run_backup
def globaltrace(self, frame, event, arg):
if event == 'call':
def globaltrace(self, frame, event, arg):
if event == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, event, arg):
if self.killed:
if event == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
def localtrace(self, frame, event, arg):
if self.killed:
if event == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
self.killed=True
def clone(self,fn):
return threadWithTrace(target=fn, \
args=self._args, \
daemon=True)
daemon=True)
def removeDormantThreads(baseDir: str,threadsList: [],debug: bool) -> None:
"""Removes threads whose execution has completed
@ -82,7 +82,7 @@ def removeDormantThreads(baseDir: str,threadsList: [],debug: bool) -> None:
# which threads are dormant?
noOfActiveThreads=0
for th in threadsList:
removeThread=False
removeThread=False
if th.isStarted:
if not th.is_alive():
@ -114,7 +114,7 @@ def removeDormantThreads(baseDir: str,threadsList: [],debug: bool) -> None:
for th in dormantThreads:
if debug:
print('DEBUG: Removing dormant thread '+str(dormantCtr))
dormantCtr+=1
dormantCtr+=1
threadsList.remove(th)
th.kill()
changed=True

View File

@ -220,7 +220,7 @@ def getDomainFromActor(actor: str) -> (str,int):
port=int(portStr)
domain=domain.split(':')[0]
return domain,port
def followPerson(baseDir: str,nickname: str, domain: str, \
followNickname: str, followDomain: str, \
federationList: [],debug: bool, \
@ -282,7 +282,7 @@ def followPerson(baseDir: str,nickname: str, domain: str, \
print('DEBUG: follow added')
return True
except Exception as e:
print('WARN: Failed to write entry to follow file '+filename+' '+str(e))
print('WARN: Failed to write entry to follow file '+filename+' '+str(e))
if debug:
print('DEBUG: creating new following file to follow '+handleToFollow)
with open(filename, "w") as followfile:
@ -385,9 +385,9 @@ def deletePost(baseDir: str,httpPrefix: str,nickname: str,domain: str,postFilena
getCachedPostFilename(baseDir,nickname,domain,postJsonObject)
if cachedPostFilename:
if os.path.isfile(cachedPostFilename):
os.remove(cachedPostFilename)
os.remove(cachedPostFilename)
#removePostFromCache(postJsonObject,recentPostsCache)
hasObject=False
if postJsonObject.get('object'):
hasObject=True
@ -451,7 +451,7 @@ def deletePost(baseDir: str,httpPrefix: str,nickname: str,domain: str,postFilena
# remove the replies file
os.remove(repliesFilename)
# finally, remove the post itself
os.remove(postFilename)
os.remove(postFilename)
def validNickname(domain: str,nickname: str) -> bool:
forbiddenChars=['.',' ','/','?',':',';','@']
@ -646,4 +646,4 @@ def isBlogPost(postJsonObject: {}) -> bool:
return False
if postJsonObject['object']['type']!='Article':
return False
return True
return True

View File

@ -60,7 +60,7 @@ def webfingerHandle(session,handle: str,httpPrefix: str,cachedWebfingers: {}, \
wfDomain=wfDomain.split(':')[0]
wf=getWebfingerFromCache(nickname+'@'+wfDomain,cachedWebfingers)
if wf:
return wf
return wf
url='{}://{}/.well-known/webfinger'.format(httpPrefix,domain)
par={
'resource': 'acct:{}'.format(nickname+'@'+wfDomain)
@ -85,7 +85,7 @@ def generateMagicKey(publicKeyPem) -> str:
"""See magic_key method in
https://github.com/tootsuite/mastodon/blob/707ddf7808f90e3ab042d7642d368c2ce8e95e6f/app/models/account.rb
"""
privkey=RSA.importKey(publicKeyPem)
privkey=RSA.importKey(publicKeyPem)
mod=base64.urlsafe_b64encode(number.long_to_bytes(privkey.n)).decode("utf-8")
pubexp=base64.urlsafe_b64encode(number.long_to_bytes(privkey.e)).decode("utf-8")
return f"data:application/magic-public-key,RSA.{mod}.{pubexp}"
@ -130,7 +130,7 @@ def createWebfingerEndpoint(nickname: str,domain: str,port: int, \
personId=httpPrefix+"://"+domain+"/"+personName
subjectStr="acct:"+originalDomain+"@"+originalDomain
profilePageHref=httpPrefix+'://'+domain+'/about/more?instance_actor=true'
account={
"aliases": [
httpPrefix+"://"+domain+"/@"+personName,
@ -195,7 +195,7 @@ def webfingerLookup(path: str,baseDir: str, \
port: int,debug: bool) -> {}:
"""Lookup the webfinger endpoint for an account
"""
if not path.startswith('/.well-known/webfinger?'):
if not path.startswith('/.well-known/webfinger?'):
return None
handle=None
if 'resource=acct:' in path:

View File

@ -118,7 +118,7 @@ def updateAvatarImageCache(session,baseDir: str,httpPrefix: str, \
f.write(result.content)
print('avatar image downloaded for '+actor)
return avatarImageFilename.replace(baseDir+'/cache','')
except Exception as e:
except Exception as e:
print('Failed to download avatar image: '+str(avatarUrl))
print(e)
if '/channel/' not in actor:
@ -155,7 +155,7 @@ def updateAvatarImageCache(session,baseDir: str,httpPrefix: str, \
def getPersonAvatarUrl(baseDir: str,personUrl: str,personCache: {}) -> str:
"""Returns the avatar url for the person
"""
personJson=getPersonFromCache(baseDir,personUrl,personCache)
personJson=getPersonFromCache(baseDir,personUrl,personCache)
if not personJson:
return None
# get from locally stored image
@ -171,7 +171,7 @@ def getPersonAvatarUrl(baseDir: str,personUrl: str,personCache: {}) -> str:
return '/avatars/'+actorStr+'.webp'
if os.path.isfile(avatarImagePath):
return '/avatars/'+actorStr
if personJson.get('icon'):
if personJson['icon'].get('url'):
return personJson['icon']['url']
@ -183,14 +183,14 @@ def htmlSearchEmoji(translate: {},baseDir: str,httpPrefix: str, \
"""
# emoji.json is generated so that it can be customized and the changes
# will be retained even if default_emoji.json is subsequently updated
# will be retained even if default_emoji.json is subsequently updated
if not os.path.isfile(baseDir+'/emoji/emoji.json'):
copyfile(baseDir+'/emoji/default_emoji.json',baseDir+'/emoji/emoji.json')
searchStr=searchStr.lower().replace(':','').strip('\n')
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
emojiCSS=cssFile.read()
if httpPrefix!='https':
@ -285,7 +285,7 @@ def htmlSearchSharedItems(translate: {}, \
sharesJson=loadJson(sharesFilename)
if not sharesJson:
continue
for name,sharedItem in sharesJson.items():
matched=True
for searchSubstr in searchStrLowerList:
@ -370,13 +370,13 @@ def htmlSearchSharedItems(translate: {}, \
if not resultsExist:
sharedItemsForm+='<center><h5>'+translate['No results']+'</h5></center>'
sharedItemsForm+=htmlFooter()
return sharedItemsForm
return sharedItemsForm
def htmlModerationInfo(translate: {},baseDir: str,httpPrefix: str) -> str:
infoForm=''
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
infoCSS=cssFile.read()
if httpPrefix!='https':
@ -386,7 +386,7 @@ def htmlModerationInfo(translate: {},baseDir: str,httpPrefix: str) -> str:
infoForm+= \
'<center><h1>'+translate['Moderation Information']+'</h1></center>'
infoShown=False
infoShown=False
suspendedFilename=baseDir+'/accounts/suspended.txt'
if os.path.isfile(suspendedFilename):
with open(suspendedFilename, "r") as f:
@ -412,7 +412,7 @@ def htmlModerationInfo(translate: {},baseDir: str,httpPrefix: str) -> str:
infoForm+= \
' <textarea id="message" name="blocked" style="height:400px">'+ \
blockedStr+'</textarea>'
infoForm+='</div>'
infoForm+='</div>'
infoShown=True
if not infoShown:
infoForm+= \
@ -420,7 +420,7 @@ def htmlModerationInfo(translate: {},baseDir: str,httpPrefix: str) -> str:
translate['Any blocks or suspensions made by moderators will be shown here.']+ \
'</p></center>'
infoForm+=htmlFooter()
return infoForm
return infoForm
def htmlHashtagSearch(nickname: str,domain: str,port: int, \
recentPostsCache: {},maxRecentPosts: int, \
@ -450,7 +450,7 @@ def htmlHashtagSearch(nickname: str,domain: str,port: int, \
# read the css
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
hashtagSearchCSS=cssFile.read()
if httpPrefix!='https':
@ -467,7 +467,7 @@ def htmlHashtagSearch(nickname: str,domain: str,port: int, \
startIndex=int((pageNumber-1)*postsPerPage)
endIndex=startIndex+postsPerPage
noOfLines=len(lines)
if endIndex>=noOfLines and noOfLines>0:
if endIndex>=noOfLines and noOfLines>0:
endIndex=noOfLines-1
# add the page title
@ -506,7 +506,7 @@ def htmlHashtagSearch(nickname: str,domain: str,port: int, \
if postJsonObject:
if not isPublicPost(postJsonObject):
index+=1
continue
continue
showIndividualPostIcons=False
if nickname:
showIndividualPostIcons=True
@ -606,7 +606,7 @@ def htmlSkillsSearch(translate: {},baseDir: str, \
if skillLevel<100:
skillLevelStr='0'+skillLevelStr
if skillLevel<10:
skillLevelStr='0'+skillLevelStr
skillLevelStr='0'+skillLevelStr
indexStr= \
skillLevelStr+';'+actor+';'+actorJson['name']+ \
';'+actorJson['icon']['url']
@ -617,7 +617,7 @@ def htmlSkillsSearch(translate: {},baseDir: str, \
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
skillSearchCSS=cssFile.read()
if httpPrefix!='https':
@ -733,7 +733,7 @@ def htmlEditProfile(translate: {},baseDir: str,path: str, \
if mediaInstance:
if mediaInstance==True:
mediaInstanceStr='checked'
filterStr=''
filterFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/filters.txt'
if os.path.isfile(filterFilename):
@ -783,7 +783,7 @@ def htmlEditProfile(translate: {},baseDir: str,path: str, \
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
editProfileCSS=cssFile.read()
if httpPrefix!='https':
@ -818,7 +818,7 @@ def htmlEditProfile(translate: {},baseDir: str,path: str, \
' <input type="file" id="instanceLogo" name="instanceLogo"'
instanceStr+=' accept="'+imageFormats+'">'
instanceStr+='</div>'
moderators=''
moderatorsFile=baseDir+'/accounts/moderators.txt'
if os.path.isfile(moderatorsFile):
@ -1017,7 +1017,7 @@ def htmlLogin(translate: {},baseDir: str,autocomplete=True) -> str:
if os.path.isfile(baseDir+'/accounts/login.txt'):
# custom login message
with open(baseDir+'/accounts/login.txt', 'r') as file:
loginText='<p class="login-text">'+file.read()+'</p>'
loginText='<p class="login-text">'+file.read()+'</p>'
cssFilename=baseDir+'/epicyon-login.css'
if os.path.isfile(baseDir+'/login.css'):
@ -1096,17 +1096,17 @@ def htmlTermsOfService(baseDir: str,httpPrefix: str,domainFull: str) -> str:
TOSText='Terms of Service go here.'
if os.path.isfile(baseDir+'/accounts/tos.txt'):
with open(baseDir+'/accounts/tos.txt', 'r') as file:
TOSText=file.read()
TOSText=file.read()
TOSForm=''
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
termsCSS=cssFile.read()
if httpPrefix!='https':
termsCSS=termsCSS.replace('https://',httpPrefix+'://')
TOSForm=htmlHeader(cssFilename,termsCSS)
TOSForm+='<div class="container">'+TOSText+'</div>'
if adminNickname:
@ -1131,12 +1131,12 @@ def htmlAbout(baseDir: str,httpPrefix: str,domainFull: str) -> str:
aboutText='Information about this instance goes here.'
if os.path.isfile(baseDir+'/accounts/about.txt'):
with open(baseDir+'/accounts/about.txt', 'r') as file:
aboutText=file.read()
aboutText=file.read()
aboutForm=''
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
termsCSS=cssFile.read()
if httpPrefix!='http':
@ -1160,7 +1160,7 @@ def htmlHashtagBlocked(baseDir: str) -> str:
if os.path.isfile(baseDir+'/suspended.css'):
cssFilename=baseDir+'/suspended.css'
with open(cssFilename, 'r') as cssFile:
blockedHashtagCSS=cssFile.read()
blockedHashtagCSS=cssFile.read()
blockedHashtagForm=htmlHeader(cssFilename,blockedHashtagCSS)
blockedHashtagForm+='<div><center>'
blockedHashtagForm+=' <p class="screentitle">Hashtag Blocked</p>'
@ -1168,7 +1168,7 @@ def htmlHashtagBlocked(baseDir: str) -> str:
blockedHashtagForm+='</center></div>'
blockedHashtagForm+=htmlFooter()
return blockedHashtagForm
def htmlSuspended(baseDir: str) -> str:
"""Show the screen for suspended accounts
"""
@ -1177,7 +1177,7 @@ def htmlSuspended(baseDir: str) -> str:
if os.path.isfile(baseDir+'/suspended.css'):
cssFilename=baseDir+'/suspended.css'
with open(cssFilename, 'r') as cssFile:
suspendedCSS=cssFile.read()
suspendedCSS=cssFile.read()
suspendedForm=htmlHeader(cssFilename,suspendedCSS)
suspendedForm+='<div><center>'
suspendedForm+=' <p class="screentitle">Account Suspended</p>'
@ -1251,11 +1251,11 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
if os.path.isfile(baseDir+'/accounts/newpost.txt'):
with open(baseDir+'/accounts/newpost.txt', 'r') as file:
newPostText='<p class="new-post-text">'+file.read()+'</p>'
newPostText='<p class="new-post-text">'+file.read()+'</p>'
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
newPostCSS=cssFile.read()
if httpPrefix!='https':
@ -1272,7 +1272,7 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
newPostImageSection+=' <input type="file" id="attachpic" name="attachpic"'
newPostImageSection+=' accept=".png, .jpg, .jpeg, .gif, .webp, .mp4, .webm, .ogv, .mp3, .ogg">'
newPostImageSection+=' </div>'
scopeIcon='scope_public.png'
scopeDescription=translate['Public']
placeholderSubject=translate['Subject or Content Warning (optional)']+'...'
@ -1320,7 +1320,7 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
scopeDescription=translate['Shared Item']
placeholderSubject=translate['Name of the shared item']+'...'
placeholderMessage=translate['Description of the item being shared']+'...'
endpoint='newshare'
endpoint='newshare'
extraFields='<div class="container">'
extraFields+= \
' <label class="labels">'+ \
@ -1403,14 +1403,14 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
dropdownNewBlogSuffix='/newblog'
dropdownUnlistedSuffix='/newunlisted'
dropdownFollowersSuffix='/newfollowers'
dropdownDMSuffix='/newdm'
dropdownReportSuffix='/newreport'
dropdownDMSuffix='/newdm'
dropdownReportSuffix='/newreport'
if inReplyTo or mentions:
dropdownNewPostSuffix=''
dropdownNewBlogSuffix=''
dropdownUnlistedSuffix=''
dropdownFollowersSuffix=''
dropdownDMSuffix=''
dropdownDMSuffix=''
dropdownReportSuffix=''
if inReplyTo:
dropdownNewPostSuffix+='?replyto='+inReplyTo
@ -1425,7 +1425,7 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
dropdownFollowersSuffix+='?mention='+mentionedActor
dropdownDMSuffix+='?mention='+mentionedActor
dropdownReportSuffix+='?mention='+mentionedActor
dropDownContent=''
if not reportUrl:
dropDownContent+=' <div id="myDropdown" class="dropdown-content">'
@ -1468,7 +1468,7 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \
dropDownContent+=' </div>'
else:
mentionsStr='Re: '+reportUrl+'\n\n'+mentionsStr
newPostForm+= \
'<form enctype="multipart/form-data" method="POST" accept-charset="UTF-8" action="'+ \
path+'?'+endpoint+'?page='+str(pageNumber)+'">'
@ -1556,7 +1556,7 @@ def htmlHeader(cssFilename: str,css=None,refreshSec=0,lang='en') -> str:
htmlStr+='<html lang="'+lang+'">\n'
htmlStr+=meta
htmlStr+=' <style>\n'+css+'</style>\n'
htmlStr+=' <body>\n'
htmlStr+=' <body>\n'
return htmlStr
def htmlFooter() -> str:
@ -1764,7 +1764,7 @@ def sharesTimelineJson(actor: str,pageNumber: int,itemsPerPage: int, \
resultJson={}
for published,item in sharesJson.items():
if ctr>=startIndex+itemsPerPage:
break
break
if ctr<startIndex:
ctr+=1
continue
@ -1813,7 +1813,7 @@ def htmlSharesTimeline(translate: {},pageNumber: int,itemsPerPage: int, \
str(pageNumber+1)+'"><img loading="lazy" class="pageicon" src="/'+ \
iconsDir+'/pagedown.png" title="'+translate['Page down']+ \
'" alt="'+translate['Page down']+'"></a></center>'
return timelineStr
def htmlProfile(defaultTimeline: str, \
@ -1835,14 +1835,14 @@ def htmlProfile(defaultTimeline: str, \
displayName= \
addEmojiToDisplayName(baseDir,httpPrefix, \
nickname,domain, \
profileJson['name'],True)
profileJson['name'],True)
domainFull=domain
if port:
domainFull=domain+':'+str(port)
profileDescription= \
addEmojiToDisplayName(baseDir,httpPrefix, \
nickname,domain, \
profileJson['summary'],False)
profileJson['summary'],False)
postsButton='button'
followingButton='button'
followersButton='button'
@ -2032,10 +2032,10 @@ def htmlProfile(defaultTimeline: str, \
profileStr+='</div>'
profileStr+=followApprovalsSection
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
profileStyle= \
cssFile.read().replace('image.png', \
@ -2354,7 +2354,7 @@ def addEmbeddedVideoFromSites(translate: {},content: str,width=400,height=300) -
if '"https://'+site in content:
url=content.split('"https://'+site)[1]
if '"' in url:
url=url.split('"')[0].replace('/watch/','/embed/')
url=url.split('"')[0].replace('/watch/','/embed/')
content= \
content+"<center><iframe loading=\"lazy\" sandbox=\"allow-same-origin allow-scripts\" src=\"https://"+ \
site+url+"\" width=\""+str(width)+"\" height=\""+str(height)+ \
@ -2518,7 +2518,7 @@ def postContainsPublic(postJsonObject: {}) -> bool:
containsPublic=False
if not postJsonObject['object'].get('to'):
return containsPublic
for toAddress in postJsonObject['object']['to']:
if toAddress.endswith('#Public'):
containsPublic=True
@ -2543,10 +2543,10 @@ def loadIndividualPostAsHtmlFromCache(baseDir: str,nickname: str,domain: str, \
postHtml=''
if not cachedPostFilename:
return postHtml
if not os.path.isfile(cachedPostFilename):
return postHtml
tries=0
while tries<3:
try:
@ -2682,7 +2682,7 @@ def getPostAttachmentsAsHtml(postJsonObject: {},boxName: str,translate: {}, \
'<img loading="lazy" src="'+attach['url']+ \
'" alt="'+imageDescription+'" title="'+ \
imageDescription+'" class="attachment"></a>\n'
attachmentCtr+=1
attachmentCtr+=1
elif mediaType=='video/mp4' or \
mediaType=='video/webm' or \
mediaType=='video/ogv':
@ -2743,7 +2743,7 @@ def getPostAttachmentsAsHtml(postJsonObject: {},boxName: str,translate: {}, \
mediaType=='audio/ogg':
extension='.mp3'
if attach['url'].endswith('.ogg'):
extension='.ogg'
extension='.ogg'
if attach['url'].endswith(extension):
if attachmentCtr>0:
attachmentStr+='<br>'
@ -2884,7 +2884,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
avatarLink+= \
' <img loading="lazy" src="'+avatarUrl+'" title="'+ \
translate['Show profile']+'" alt=" "'+avatarPosition+'/></a>'
if showAvatarOptions and fullDomain+'/users/'+nickname not in postActor:
avatarLink= \
' <a href="/users/'+nickname+'?options='+postActor+ \
@ -2914,7 +2914,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
else:
if not isPublicPost(postJsonObject):
isPublicRepeat=True
titleStr=''
galleryStr=''
isAnnounced=False
@ -2933,7 +2933,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
if showPublicOnly:
if not postContainsPublic(postJsonObject):
return ''
isModerationPost=False
if postJsonObject['object'].get('moderationStatus'):
isModerationPost=True
@ -2993,7 +2993,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
if len(replyToLink)>500:
break
replyToLink+=pageNumberParam
replyStr=''
if isPublicRepeat:
replyStr+= \
@ -3031,7 +3031,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
translate['Edit blog post']+' |" alt="'+ \
translate['Edit blog post']+ \
' |" src="/'+iconsDir+'/edit.png"/></a>'
announceStr=''
if not isModerationPost and showRepeatIcon:
# don't allow announce/repeat of your own posts
@ -3131,7 +3131,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \
'<img loading="lazy" alt="'+translate['Undo mute']+ \
' |" title="'+translate['Undo mute']+ \
' |" src="/'+iconsDir+'/unmute.png"/></a>'
replyAvatarImageInPost=''
if showRepeatIcon:
if isAnnounced:
@ -3419,7 +3419,7 @@ def htmlTimeline(defaultTimeline: str, \
manuallyApproveFollowers: bool) -> str:
"""Show the timeline as html
"""
accountDir=baseDir+'/accounts/'+nickname+'@'+domain
accountDir=baseDir+'/accounts/'+nickname+'@'+domain
# should the calendar icon be highlighted?
calendarImage='calendar.png'
@ -3477,8 +3477,8 @@ def htmlTimeline(defaultTimeline: str, \
bannerFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+bannerFile
if not os.path.isfile(bannerFilename):
bannerFile='banner.webp'
with open(cssFilename, 'r') as cssFile:
with open(cssFilename, 'r') as cssFile:
profileStyle= \
cssFile.read().replace('banner.png', \
'/users/'+nickname+'/'+bannerFile)
@ -3538,7 +3538,7 @@ def htmlTimeline(defaultTimeline: str, \
actor=httpPrefix+'://'+fullDomain+'/users/'+nickname
showIndividualPostIcons=True
followApprovals=''
followRequestsFilename= \
baseDir+'/accounts/'+nickname+'@'+domain+'/followrequests.txt'
@ -3577,7 +3577,7 @@ def htmlTimeline(defaultTimeline: str, \
#tlStr=htmlHeader(cssFilename,profileStyle,240)
if boxName!='dm':
if boxName!='tlblogs':
if boxName!='tlblogs':
if not manuallyApproveFollowers:
newPostButtonStr= \
'<a href="'+actor+'/newpost"><img loading="lazy" src="/'+ \
@ -3605,7 +3605,7 @@ def htmlTimeline(defaultTimeline: str, \
tlStr+= \
'<a href="/users/'+nickname+'"><label class="transparent">'+ \
translate['Switch to profile view']+'</label></a>'
# banner and row of buttons
tlStr+= \
'<a href="/users/'+nickname+'" title="'+ \
@ -3785,7 +3785,7 @@ def htmlTimeline(defaultTimeline: str, \
postId=item['id'].replace('/activity','').replace('/','#')
if postId in recentPostsCache['index']:
if not item.get('muted'):
if recentPostsCache['html'].get(postId):
if recentPostsCache['html'].get(postId):
currTlStr=recentPostsCache['html'][postId]
currTlStr= \
preparePostFromHtmlCache(currTlStr,boxName,pageNumber)
@ -3973,7 +3973,7 @@ def htmlIndividualPost(recentPostsCache: {},maxRecentPosts: int, \
"""Show an individual post as html
"""
iconsDir=getIconsDir(baseDir)
postStr='<script>'+contentWarningScript()+'</script>'
postStr='<script>'+contentWarningScript()+'</script>'
postStr+= \
individualPostAsHtml(recentPostsCache,maxRecentPosts, \
iconsDir,translate,None, \
@ -4023,7 +4023,7 @@ def htmlIndividualPost(recentPostsCache: {},maxRecentPosts: int, \
False,authorized,False,False,False)
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
postsCSS=cssFile.read()
if httpPrefix!='https':
@ -4051,7 +4051,7 @@ def htmlPostReplies(recentPostsCache: {},maxRecentPosts: int, \
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
postsCSS=cssFile.read()
if httpPrefix!='https':
@ -4068,7 +4068,7 @@ def htmlRemoveSharedItem(translate: {},baseDir: str,actor: str,shareName: str) -
if not os.path.isfile(sharesFile):
print('ERROR: no shares file '+sharesFile)
return None
sharesJson=loadJson(sharesFile)
sharesJson=loadJson(sharesFile)
if not sharesJson:
print('ERROR: unable to load shares.json')
return None
@ -4087,7 +4087,7 @@ def htmlRemoveSharedItem(translate: {},baseDir: str,actor: str,shareName: str) -
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
sharesStr=htmlHeader(cssFilename,profileStyle)
@ -4145,7 +4145,7 @@ def htmlDeletePost(recentPostsCache: {},maxRecentPosts: int, \
deletePostStr=None
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
if httpPrefix!='https':
@ -4209,7 +4209,7 @@ def htmlCalendarDeleteConfirm(translate: {},baseDir: str, \
deletePostStr=None
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
if httpPrefix!='https':
@ -4245,7 +4245,7 @@ def htmlFollowConfirm(translate: {},baseDir: str, \
"""Asks to confirm a follow
"""
followDomain,port=getDomainFromActor(followActor)
if os.path.isfile(baseDir+'/img/follow-background.png'):
if not os.path.isfile(baseDir+'/accounts/follow-background.png'):
copyfile(baseDir+'/img/follow-background.png', \
@ -4253,7 +4253,7 @@ def htmlFollowConfirm(translate: {},baseDir: str, \
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
followStr=htmlHeader(cssFilename,profileStyle)
@ -4287,7 +4287,7 @@ def htmlUnfollowConfirm(translate: {},baseDir: str, \
"""Asks to confirm unfollowing an actor
"""
followDomain,port=getDomainFromActor(followActor)
if os.path.isfile(baseDir+'/img/follow-background.png'):
if not os.path.isfile(baseDir+'/accounts/follow-background.png'):
copyfile(baseDir+'/img/follow-background.png', \
@ -4295,7 +4295,7 @@ def htmlUnfollowConfirm(translate: {},baseDir: str, \
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
followStr=htmlHeader(cssFilename,profileStyle)
@ -4338,7 +4338,7 @@ def htmlPersonOptions(translate: {},baseDir: str, \
"""Show options for a person: view/follow/block/report
"""
optionsDomain,optionsPort=getDomainFromActor(optionsActor)
if os.path.isfile(baseDir+'/img/options-background.png'):
if not os.path.isfile(baseDir+'/accounts/options-background.png'):
copyfile(baseDir+'/img/options-background.png', \
@ -4370,7 +4370,7 @@ def htmlPersonOptions(translate: {},baseDir: str, \
optionsLinkStr=' <input type="hidden" name="postUrl" value="'+optionsLink+'">'
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
@ -4452,7 +4452,7 @@ def htmlPersonOptions(translate: {},baseDir: str, \
# """Asks to confirm a block
# """
# blockDomain,port=getDomainFromActor(blockActor)
#
#
# if os.path.isfile(baseDir+'/img/block-background.png'):
# if not os.path.isfile(baseDir+'/accounts/block-background.png'):
# copyfile(baseDir+'/img/block-background.png',baseDir+'/accounts/block-background.png')
@ -4484,7 +4484,7 @@ def htmlUnblockConfirm(translate: {},baseDir: str, \
"""Asks to confirm unblocking an actor
"""
blockDomain,port=getDomainFromActor(blockActor)
if os.path.isfile(baseDir+'/img/block-background.png'):
if not os.path.isfile(baseDir+'/accounts/block-background.png'):
copyfile(baseDir+'/img/block-background.png', \
@ -4492,7 +4492,7 @@ def htmlUnblockConfirm(translate: {},baseDir: str, \
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
blockStr=htmlHeader(cssFilename,profileStyle)
@ -4524,7 +4524,7 @@ def htmlSearchEmojiTextEntry(translate: {}, \
"""Search for an emoji by name
"""
# emoji.json is generated so that it can be customized and the changes
# will be retained even if default_emoji.json is subsequently updated
# will be retained even if default_emoji.json is subsequently updated
if not os.path.isfile(baseDir+'/emoji/emoji.json'):
copyfile(baseDir+'/emoji/default_emoji.json', \
baseDir+'/emoji/emoji.json')
@ -4532,7 +4532,7 @@ def htmlSearchEmojiTextEntry(translate: {}, \
actor=path.replace('/search','')
nickname=getNicknameFromActor(actor)
domain,port=getDomainFromActor(actor)
if os.path.isfile(baseDir+'/img/search-background.png'):
if not os.path.isfile(baseDir+'/accounts/search-background.png'):
copyfile(baseDir+'/img/search-background.png', \
@ -4540,13 +4540,13 @@ def htmlSearchEmojiTextEntry(translate: {}, \
cssFilename=baseDir+'/epicyon-follow.css'
if os.path.isfile(baseDir+'/follow.css'):
cssFilename=baseDir+'/follow.css'
cssFilename=baseDir+'/follow.css'
with open(cssFilename, 'r') as cssFile:
profileStyle=cssFile.read()
emojiStr=htmlHeader(cssFilename,profileStyle)
emojiStr+='<div class="follow">'
emojiStr+=' <div class="followAvatar">'
emojiStr+=' <center>'
emojiStr+=' <center>'
emojiStr+= \
' <p class="followText">'+ \
translate['Enter an emoji name to search for']+'</p>'
@ -4585,7 +4585,7 @@ def htmlCalendarDay(translate: {}, \
cssFilename=baseDir+'/epicyon-calendar.css'
if os.path.isfile(baseDir+'/calendar.css'):
cssFilename=baseDir+'/calendar.css'
cssFilename=baseDir+'/calendar.css'
with open(cssFilename, 'r') as cssFile:
calendarStyle=cssFile.read()
@ -4617,15 +4617,15 @@ def htmlCalendarDay(translate: {}, \
if ev.get('startTime'):
eventDate= \
datetime.strptime(ev['startTime'], \
"%Y-%m-%dT%H:%M:%S%z")
"%Y-%m-%dT%H:%M:%S%z")
eventTime=eventDate.strftime("%H:%M").strip()
if ev.get('name'):
eventDescription=ev['name'].strip()
elif ev['type']=='Place':
if ev.get('name'):
eventPlace=ev['name']
deleteButtonStr=''
deleteButtonStr=''
if postId:
deleteButtonStr= \
'<td class="calendar__day__icons"><a href="'+actor+ \
@ -4668,7 +4668,7 @@ def htmlCalendarDay(translate: {}, \
calendarStr+=htmlFooter()
return calendarStr
def htmlCalendar(translate: {}, \
baseDir: str,path: str, \
httpPrefix: str,domainFull: str) -> str:
@ -4678,7 +4678,7 @@ def htmlCalendar(translate: {}, \
domain=domainFull
if ':' in domainFull:
domain=domainFull.split(':')[0]
monthNumber=0
dayNumber=None
year=1970
@ -4732,7 +4732,7 @@ def htmlCalendar(translate: {}, \
year,monthNumber,dayNumber, \
nickname,domain,dayEvents, \
monthName,actor)
events= \
getCalendarEvents(baseDir,nickname,domain,year,monthNumber)
@ -4758,10 +4758,10 @@ def htmlCalendar(translate: {}, \
cssFilename=baseDir+'/epicyon-calendar.css'
if os.path.isfile(baseDir+'/calendar.css'):
cssFilename=baseDir+'/calendar.css'
cssFilename=baseDir+'/calendar.css'
with open(cssFilename, 'r') as cssFile:
calendarStyle=cssFile.read()
calendarStr=htmlHeader(cssFilename,calendarStyle)
calendarStr+='<main><table class="calendar">\n'
calendarStr+='<caption class="calendar__banner--month">\n'
@ -4831,7 +4831,7 @@ def htmlCalendar(translate: {}, \
else:
calendarStr+=' <td class="calendar__day__cell"></td>\n'
calendarStr+=' </tr>\n'
calendarStr+='</tbody>\n'
calendarStr+='</table></main>\n'
calendarStr+=htmlFooter()
@ -4914,7 +4914,7 @@ def htmlSearch(translate: {}, \
followStr=htmlHeader(cssFilename,profileStyle)
followStr+='<div class="follow">'
followStr+=' <div class="followAvatar">'
followStr+=' <center>'
followStr+=' <center>'
followStr+= \
' <p class="followText">'+ \
translate['Enter an address, shared item, #hashtag, *skill or :emoji: to search for']+'</p>'
@ -4978,11 +4978,11 @@ def htmlProfileAfterSearch(recentPostsCache: {},maxRecentPosts: int, \
if searchPort!=80 and searchPort!=443:
if ':' not in searchDomain:
searchDomainFull=searchDomain+':'+str(searchPort)
profileStr=''
cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css'
cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile:
wf= \
webfingerHandle(session, \
@ -4996,7 +4996,7 @@ def htmlProfileAfterSearch(recentPostsCache: {},maxRecentPosts: int, \
personUrl=None
if wf.get('errors'):
personUrl=httpPrefix+'://'+searchDomainFull+'/users/'+searchNickname
asHeader={
'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'
}
@ -5119,4 +5119,3 @@ def htmlProfileAfterSearch(recentPostsCache: {},maxRecentPosts: int, \
break
return htmlHeader(cssFilename,profileStyle)+profileStr+htmlFooter()