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

View File

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

View File

@ -22,7 +22,7 @@ def hashPassword(password: str) -> str:
salt, 100000) salt, 100000)
pwdhash=binascii.hexlify(pwdhash) pwdhash=binascii.hexlify(pwdhash)
return (salt+pwdhash).decode('ascii') return (salt+pwdhash).decode('ascii')
def verifyPassword(storedPassword: str,providedPassword: str) -> bool: def verifyPassword(storedPassword: str,providedPassword: str) -> bool:
"""Verify a stored password against one provided by user """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 port!=80 and port!=443:
if ':' not in domain: if ':' not in domain:
domainFull=domain+':'+str(port) domainFull=domain+':'+str(port)
toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname
ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers' ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers'
@ -117,7 +117,7 @@ def sendAvailabilityViaServer(baseDir: str,session, \
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \
getPersonBox(baseDir,session,wfRequest,personCache, \ getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,nickname,domain,postToBox) projectVersion,httpPrefix,nickname,domain,postToBox)
if not inboxUrl: if not inboxUrl:
if debug: if debug:
print('DEBUG: No '+postToBox+' was found for '+handle) print('DEBUG: No '+postToBox+' was found for '+handle)
@ -126,9 +126,9 @@ def sendAvailabilityViaServer(baseDir: str,session, \
if debug: if debug:
print('DEBUG: No actor was found for '+handle) print('DEBUG: No actor was found for '+handle)
return 4 return 4
authHeader=createBasicAuthHeader(Nickname,password) authHeader=createBasicAuthHeader(Nickname,password)
headers={ headers={
'host': domain, \ 'host': domain, \
'Content-type': 'application/json', \ 'Content-type': 'application/json', \

View File

@ -14,7 +14,7 @@ def addGlobalBlock(baseDir: str, \
"""Global block which applies to all accounts """Global block which applies to all accounts
""" """
blockingFilename=baseDir+'/accounts/blocking.txt' blockingFilename=baseDir+'/accounts/blocking.txt'
if not blockNickname.startswith('#'): if not blockNickname.startswith('#'):
blockHandle=blockNickname+'@'+blockDomain blockHandle=blockNickname+'@'+blockDomain
if os.path.isfile(blockingFilename): if os.path.isfile(blockingFilename):
if blockHandle in open(blockingFilename).read(): if blockHandle in open(blockingFilename).read():
@ -54,7 +54,7 @@ def removeGlobalBlock(baseDir: str, \
"""Unblock the given global block """Unblock the given global block
""" """
unblockingFilename=baseDir+'/accounts/blocking.txt' unblockingFilename=baseDir+'/accounts/blocking.txt'
if not unblockNickname.startswith('#'): if not unblockNickname.startswith('#'):
unblockHandle=unblockNickname+'@'+unblockDomain unblockHandle=unblockNickname+'@'+unblockDomain
if os.path.isfile(unblockingFilename): if os.path.isfile(unblockingFilename):
if unblockHandle in open(unblockingFilename).read(): 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 # posts from the domain are expected to have an attributedTo field
if restrictToDomain: if restrictToDomain:
return '' return ''
if postJsonObject['object'].get('published'): if postJsonObject['object'].get('published'):
if 'T' in postJsonObject['object']['published']: if 'T' in postJsonObject['object']['published']:
blogStr+='<h3>'+postJsonObject['object']['published'].split('T')[0] 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: if ' ' in nickname or '@' in nickname or '\n' in nickname:
return None return None
blogStr='' blogStr=''
cssFilename=baseDir+'/epicyon-profile.css' cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'): if os.path.isfile(baseDir+'/epicyon.css'):
@ -424,7 +424,7 @@ def htmlBlogPageRSS(authorized: bool, session, \
if not timelineJson: if not timelineJson:
return blogRSS+rssFooter() return blogRSS+rssFooter()
if pageNumber!=None: if pageNumber!=None:
for item in timelineJson['orderedItems']: for item in timelineJson['orderedItems']:
if item['type']!='Create': if item['type']!='Create':
continue continue
@ -556,11 +556,11 @@ def htmlEditBlog(mediaInstance: bool,translate: {}, \
if os.path.isfile(baseDir+'/accounts/newpost.txt'): if os.path.isfile(baseDir+'/accounts/newpost.txt'):
with open(baseDir+'/accounts/newpost.txt', 'r') as file: 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' cssFilename=baseDir+'/epicyon-profile.css'
if os.path.isfile(baseDir+'/epicyon.css'): if os.path.isfile(baseDir+'/epicyon.css'):
cssFilename=baseDir+'/epicyon.css' cssFilename=baseDir+'/epicyon.css'
with open(cssFilename, 'r') as cssFile: with open(cssFilename, 'r') as cssFile:
editBlogCSS=cssFile.read() editBlogCSS=cssFile.read()
if httpPrefix!='https': if httpPrefix!='https':
@ -606,7 +606,7 @@ def htmlEditBlog(mediaInstance: bool,translate: {}, \
editBlogForm=htmlHeader(cssFilename,editBlogCSS) editBlogForm=htmlHeader(cssFilename,editBlogCSS)
mentionsStr='' mentionsStr=''
editBlogForm+= \ editBlogForm+= \
'<form enctype="multipart/form-data" method="POST" accept-charset="UTF-8" action="'+ \ '<form enctype="multipart/form-data" method="POST" accept-charset="UTF-8" action="'+ \
pathBase+'?'+endpoint+'?page='+str(pageNumber)+'">' 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 * The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. 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. 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 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
@ -23,7 +23,7 @@ SOFTWARE.
https://github.com/halcy/blurhash-python 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. both de- and encoding.
Very close port of the original Swift implementation by Dag Ågren. 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): def base83_encode(value, length):
""" """
Decodes an integer to a base83 string, as used in blurhash. Decodes an integer to a base83 string, as used in blurhash.
Length is how long the resulting string should be. Will complain Length is how long the resulting string should be. Will complain
if the specified length is too short. if the specified length is too short.
""" """
if int(value) // (83 ** (length)) != 0: if int(value) // (83 ** (length)) != 0:
raise ValueError("Specified length is too short to encode given value.") raise ValueError("Specified length is too short to encode given value.")
result="" result=""
for i in range(1,length+1): for i in range(1,length+1):
digit=int(value) // (83 ** (length - i)) % 83 digit=int(value) // (83 ** (length - i)) % 83
@ -90,44 +90,44 @@ def blurhash_components(blurhash):
""" """
if len(blurhash) < 6: if len(blurhash) < 6:
raise ValueError("BlurHash must be at least 6 characters long.") raise ValueError("BlurHash must be at least 6 characters long.")
# Decode metadata # Decode metadata
size_info=base83_decode(blurhash[0]) size_info=base83_decode(blurhash[0])
size_y=int(size_info / 9)+1 size_y=int(size_info / 9)+1
size_x=(size_info % 9)+1 size_x=(size_info % 9)+1
return size_x, size_y return size_x, size_y
def blurhash_decode(blurhash,width,height,punch=1.0,linear=False): def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
""" """
Decodes the given blurhash to an image of the specified size. 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 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. RGB back.
The punch parameter can be used to de- or increase the contrast of the The punch parameter can be used to de- or increase the contrast of the
resulting image. resulting image.
As per the original implementation it is suggested to only decode As per the original implementation it is suggested to only decode
to a relatively small size and then scale the result up, as it to a relatively small size and then scale the result up, as it
basically looks the same anyways. basically looks the same anyways.
""" """
if len(blurhash) < 6: if len(blurhash) < 6:
raise ValueError("BlurHash must be at least 6 characters long.") raise ValueError("BlurHash must be at least 6 characters long.")
# Decode metadata # Decode metadata
size_info=base83_decode(blurhash[0]) size_info=base83_decode(blurhash[0])
size_y=int(size_info / 9)+1 size_y=int(size_info / 9)+1
size_x=(size_info % 9)+1 size_x=(size_info % 9)+1
quant_max_value=base83_decode(blurhash[1]) quant_max_value=base83_decode(blurhash[1])
real_max_value=(float(quant_max_value+1)/166.0)*punch real_max_value=(float(quant_max_value+1)/166.0)*punch
# Make sure we at least have the right number of characters # Make sure we at least have the right number of characters
if len(blurhash) != 4+2*size_x*size_y: if len(blurhash) != 4+2*size_x*size_y:
raise ValueError("Invalid BlurHash length.") raise ValueError("Invalid BlurHash length.")
# Decode DC component # Decode DC component
dc_value=base83_decode(blurhash[2:6]) dc_value=base83_decode(blurhash[2:6])
colours=[( 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 >> 8) & 255),
srgb_to_linear(dc_value & 255) srgb_to_linear(dc_value & 255)
)] )]
# Decode AC components # Decode AC components
for component in range(1, size_x * size_y): for component in range(1, size_x * size_y):
ac_value=base83_decode(blurhash[4+component*2:4+(component+1)*2]) 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(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 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. # consumable by something like numpy or PIL.
pixels=[] pixels=[]
for y in range(height): for y in range(height):
@ -172,24 +172,24 @@ def blurhash_decode(blurhash,width,height,punch=1.0,linear=False):
pixel_row.append(pixel) pixel_row.append(pixel)
pixels.append(pixel_row) pixels.append(pixel_row)
return pixels return pixels
def blurhash_encode(image,components_x=4,components_y=4,linear=False): 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. 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 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). 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 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 useful if you want to encode a version of your image resized to a smaller size (which
you should ideally do in linear colour). 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.") raise ValueError("x and y component counts must be between 1 and 9 inclusive.")
height=float(len(image)) height=float(len(image))
width=float(len(image[0])) width=float(len(image[0]))
# Convert to linear if neeeded # Convert to linear if neeeded
image_linear=[] image_linear=[]
if linear==False: 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) image_linear.append(image_linear_line)
else: else:
image_linear=image image_linear=image
# Calculate components # Calculate components
components=[] components=[]
max_ac_component=0.0 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[0] += basis * image_linear[y][x][0]
component[1] += basis * image_linear[y][x][1] component[1] += basis * image_linear[y][x][1]
component[2] += basis * image_linear[y][x][2] component[2] += basis * image_linear[y][x][2]
component[0] /= (width * height) component[0] /= (width * height)
component[1] /= (width * height) component[1] /= (width * height)
component[2] /= (width * height) component[2] /= (width * height)
components.append(component) components.append(component)
if not (i==0 and j==0): if not (i==0 and j==0):
max_ac_component= \ max_ac_component= \
max(max_ac_component,abs(component[0]), \ max(max_ac_component,abs(component[0]), \
abs(component[1]),abs(component[2])) abs(component[1]),abs(component[2]))
# Encode components # Encode components
dc_value= \ dc_value= \
(linear_to_srgb(components[0][0]) << 16)+ \ (linear_to_srgb(components[0][0]) << 16)+ \
(linear_to_srgb(components[0][1]) << 8)+ \ (linear_to_srgb(components[0][1]) << 8)+ \
linear_to_srgb(components[0][2]) linear_to_srgb(components[0][2])
quant_max_ac_component= \ quant_max_ac_component= \
int(max(0, min(82, math.floor(max_ac_component * 166 - 0.5)))) int(max(0, min(82, math.floor(max_ac_component * 166 - 0.5))))
ac_component_norm_factor= \ ac_component_norm_factor= \
float(quant_max_ac_component+1) / 166.0 float(quant_max_ac_component+1) / 166.0
ac_values=[] ac_values=[]
for r, g, b in components[1:]: for r, g, b in components[1:]:
ac_values.append( 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(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)))) 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 # Build final blurhash
blurhash="" blurhash=""
blurhash += base83_encode((components_x - 1) + (components_y - 1) * 9, 1) 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) blurhash += base83_encode(dc_value, 4)
for ac_value in ac_values: for ac_value in ac_values:
blurhash += base83_encode(ac_value, 2) blurhash += base83_encode(ac_value, 2)
return blurhash return blurhash

View File

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

View File

@ -11,7 +11,7 @@ import time
import datetime import datetime
from utils import loadJson from utils import loadJson
from utils import saveJson from utils import saveJson
def storePersonInCache(baseDir: str,personUrl: str,personJson: {},personCache: {}) -> None: def storePersonInCache(baseDir: str,personUrl: str,personJson: {},personCache: {}) -> None:
"""Store an actor in the cache """Store an actor in the cache
""" """
@ -41,7 +41,7 @@ def getPersonFromCache(baseDir: str,personUrl: str,personCache: {}) -> {}:
if personJson: if personJson:
storePersonInCache(baseDir,personUrl,personJson,personCache) storePersonInCache(baseDir,personUrl,personJson,personCache)
loadedFromFile=True loadedFromFile=True
if personCache.get(personUrl): if personCache.get(personUrl):
if not loadedFromFile: if not loadedFromFile:
# update the timestamp for the last time the actor was retrieved # 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 'inbox:cw' in capabilityList:
if debug: if debug:
print('DEBUG: inbox post rejected because inbox:cw, summary missing') print('DEBUG: inbox post rejected because inbox:cw, summary missing')
return False return False
if 'inbox:write' in capabilityList: if 'inbox:write' in capabilityList:
return True return True
return True return True
@ -102,7 +102,7 @@ def capabilitiesRequest(baseDir: str,httpPrefix: str,domain: str, \
"actor": requestedActor "actor": requestedActor
} }
return ocapRequest return ocapRequest
def capabilitiesAccept(baseDir: str,httpPrefix: str, \ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
nickname: str,domain: str, port: int, \ nickname: str,domain: str, port: int, \
acceptedActor: str, saveToFile: bool, \ acceptedActor: str, saveToFile: bool, \
@ -119,7 +119,7 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
if port!=80 and port !=443: if port!=80 and port !=443:
if ':' not in domain: if ':' not in domain:
fullDomain=domain+':'+str(port) fullDomain=domain+':'+str(port)
# make directories to store capabilities # make directories to store capabilities
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,acceptedActor,'accept') ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,acceptedActor,'accept')
if not ocapFilename: if not ocapFilename:
@ -129,14 +129,14 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
# if the capability already exists then load it from file # if the capability already exists then load it from file
if os.path.isfile(ocapFilename): if os.path.isfile(ocapFilename):
ocapAccept=loadJson(ocapFilename) ocapAccept=loadJson(ocapFilename)
# otherwise create a new capability # otherwise create a new capability
if not ocapAccept: if not ocapAccept:
acceptedActorNickname=getNicknameFromActor(acceptedActor) acceptedActorNickname=getNicknameFromActor(acceptedActor)
if not acceptedActorNickname: if not acceptedActorNickname:
print('WARN: unable to find nickname in '+acceptedActor) print('WARN: unable to find nickname in '+acceptedActor)
return None return None
acceptedActorDomain,acceptedActorPort=getDomainFromActor(acceptedActor) acceptedActorDomain,acceptedActorPort=getDomainFromActor(acceptedActor)
if acceptedActorPort: if acceptedActorPort:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+str(acceptedActorPort)+'#'+createPassword(32) ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+str(acceptedActorPort)+'#'+createPassword(32)
else: else:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+createPassword(32) ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+createPassword(32)
@ -185,7 +185,7 @@ def capabilitiesUpdate(baseDir: str,httpPrefix: str, \
if port!=80 and port !=443: if port!=80 and port !=443:
if ':' not in domain: if ':' not in domain:
fullDomain=domain+':'+str(port) fullDomain=domain+':'+str(port)
# Get the filename of the capability # Get the filename of the capability
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept') ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept')
if not ocapFilename: if not ocapFilename:

View File

@ -92,7 +92,7 @@ def replaceEmojiFromTags(content: str,tag: [],messageType: str) -> str:
htmlClass='emoji' htmlClass='emoji'
if messageType=='post header': if messageType=='post header':
htmlClass='emojiheader' htmlClass='emojiheader'
if messageType=='profile': if messageType=='profile':
htmlClass='emojiprofile' htmlClass='emojiprofile'
emojiHtml="<img src=\""+tagItem['icon']['url']+"\" alt=\""+tagItem['name'].replace(':','')+"\" align=\"middle\" class=\""+htmlClass+"\"/>" 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 return True
# @nick@domain # @nick@domain
if not (possibleDomain=='localhost' or '.' in possibleDomain): if not (possibleDomain=='localhost' or '.' in possibleDomain):
return False return False
recipientActor=httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname recipientActor=httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname
if recipientActor not in recipients: if recipientActor not in recipients:
recipients.append(recipientActor) recipients.append(recipientActor)
@ -389,7 +389,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
content=content.replace('\n',' --linebreak-- ') content=content.replace('\n',' --linebreak-- ')
content=addMusicTag(content,'nowplaying') content=addMusicTag(content,'nowplaying')
words=content.replace(',',' ').replace(';',' ').split(' ') words=content.replace(',',' ').replace(';',' ').split(' ')
# remove . for words which are not mentions # remove . for words which are not mentions
wordCtr=0 wordCtr=0
newWords=[] newWords=[]
@ -461,7 +461,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
content=removeLongWords(content,maxWordLength,longWordsList) content=removeLongWords(content,maxWordLength,longWordsList)
content=content.replace(' --linebreak-- ','</p><p>') content=content.replace(' --linebreak-- ','</p><p>')
return '<p>'+content+'</p>' return '<p>'+content+'</p>'
def getMentionsFromHtml(htmlText: str,matchStr="<span class=\"h-card\"><a href=\"") -> []: def getMentionsFromHtml(htmlText: str,matchStr="<span class=\"h-card\"><a href=\"") -> []:
"""Extracts mentioned actors from the given html content string """Extracts mentioned actors from the given html content string
""" """
@ -521,7 +521,7 @@ def saveMediaInFormPOST(mediaBytes,debug: bool, \
mediaLocation=-1 mediaLocation=-1
searchStr='' searchStr=''
filename=None filename=None
# directly search the binary array for the beginning # directly search the binary array for the beginning
# of an image # of an image
extensionList= { extensionList= {
@ -579,7 +579,7 @@ def saveMediaInFormPOST(mediaBytes,debug: bool, \
def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}: def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
"""Returns a dictionary containing the text fields of a http form POST """Returns a dictionary containing the text fields of a http form POST
The boundary argument comes from the http header The boundary argument comes from the http header
""" """
msg=email.parser.BytesParser().parsebytes(postBytes) msg=email.parser.BytesParser().parsebytes(postBytes)
if debug: if debug:
print('DEBUG: POST arriving '+msg.get_payload(decode=True).decode('utf-8')) print('DEBUG: POST arriving '+msg.get_payload(decode=True).decode('utf-8'))
@ -590,7 +590,7 @@ def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
if f=='--': if f=='--':
continue continue
if ' name="' not in f: if ' name="' not in f:
continue continue
postStr=f.split(' name="',1)[1] postStr=f.split(' name="',1)[1]
if '"' not in postStr: if '"' not in postStr:
continue continue
@ -600,7 +600,7 @@ def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}:
continue continue
if '\r\n' not in postValueStr: if '\r\n' not in postValueStr:
continue continue
postLines=postValueStr.split('\r\n') postLines=postValueStr.split('\r\n')
postValue='' postValue=''
if len(postLines)>2: if len(postLines)>2:
for line in range(2,len(postLines)-1): 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', \ 'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix,True,clientToServer,federationList, \ httpPrefix,True,clientToServer,federationList, \
sendThreads,postLog,cachedWebfingers,personCache,debug) sendThreads,postLog,cachedWebfingers,personCache,debug)
return newDelete return newDelete
def sendDeleteViaServer(baseDir: str,session, \ def sendDeleteViaServer(baseDir: str,session, \
@ -131,7 +131,7 @@ def sendDeleteViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \ getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \ projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox) fromDomain,postToBox)
if not inboxUrl: if not inboxUrl:
if debug: if debug:
print('DEBUG: No '+postToBox+' was found for '+handle) print('DEBUG: No '+postToBox+' was found for '+handle)
@ -140,9 +140,9 @@ def sendDeleteViaServer(baseDir: str,session, \
if debug: if debug:
print('DEBUG: No actor was found for '+handle) print('DEBUG: No actor was found for '+handle)
return 4 return 4
authHeader=createBasicAuthHeader(fromNickname,password) authHeader=createBasicAuthHeader(fromNickname,password)
headers={ headers={
'host': fromDomain, \ 'host': fromDomain, \
'Content-type': 'application/json', \ 'Content-type': 'application/json', \
@ -257,14 +257,14 @@ def outboxDelete(baseDir: str,httpPrefix: str, \
if deleteNickname!=nickname: if deleteNickname!=nickname:
if debug: if debug:
print("DEBUG: you can't delete a post which wasn't created by you (nickname does not match)") print("DEBUG: you can't delete a post which wasn't created by you (nickname does not match)")
return return
deleteDomain,deletePort=getDomainFromActor(messageId) deleteDomain,deletePort=getDomainFromActor(messageId)
if ':' in domain: if ':' in domain:
domain=domain.split(':')[0] domain=domain.split(':')[0]
if deleteDomain!=domain: if deleteDomain!=domain:
if debug: if debug:
print("DEBUG: you can't delete a post which wasn't created by you (domain does not match)") print("DEBUG: you can't delete a post which wasn't created by you (domain does not match)")
return return
removeModerationPostFromIndex(baseDir,messageId,debug) removeModerationPostFromIndex(baseDir,messageId,debug)
postFilename=locatePost(baseDir,deleteNickname,deleteDomain,messageId) postFilename=locatePost(baseDir,deleteNickname,deleteDomain,messageId)
if not postFilename: if not postFilename:

View File

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

View File

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

View File

@ -88,7 +88,7 @@ def isFollowingActor(baseDir: str,nickname: str,domain: str,actor: str) -> bool:
handle=nickname+'@'+domain handle=nickname+'@'+domain
if not os.path.isdir(baseDir+'/accounts/'+handle): if not os.path.isdir(baseDir+'/accounts/'+handle):
return False return False
followingFile=baseDir+'/accounts/'+handle+'/following.txt' followingFile=baseDir+'/accounts/'+handle+'/following.txt'
if not os.path.isfile(followingFile): if not os.path.isfile(followingFile):
return False return False
if actor in open(followingFile).read(): if actor in open(followingFile).read():
@ -284,7 +284,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \
return None return None
# handle page numbers # handle page numbers
headerOnly=True headerOnly=True
pageNumber=None pageNumber=None
if '?page=' in path: if '?page=' in path:
pageNumber=path.split('?page=')[1] pageNumber=path.split('?page=')[1]
if pageNumber=='true' or not authenticated: if pageNumber=='true' or not authenticated:
@ -296,7 +296,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \
pass pass
path=path.split('?page=')[0] path=path.split('?page=')[0]
headerOnly=False headerOnly=False
if not path.endswith('/'+followFile): if not path.endswith('/'+followFile):
return None return None
nickname=None nickname=None
@ -495,7 +495,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
'/channel/' not in messageJson['actor'] and \ '/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']: '/profile/' not in messageJson['actor']:
if debug: if debug:
print('DEBUG: "users" or "profile" missing from actor') print('DEBUG: "users" or "profile" missing from actor')
return False return False
domain,tempPort=getDomainFromActor(messageJson['actor']) domain,tempPort=getDomainFromActor(messageJson['actor'])
fromPort=port fromPort=port
@ -534,7 +534,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
if tempPort: if tempPort:
if tempPort!=80 and tempPort!=443: if tempPort!=80 and tempPort!=443:
if ':' not in domainToFollow: if ':' not in domainToFollow:
domainToFollowFull=domainToFollow+':'+str(tempPort) domainToFollowFull=domainToFollow+':'+str(tempPort)
nicknameToFollow=getNicknameFromActor(messageJson['object']) nicknameToFollow=getNicknameFromActor(messageJson['object'])
if not nicknameToFollow: if not nicknameToFollow:
if debug: if debug:
@ -547,7 +547,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
print('DEBUG: followed account not found - '+ \ print('DEBUG: followed account not found - '+ \
baseDir+'/accounts/'+handleToFollow) baseDir+'/accounts/'+handleToFollow)
return True return True
if isFollowerOfPerson(baseDir, \ if isFollowerOfPerson(baseDir, \
nicknameToFollow,domainToFollowFull, \ nicknameToFollow,domainToFollowFull, \
nickname,domainFull): nickname,domainFull):
@ -556,13 +556,13 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
' is already a follower of '+ \ ' is already a follower of '+ \
nicknameToFollow+'@'+domainToFollow) nicknameToFollow+'@'+domainToFollow)
return True return True
# what is the followers policy? # what is the followers policy?
approveHandle=nickname+'@'+domainFull approveHandle=nickname+'@'+domainFull
if followApprovalRequired(baseDir,nicknameToFollow, \ if followApprovalRequired(baseDir,nicknameToFollow, \
domainToFollow,debug,approveHandle): domainToFollow,debug,approveHandle):
print('Follow approval is required') print('Follow approval is required')
if not domain.endswith('.onion'): if not domain.endswith('.onion'):
if noOfFollowRequests(baseDir, \ if noOfFollowRequests(baseDir, \
nicknameToFollow,domainToFollow, \ nicknameToFollow,domainToFollow, \
nickname,domain,fromPort, \ nickname,domain,fromPort, \
@ -730,10 +730,10 @@ def sendFollowRequest(session,baseDir: str, \
personCache: {},debug : bool, \ personCache: {},debug : bool, \
projectVersion: str) -> {}: projectVersion: str) -> {}:
"""Gets the json object for sending a follow request """Gets the json object for sending a follow request
""" """
if not domainPermitted(followDomain,federationList): if not domainPermitted(followDomain,federationList):
return None return None
fullDomain=domain fullDomain=domain
followActor=httpPrefix+'://'+domain+'/users/'+nickname followActor=httpPrefix+'://'+domain+'/users/'+nickname
if port: if port:
@ -749,7 +749,7 @@ def sendFollowRequest(session,baseDir: str, \
requestDomain=followDomain+':'+str(followPort) requestDomain=followDomain+':'+str(followPort)
statusNumber,published=getStatusNumber() statusNumber,published=getStatusNumber()
if followNickname: if followNickname:
followedId=followHttpPrefix+'://'+requestDomain+'/users/'+followNickname followedId=followHttpPrefix+'://'+requestDomain+'/users/'+followNickname
followHandle=followNickname+'@'+requestDomain followHandle=followNickname+'@'+requestDomain
@ -812,7 +812,7 @@ def sendFollowRequestViaServer(baseDir: str,session, \
if ':' not in followDomain: if ':' not in followDomain:
followDomainFull=followDomain+':'+str(followPort) followDomainFull=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname
statusNumber,published=getStatusNumber() statusNumber,published=getStatusNumber()
@ -842,7 +842,7 @@ def sendFollowRequestViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \ getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \ projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox) fromDomain,postToBox)
if not inboxUrl: if not inboxUrl:
if debug: if debug:
print('DEBUG: No '+postToBox+' was found for '+handle) print('DEBUG: No '+postToBox+' was found for '+handle)
@ -851,9 +851,9 @@ def sendFollowRequestViaServer(baseDir: str,session, \
if debug: if debug:
print('DEBUG: No actor was found for '+handle) print('DEBUG: No actor was found for '+handle)
return 4 return 4
authHeader=createBasicAuthHeader(fromNickname,password) authHeader=createBasicAuthHeader(fromNickname,password)
headers={ headers={
'host': fromDomain, \ 'host': fromDomain, \
'Content-type': 'application/json', \ 'Content-type': 'application/json', \
@ -895,7 +895,7 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
if ':' not in followDomain: if ':' not in followDomain:
followDomainFull=followDomain+':'+str(followPort) followDomainFull=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname
statusNumber,published=getStatusNumber() statusNumber,published=getStatusNumber()
@ -930,7 +930,7 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
getPersonBox(baseDir,session,wfRequest,personCache, \ getPersonBox(baseDir,session,wfRequest,personCache, \
projectVersion,httpPrefix,fromNickname, \ projectVersion,httpPrefix,fromNickname, \
fromDomain,postToBox) fromDomain,postToBox)
if not inboxUrl: if not inboxUrl:
if debug: if debug:
print('DEBUG: No '+postToBox+' was found for '+handle) print('DEBUG: No '+postToBox+' was found for '+handle)
@ -939,9 +939,9 @@ def sendUnfollowRequestViaServer(baseDir: str,session, \
if debug: if debug:
print('DEBUG: No actor was found for '+handle) print('DEBUG: No actor was found for '+handle)
return 4 return 4
authHeader=createBasicAuthHeader(fromNickname,password) authHeader=createBasicAuthHeader(fromNickname,password)
headers={ headers={
'host': fromDomain, \ 'host': fromDomain, \
'Content-type': 'application/json', \ 'Content-type': 'application/json', \
@ -1008,7 +1008,7 @@ def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}:
if ocapJson.get('id'): if ocapJson.get('id'):
if debug: if debug:
print('DEBUG: capabilities id found for '+account) print('DEBUG: capabilities id found for '+account)
recipientsDict[account]=ocapJson['id'] recipientsDict[account]=ocapJson['id']
else: else:
if debug: if debug:
@ -1058,7 +1058,7 @@ def outboxUndoFollow(baseDir: str,messageJson: {},debug: bool) -> None:
if portFollower!=80 and portFollower!=443: if portFollower!=80 and portFollower!=443:
if ':' not in domainFollower: if ':' not in domainFollower:
domainFollowerFull=domainFollower+':'+str(portFollower) domainFollowerFull=domainFollower+':'+str(portFollower)
nicknameFollowing=getNicknameFromActor(messageJson['object']['object']) nicknameFollowing=getNicknameFromActor(messageJson['object']['object'])
if not nicknameFollowing: if not nicknameFollowing:
print('WARN: unable to find nickname in '+messageJson['object']['object']) 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=[] calendarPostIds=[]
recreateEventsFile=False recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile: with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile: for postId in eventsFile:
postId=postId.replace('\n','') postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId) postFilename=locatePost(baseDir,nickname,domain,postId)
@ -134,7 +134,7 @@ def todaysEventsCheck(baseDir: str,nickname: str,domain: str) -> bool:
return False return False
eventsExist=False eventsExist=False
with open(calendarFilename,'r') as eventsFile: with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile: for postId in eventsFile:
postId=postId.replace('\n','') postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId) postFilename=locatePost(baseDir,nickname,domain,postId)
@ -180,7 +180,7 @@ def thisWeeksEventsCheck(baseDir: str,nickname: str,domain: str) -> bool:
return False return False
eventsExist=False eventsExist=False
with open(calendarFilename,'r') as eventsFile: with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile: for postId in eventsFile:
postId=postId.replace('\n','') postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId) 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("%m"))==monthNumber and \
(int(eventTime.strftime("%d"))>dayNumber and \ (int(eventTime.strftime("%d"))>dayNumber and \
int(eventTime.strftime("%d"))<=dayNumber+6): int(eventTime.strftime("%d"))<=dayNumber+6):
eventsExist=True eventsExist=True
break break
return eventsExist return eventsExist
@ -233,7 +233,7 @@ def getThisWeeksEvents(baseDir: str,nickname: str,domain: str) -> {}:
calendarPostIds=[] calendarPostIds=[]
recreateEventsFile=False recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile: with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile: for postId in eventsFile:
postId=postId.replace('\n','') postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId) postFilename=locatePost(baseDir,nickname,domain,postId)
@ -317,7 +317,7 @@ def getCalendarEvents(baseDir: str,nickname: str,domain: str, \
calendarPostIds=[] calendarPostIds=[]
recreateEventsFile=False recreateEventsFile=False
with open(calendarFilename,'r') as eventsFile: with open(calendarFilename,'r') as eventsFile:
for postId in eventsFile: for postId in eventsFile:
postId=postId.replace('\n','') postId=postId.replace('\n','')
postFilename=locatePost(baseDir,nickname,domain,postId) postFilename=locatePost(baseDir,nickname,domain,postId)
@ -362,7 +362,7 @@ def getCalendarEvents(baseDir: str,nickname: str,domain: str, \
for postId in calendarPostIds: for postId in calendarPostIds:
calendarFile.write(postId+'\n') calendarFile.write(postId+'\n')
calendarFile.close() calendarFile.close()
return events return events
def removeCalendarEvent(baseDir: str,nickname: str,domain: str, \ 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 # see https://tools.ietf.org/html/draft-cavage-http-signatures-06
try: try:
from Cryptodome.PublicKey import RSA from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256 from Cryptodome.Hash import SHA256
from Cryptodome.Signature import pkcs1_15 from Cryptodome.Signature import pkcs1_15
@ -175,7 +175,7 @@ def verifyPostHeaders(httpPrefix: str,publicKeyPem: str,headers: dict, \
if debug: if debug:
print('DEBUG: verifyPostHeaders '+method) print('DEBUG: verifyPostHeaders '+method)
publicKeyPem=RSA.import_key(publicKeyPem) publicKeyPem=RSA.import_key(publicKeyPem)
# Build a dictionary of the signature values # Build a dictionary of the signature values
signatureHeader=headers['signature'] signatureHeader=headers['signature']

View File

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

26
like.py
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

122
posts.py
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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