From 5bc7a337a00bfe6f73ad312e3923e2bb3fe0ffd8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 09:10:21 +0000 Subject: [PATCH 01/63] Comment --- epicyon.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/epicyon.py b/epicyon.py index fe98b2e05..cb6b0637d 100644 --- a/epicyon.py +++ b/epicyon.py @@ -71,7 +71,9 @@ from socnet import instancesGraph import argparse -def str2bool(v): +def str2bool(v) -> bool: + """Returns true if the given value is a boolean + """ if isinstance(v, bool): return v if v.lower() in ('yes', 'true', 't', 'y', '1'): From d354b8a339993deb2e978a9bbd09fac1730f2d3a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 09:52:13 +0000 Subject: [PATCH 02/63] Option to list blocked domains for a handle --- epicyon.py | 43 +++++++++++++++++++++++++++++++++++++++++++ posts.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/epicyon.py b/epicyon.py index cb6b0637d..0fae722f0 100644 --- a/epicyon.py +++ b/epicyon.py @@ -16,6 +16,7 @@ from skills import setSkillLevel from roles import setRole from webfinger import webfingerHandle from posts import getPublicPostDomains +from posts import getPublicPostDomainsBlocked from posts import sendBlockViaServer from posts import sendUndoBlockViaServer from posts import createPublicPost @@ -157,6 +158,10 @@ parser.add_argument('--postDomains', dest='postDomains', type=str, default=None, help='Show domains referenced in public ' 'posts for the given handle') +parser.add_argument('--postDomainsBlocked', dest='postDomainsBlocked', + type=str, default=None, + help='Show blocked domains referenced in public ' + 'posts for the given handle') parser.add_argument('--socnet', dest='socnet', type=str, default=None, help='Show dot diagram for social network ' @@ -475,6 +480,44 @@ if args.postDomains: print(postDomain) sys.exit() +if args.postDomainsBlocked: + # Domains which were referenced in public posts by a + # given handle but which are globally blocked on this instance + if '@' not in args.postDomainsBlocked: + if '/users/' in args.postDomainsBlocked: + postsNickname = getNicknameFromActor(args.posts) + postsDomain, postsPort = getDomainFromActor(args.posts) + args.postDomainsBlocked = postsNickname + '@' + postsDomain + if postsPort: + if postsPort != 80 and postsPort != 443: + args.postDomainsBlocked += ':' + str(postsPort) + else: + print('Syntax: --postDomainsBlocked nickname@domain') + sys.exit() + if not args.http: + args.port = 443 + nickname = args.postDomainsBlocked.split('@')[0] + domain = args.postDomainsBlocked.split('@')[1] + proxyType = None + if args.tor or domain.endswith('.onion'): + proxyType = 'tor' + if domain.endswith('.onion'): + args.port = 80 + elif args.i2p or domain.endswith('.i2p'): + proxyType = 'i2p' + if domain.endswith('.i2p'): + args.port = 80 + elif args.gnunet: + proxyType = 'gnunet' + domainList = [] + domainList = getPublicPostDomainsBlocked(baseDir, nickname, domain, + proxyType, args.port, + httpPrefix, debug, + __version__, domainList) + for postDomain in domainList: + print(postDomain) + sys.exit() + if args.socnet: if ',' not in args.socnet: print('Syntax: ' diff --git a/posts.py b/posts.py index 3a594a811..cee86d534 100644 --- a/posts.py +++ b/posts.py @@ -3288,6 +3288,42 @@ def getPublicPostDomains(baseDir: str, nickname: str, domain: str, return postDomains +def getPublicPostDomainsBlocked(baseDir: str, nickname: str, domain: str, + proxyType: str, port: int, httpPrefix: str, + debug: bool, projectVersion: str, + domainList=[]) -> []: + """ Returns a list of domains referenced within public posts which + are globally blocked on this instance + """ + postDomains = \ + getPublicPostDomains(baseDir, nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, + domainList) + if not postDomains: + return [] + + blockingFilename = baseDir + '/accounts/blocking.txt' + if not os.path.isfile(blockingFilename): + return [] + + # read the blocked domains as a single string + blockedStr = '' + with open(blockingFilename, 'r') as fp: + blockedStr = fp.read() + + blockedDomains = [] + for domainName in postDomains: + if '@' not in domainName: + continue + # get the domain after the @ + domainName = domainName.split('@')[1].strip() + if domainName in blockedStr: + blockedDomains.append(domainName) + + return blockedDomains + + def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, followerUrl, updateCaps: [], From 148fb121b3be125c0f2c6d8b6b7886514dbad7ee Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 10:05:23 +0000 Subject: [PATCH 03/63] Optionally pass session --- epicyon.py | 6 ++++-- posts.py | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/epicyon.py b/epicyon.py index 0fae722f0..edb04cbfd 100644 --- a/epicyon.py +++ b/epicyon.py @@ -472,7 +472,8 @@ if args.postDomains: elif args.gnunet: proxyType = 'gnunet' domainList = [] - domainList = getPublicPostDomains(baseDir, nickname, domain, + domainList = getPublicPostDomains(None, + baseDir, nickname, domain, proxyType, args.port, httpPrefix, debug, __version__, domainList) @@ -510,7 +511,8 @@ if args.postDomainsBlocked: elif args.gnunet: proxyType = 'gnunet' domainList = [] - domainList = getPublicPostDomainsBlocked(baseDir, nickname, domain, + domainList = getPublicPostDomainsBlocked(None, + baseDir, nickname, domain, proxyType, args.port, httpPrefix, debug, __version__, domainList) diff --git a/posts.py b/posts.py index cee86d534..6fb17239a 100644 --- a/posts.py +++ b/posts.py @@ -3240,13 +3240,14 @@ def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str, projectVersion, httpPrefix, domain) -def getPublicPostDomains(baseDir: str, nickname: str, domain: str, +def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, domainList=[]) -> []: """ Returns a list of domains referenced within public posts """ - session = createSession(proxyType) + if not session: + session = createSession(proxyType) if not session: return domainList personCache = {} @@ -3288,7 +3289,8 @@ def getPublicPostDomains(baseDir: str, nickname: str, domain: str, return postDomains -def getPublicPostDomainsBlocked(baseDir: str, nickname: str, domain: str, +def getPublicPostDomainsBlocked(session, baseDir: str, + nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, domainList=[]) -> []: @@ -3296,7 +3298,7 @@ def getPublicPostDomainsBlocked(baseDir: str, nickname: str, domain: str, are globally blocked on this instance """ postDomains = \ - getPublicPostDomains(baseDir, nickname, domain, + getPublicPostDomains(session, baseDir, nickname, domain, proxyType, port, httpPrefix, debug, projectVersion, domainList) From e70158ed51324fe7f5384ee473aae617604c474e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 10:12:36 +0000 Subject: [PATCH 04/63] Evilness check --- posts.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/posts.py b/posts.py index 6fb17239a..8f7355160 100644 --- a/posts.py +++ b/posts.py @@ -29,6 +29,7 @@ from session import postJsonString from session import postImage from webfinger import webfingerHandle from httpsig import createSignedHeader +from utils import isEvil from utils import removeIdEnding from utils import siteIsActive from utils import getCachedPostFilename @@ -3320,6 +3321,9 @@ def getPublicPostDomainsBlocked(session, baseDir: str, continue # get the domain after the @ domainName = domainName.split('@')[1].strip() + if isEvil(domainName): + blockedDomains.append(domainName) + continue if domainName in blockedStr: blockedDomains.append(domainName) From bdb5fa580497ea8975738e541f4b1507b5c9121c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 10:20:58 +0000 Subject: [PATCH 05/63] Function for non-mutuals --- follow.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/follow.py b/follow.py index ea3ac55b0..2dd88048a 100644 --- a/follow.py +++ b/follow.py @@ -112,8 +112,7 @@ def isFollowingActor(baseDir: str, def getMutualsOfPerson(baseDir: str, - nickname: str, domain: str, - followFile='following.txt') -> []: + nickname: str, domain: str) -> []: """Returns the mutuals of a person i.e. accounts which they follow and which also follow back """ @@ -128,6 +127,22 @@ def getMutualsOfPerson(baseDir: str, return mutuals +def getNonMutualsOfPerson(baseDir: str, + nickname: str, domain: str) -> []: + """Returns the followers who are not mutuals of a person + i.e. accounts which follow you but you don't follow them + """ + followers = \ + getFollowersOfPerson(baseDir, nickname, domain, 'followers') + following = \ + getFollowersOfPerson(baseDir, nickname, domain, 'following') + nonMutuals = [] + for handle in following: + if handle not in followers: + nonMutuals.append(handle) + return nonMutuals + + def getFollowersOfPerson(baseDir: str, nickname: str, domain: str, followFile='following.txt') -> []: From 88ac5bb75cae0a48823e056ce04716d4b0cb9325 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 12:33:28 +0000 Subject: [PATCH 06/63] Checking non-mutual followers --- epicyon.py | 43 +++++++++++++++++++++++++++++++++++++++++++ posts.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/epicyon.py b/epicyon.py index edb04cbfd..3a1d52789 100644 --- a/epicyon.py +++ b/epicyon.py @@ -15,6 +15,7 @@ from person import deactivateAccount from skills import setSkillLevel from roles import setRole from webfinger import webfingerHandle +from posts import checkDomains from posts import getPublicPostDomains from posts import getPublicPostDomainsBlocked from posts import sendBlockViaServer @@ -162,6 +163,10 @@ parser.add_argument('--postDomainsBlocked', dest='postDomainsBlocked', type=str, default=None, help='Show blocked domains referenced in public ' 'posts for the given handle') +parser.add_argument('--checkDomains', dest='checkDomains', type=str, + default=None, + help='Check domains of non-mutual followers for ' + 'domains which are globally blocked by this instance') parser.add_argument('--socnet', dest='socnet', type=str, default=None, help='Show dot diagram for social network ' @@ -520,6 +525,44 @@ if args.postDomainsBlocked: print(postDomain) sys.exit() +if args.checkDomains: + # Domains which were referenced in public posts by a + # given handle but which are globally blocked on this instance + if '@' not in args.checkDomains: + if '/users/' in args.checkDomains: + postsNickname = getNicknameFromActor(args.posts) + postsDomain, postsPort = getDomainFromActor(args.posts) + args.checkDomains = postsNickname + '@' + postsDomain + if postsPort: + if postsPort != 80 and postsPort != 443: + args.checkDomains += ':' + str(postsPort) + else: + print('Syntax: --checkDomains nickname@domain') + sys.exit() + if not args.http: + args.port = 443 + nickname = args.checkDomains.split('@')[0] + domain = args.checkDomains.split('@')[1] + proxyType = None + if args.tor or domain.endswith('.onion'): + proxyType = 'tor' + if domain.endswith('.onion'): + args.port = 80 + elif args.i2p or domain.endswith('.i2p'): + proxyType = 'i2p' + if domain.endswith('.i2p'): + args.port = 80 + elif args.gnunet: + proxyType = 'gnunet' + maxBlockedDomains = 2 + checkDomains(None, + baseDir, nickname, domain, + proxyType, args.port, + httpPrefix, debug, + __version__, + maxBlockedDomains, False) + sys.exit() + if args.socnet: if ',' not in args.socnet: print('Syntax: ' diff --git a/posts.py b/posts.py index 8f7355160..4218dbecb 100644 --- a/posts.py +++ b/posts.py @@ -14,6 +14,7 @@ import shutil import sys import time import uuid +import random from socket import error as SocketError from time import gmtime, strftime from collections import OrderedDict @@ -59,6 +60,7 @@ from filters import isFiltered from git import convertPostToPatch from jsonldsig import jsonldSign from petnames import resolvePetnames +from follow import getNonMutualsOfPerson # try: # from BeautifulSoup import BeautifulSoup # except ImportError: @@ -3330,6 +3332,57 @@ def getPublicPostDomainsBlocked(session, baseDir: str, return blockedDomains +def checkDomains(session, baseDir: str, + nickname: str, domain: str, + proxyType: str, port: int, httpPrefix: str, + debug: bool, projectVersion: str, + maxBlockedDomains: int, singleCheck: bool): + """Checks follower accounts for references to globally blocked domains + """ + nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) + if not nonMutuals: + return + followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' + updateFollowerWarnings = False + followerWarningStr = '' + if os.path.isfile(followerWarningFilename): + with open(followerWarningFilename, 'r') as fp: + followerWarningStr = fp.read() + + if singleCheck: + # checks a single random non-mutual + index = random.randrange(0, len(nonMutuals)) + domainName = nonMutuals[index] + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + else: + # checks all non-mutuals + for domainName in nonMutuals: + if domainName in followerWarningStr: + continue + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + + if updateFollowerWarnings and followerWarningStr: + with open(followerWarningFilename, 'w+') as fp: + fp.write(followerWarningStr) + print(followerWarningStr) + + def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, followerUrl, updateCaps: [], From 87be0a1efd38c75a5b2c92ba52c26f2cf5d57866 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 12:35:44 +0000 Subject: [PATCH 07/63] More verbose --- posts.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/posts.py b/posts.py index 4218dbecb..ee7b8ccd4 100644 --- a/posts.py +++ b/posts.py @@ -3373,6 +3373,9 @@ def checkDomains(session, baseDir: str, proxyType, port, httpPrefix, debug, projectVersion, []) if blockedDomains: + print(domainName) + for d in blockedDomains: + print(' ' + d) if len(blockedDomains) > maxBlockedDomains: followerWarningStr += domainName + '\n' updateFollowerWarnings = True @@ -3380,7 +3383,8 @@ def checkDomains(session, baseDir: str, if updateFollowerWarnings and followerWarningStr: with open(followerWarningFilename, 'w+') as fp: fp.write(followerWarningStr) - print(followerWarningStr) + if not singleCheck: + print(followerWarningStr) def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, From d259944edaab13ad1f2f69384599138272398ddb Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 13:41:16 +0100 Subject: [PATCH 08/63] Disable functions --- epicyon.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/epicyon.py b/epicyon.py index 3a1d52789..ef15486d1 100644 --- a/epicyon.py +++ b/epicyon.py @@ -15,9 +15,9 @@ from person import deactivateAccount from skills import setSkillLevel from roles import setRole from webfinger import webfingerHandle -from posts import checkDomains -from posts import getPublicPostDomains -from posts import getPublicPostDomainsBlocked +#from posts import checkDomains +#from posts import getPublicPostDomains +#from posts import getPublicPostDomainsBlocked from posts import sendBlockViaServer from posts import sendUndoBlockViaServer from posts import createPublicPost @@ -516,11 +516,11 @@ if args.postDomainsBlocked: elif args.gnunet: proxyType = 'gnunet' domainList = [] - domainList = getPublicPostDomainsBlocked(None, - baseDir, nickname, domain, - proxyType, args.port, - httpPrefix, debug, - __version__, domainList) +# domainList = getPublicPostDomainsBlocked(None, +# baseDir, nickname, domain, +# proxyType, args.port, +# httpPrefix, debug, +# __version__, domainList) for postDomain in domainList: print(postDomain) sys.exit() @@ -555,12 +555,12 @@ if args.checkDomains: elif args.gnunet: proxyType = 'gnunet' maxBlockedDomains = 2 - checkDomains(None, - baseDir, nickname, domain, - proxyType, args.port, - httpPrefix, debug, - __version__, - maxBlockedDomains, False) +# checkDomains(None, +# baseDir, nickname, domain, +# proxyType, args.port, +# httpPrefix, debug, +# __version__, +# maxBlockedDomains, False) sys.exit() if args.socnet: From 4627933946d0156e4ade73c4f5bc63733ea9989c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 13:42:23 +0100 Subject: [PATCH 09/63] Disable functions --- epicyon.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/epicyon.py b/epicyon.py index ef15486d1..670a87f0d 100644 --- a/epicyon.py +++ b/epicyon.py @@ -477,11 +477,11 @@ if args.postDomains: elif args.gnunet: proxyType = 'gnunet' domainList = [] - domainList = getPublicPostDomains(None, - baseDir, nickname, domain, - proxyType, args.port, - httpPrefix, debug, - __version__, domainList) +# domainList = getPublicPostDomains(None, +# baseDir, nickname, domain, +# proxyType, args.port, +# httpPrefix, debug, +# __version__, domainList) for postDomain in domainList: print(postDomain) sys.exit() From 1655a68c7c65dbeae94f238032b7e2096e1801e7 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 13:46:06 +0100 Subject: [PATCH 10/63] Move function --- epicyon.py | 2 +- follow.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ posts.py | 56 ------------------------------------------------------ 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/epicyon.py b/epicyon.py index 670a87f0d..f2a9918ec 100644 --- a/epicyon.py +++ b/epicyon.py @@ -15,7 +15,6 @@ from person import deactivateAccount from skills import setSkillLevel from roles import setRole from webfinger import webfingerHandle -#from posts import checkDomains #from posts import getPublicPostDomains #from posts import getPublicPostDomainsBlocked from posts import sendBlockViaServer @@ -36,6 +35,7 @@ import sys import time from pprint import pprint from daemon import runDaemon +from follow import checkDomains from follow import clearFollows from follow import followerOfPerson from follow import sendFollowRequestViaServer diff --git a/follow.py b/follow.py index 2dd88048a..2d0534f30 100644 --- a/follow.py +++ b/follow.py @@ -25,6 +25,61 @@ from auth import createBasicAuthHeader from session import postJson +def checkDomains(session, baseDir: str, + nickname: str, domain: str, + proxyType: str, port: int, httpPrefix: str, + debug: bool, projectVersion: str, + maxBlockedDomains: int, singleCheck: bool): + """Checks follower accounts for references to globally blocked domains + """ + nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) + if not nonMutuals: + return + followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' + updateFollowerWarnings = False + followerWarningStr = '' + if os.path.isfile(followerWarningFilename): + with open(followerWarningFilename, 'r') as fp: + followerWarningStr = fp.read() + + if singleCheck: + # checks a single random non-mutual + index = random.randrange(0, len(nonMutuals)) + domainName = nonMutuals[index] + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + else: + # checks all non-mutuals + for domainName in nonMutuals: + if domainName in followerWarningStr: + continue + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + print(domainName) + for d in blockedDomains: + print(' ' + d) + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + + if updateFollowerWarnings and followerWarningStr: + with open(followerWarningFilename, 'w+') as fp: + fp.write(followerWarningStr) + if not singleCheck: + print(followerWarningStr) + + def preApprovedFollower(baseDir: str, nickname: str, domain: str, approveHandle: str) -> bool: diff --git a/posts.py b/posts.py index ee7b8ccd4..e98d21e83 100644 --- a/posts.py +++ b/posts.py @@ -60,7 +60,6 @@ from filters import isFiltered from git import convertPostToPatch from jsonldsig import jsonldSign from petnames import resolvePetnames -from follow import getNonMutualsOfPerson # try: # from BeautifulSoup import BeautifulSoup # except ImportError: @@ -3332,61 +3331,6 @@ def getPublicPostDomainsBlocked(session, baseDir: str, return blockedDomains -def checkDomains(session, baseDir: str, - nickname: str, domain: str, - proxyType: str, port: int, httpPrefix: str, - debug: bool, projectVersion: str, - maxBlockedDomains: int, singleCheck: bool): - """Checks follower accounts for references to globally blocked domains - """ - nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) - if not nonMutuals: - return - followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' - updateFollowerWarnings = False - followerWarningStr = '' - if os.path.isfile(followerWarningFilename): - with open(followerWarningFilename, 'r') as fp: - followerWarningStr = fp.read() - - if singleCheck: - # checks a single random non-mutual - index = random.randrange(0, len(nonMutuals)) - domainName = nonMutuals[index] - blockedDomains = \ - getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, - proxyType, port, httpPrefix, - debug, projectVersion, []) - if blockedDomains: - if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' - updateFollowerWarnings = True - else: - # checks all non-mutuals - for domainName in nonMutuals: - if domainName in followerWarningStr: - continue - blockedDomains = \ - getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, - proxyType, port, httpPrefix, - debug, projectVersion, []) - if blockedDomains: - print(domainName) - for d in blockedDomains: - print(' ' + d) - if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' - updateFollowerWarnings = True - - if updateFollowerWarnings and followerWarningStr: - with open(followerWarningFilename, 'w+') as fp: - fp.write(followerWarningStr) - if not singleCheck: - print(followerWarningStr) - - def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, followerUrl, updateCaps: [], From 6ac45978e83d192fce67b1f5ed95e1f589f276d2 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 14:09:20 +0100 Subject: [PATCH 11/63] Typo --- epicyon.py | 24 ++++++++++++------------ posts.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/epicyon.py b/epicyon.py index f2a9918ec..f34f09fa8 100644 --- a/epicyon.py +++ b/epicyon.py @@ -15,8 +15,8 @@ from person import deactivateAccount from skills import setSkillLevel from roles import setRole from webfinger import webfingerHandle -#from posts import getPublicPostDomains -#from posts import getPublicPostDomainsBlocked +from posts import getPublicPostDomains +from posts import getPublicPostDomainsBlocked from posts import sendBlockViaServer from posts import sendUndoBlockViaServer from posts import createPublicPost @@ -477,11 +477,11 @@ if args.postDomains: elif args.gnunet: proxyType = 'gnunet' domainList = [] -# domainList = getPublicPostDomains(None, -# baseDir, nickname, domain, -# proxyType, args.port, -# httpPrefix, debug, -# __version__, domainList) + domainList = getPublicPostDomains(None, + baseDir, nickname, domain, + proxyType, args.port, + httpPrefix, debug, + __version__, domainList) for postDomain in domainList: print(postDomain) sys.exit() @@ -516,11 +516,11 @@ if args.postDomainsBlocked: elif args.gnunet: proxyType = 'gnunet' domainList = [] -# domainList = getPublicPostDomainsBlocked(None, -# baseDir, nickname, domain, -# proxyType, args.port, -# httpPrefix, debug, -# __version__, domainList) + domainList = getPublicPostDomainsBlocked(None, + baseDir, nickname, domain, + proxyType, args.port, + httpPrefix, debug, + __version__, domainList) for postDomain in domainList: print(postDomain) sys.exit() diff --git a/posts.py b/posts.py index e98d21e83..b65305b68 100644 --- a/posts.py +++ b/posts.py @@ -3273,7 +3273,7 @@ def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str, return domainList (personUrl, pubKeyId, pubKey, - personId, shaedInbox, + personId, sharedInbox, capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, From e5eaf42a36daf61aaf456dedf24c618d2ee649de Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 14:21:56 +0100 Subject: [PATCH 12/63] Move functions --- epicyon.py | 14 ++++---- follow.py | 102 +---------------------------------------------------- posts.py | 72 +++++++++++++++++++++++++++++++++++++ tests.py | 2 +- utils.py | 30 ++++++++++++++++ 5 files changed, 111 insertions(+), 109 deletions(-) diff --git a/epicyon.py b/epicyon.py index f34f09fa8..7654515f9 100644 --- a/epicyon.py +++ b/epicyon.py @@ -25,6 +25,7 @@ from posts import archivePosts from posts import sendPostViaServer from posts import getPublicPostsOfPerson from posts import getUserUrl +from posts import checkDomains from session import createSession from session import getJson from filters import addFilter @@ -35,7 +36,6 @@ import sys import time from pprint import pprint from daemon import runDaemon -from follow import checkDomains from follow import clearFollows from follow import followerOfPerson from follow import sendFollowRequestViaServer @@ -555,12 +555,12 @@ if args.checkDomains: elif args.gnunet: proxyType = 'gnunet' maxBlockedDomains = 2 -# checkDomains(None, -# baseDir, nickname, domain, -# proxyType, args.port, -# httpPrefix, debug, -# __version__, -# maxBlockedDomains, False) + checkDomains(None, + baseDir, nickname, domain, + proxyType, args.port, + httpPrefix, debug, + __version__, + maxBlockedDomains, False) sys.exit() if args.socnet: diff --git a/follow.py b/follow.py index 2d0534f30..014458237 100644 --- a/follow.py +++ b/follow.py @@ -8,6 +8,7 @@ __status__ = "Production" from pprint import pprint import os +from utils import getFollowersOfPerson from utils import validNickname from utils import domainPermitted from utils import getDomainFromActor @@ -25,61 +26,6 @@ from auth import createBasicAuthHeader from session import postJson -def checkDomains(session, baseDir: str, - nickname: str, domain: str, - proxyType: str, port: int, httpPrefix: str, - debug: bool, projectVersion: str, - maxBlockedDomains: int, singleCheck: bool): - """Checks follower accounts for references to globally blocked domains - """ - nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) - if not nonMutuals: - return - followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' - updateFollowerWarnings = False - followerWarningStr = '' - if os.path.isfile(followerWarningFilename): - with open(followerWarningFilename, 'r') as fp: - followerWarningStr = fp.read() - - if singleCheck: - # checks a single random non-mutual - index = random.randrange(0, len(nonMutuals)) - domainName = nonMutuals[index] - blockedDomains = \ - getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, - proxyType, port, httpPrefix, - debug, projectVersion, []) - if blockedDomains: - if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' - updateFollowerWarnings = True - else: - # checks all non-mutuals - for domainName in nonMutuals: - if domainName in followerWarningStr: - continue - blockedDomains = \ - getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, - proxyType, port, httpPrefix, - debug, projectVersion, []) - if blockedDomains: - print(domainName) - for d in blockedDomains: - print(' ' + d) - if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' - updateFollowerWarnings = True - - if updateFollowerWarnings and followerWarningStr: - with open(followerWarningFilename, 'w+') as fp: - fp.write(followerWarningStr) - if not singleCheck: - print(followerWarningStr) - - def preApprovedFollower(baseDir: str, nickname: str, domain: str, approveHandle: str) -> bool: @@ -182,52 +128,6 @@ def getMutualsOfPerson(baseDir: str, return mutuals -def getNonMutualsOfPerson(baseDir: str, - nickname: str, domain: str) -> []: - """Returns the followers who are not mutuals of a person - i.e. accounts which follow you but you don't follow them - """ - followers = \ - getFollowersOfPerson(baseDir, nickname, domain, 'followers') - following = \ - getFollowersOfPerson(baseDir, nickname, domain, 'following') - nonMutuals = [] - for handle in following: - if handle not in followers: - nonMutuals.append(handle) - return nonMutuals - - -def getFollowersOfPerson(baseDir: str, - nickname: str, domain: str, - followFile='following.txt') -> []: - """Returns a list containing the followers of the given person - Used by the shared inbox to know who to send incoming mail to - """ - followers = [] - if ':' in domain: - domain = domain.split(':')[0] - handle = nickname + '@' + domain - if not os.path.isdir(baseDir + '/accounts/' + handle): - return followers - for subdir, dirs, files in os.walk(baseDir + '/accounts'): - for account in dirs: - filename = os.path.join(subdir, account) + '/' + followFile - if account == handle or account.startswith('inbox@'): - continue - if not os.path.isfile(filename): - continue - with open(filename, 'r') as followingfile: - for followingHandle in followingfile: - followingHandle2 = followingHandle.replace('\n', '') - followingHandle2 = followingHandle2.replace('\r', '') - if followingHandle2 == handle: - if account not in followers: - followers.append(account) - break - return followers - - def followerOfPerson(baseDir: str, nickname: str, domain: str, followerNickname: str, followerDomain: str, federationList: [], debug: bool) -> bool: diff --git a/posts.py b/posts.py index b65305b68..9f637f84f 100644 --- a/posts.py +++ b/posts.py @@ -30,6 +30,7 @@ from session import postJsonString from session import postImage from webfinger import webfingerHandle from httpsig import createSignedHeader +from utils import getFollowersOfPerson from utils import isEvil from utils import removeIdEnding from utils import siteIsActive @@ -3331,6 +3332,77 @@ def getPublicPostDomainsBlocked(session, baseDir: str, return blockedDomains +def getNonMutualsOfPerson(baseDir: str, + nickname: str, domain: str) -> []: + """Returns the followers who are not mutuals of a person + i.e. accounts which follow you but you don't follow them + """ + followers = \ + getFollowersOfPerson(baseDir, nickname, domain, 'followers') + following = \ + getFollowersOfPerson(baseDir, nickname, domain, 'following') + nonMutuals = [] + for handle in following: + if handle not in followers: + nonMutuals.append(handle) + return nonMutuals + + +def checkDomains(session, baseDir: str, + nickname: str, domain: str, + proxyType: str, port: int, httpPrefix: str, + debug: bool, projectVersion: str, + maxBlockedDomains: int, singleCheck: bool): + """Checks follower accounts for references to globally blocked domains + """ + nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) + if not nonMutuals: + return + followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' + updateFollowerWarnings = False + followerWarningStr = '' + if os.path.isfile(followerWarningFilename): + with open(followerWarningFilename, 'r') as fp: + followerWarningStr = fp.read() + + if singleCheck: + # checks a single random non-mutual + index = random.randrange(0, len(nonMutuals)) + domainName = nonMutuals[index] + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + else: + # checks all non-mutuals + for domainName in nonMutuals: + if domainName in followerWarningStr: + continue + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nickname, domain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + print(domainName) + for d in blockedDomains: + print(' ' + d) + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += domainName + '\n' + updateFollowerWarnings = True + + if updateFollowerWarnings and followerWarningStr: + with open(followerWarningFilename, 'w+') as fp: + fp.write(followerWarningStr) + if not singleCheck: + print(followerWarningStr) + + def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, followerUrl, updateCaps: [], diff --git a/tests.py b/tests.py index abb35f43e..7996c3ba9 100644 --- a/tests.py +++ b/tests.py @@ -42,10 +42,10 @@ from utils import copytree from utils import loadJson from utils import saveJson from utils import getStatusNumber +from utils import getFollowersOfPerson from follow import followerOfPerson from follow import unfollowPerson from follow import unfollowerOfPerson -from follow import getFollowersOfPerson from follow import sendFollowRequest from person import createPerson from person import setDisplayNickname diff --git a/utils.py b/utils.py index 8a18c7534..2be770b0a 100644 --- a/utils.py +++ b/utils.py @@ -19,6 +19,36 @@ from calendar import monthrange from followingCalendar import addPersonToCalendar +def getFollowersOfPerson(baseDir: str, + nickname: str, domain: str, + followFile='following.txt') -> []: + """Returns a list containing the followers of the given person + Used by the shared inbox to know who to send incoming mail to + """ + followers = [] + if ':' in domain: + domain = domain.split(':')[0] + handle = nickname + '@' + domain + if not os.path.isdir(baseDir + '/accounts/' + handle): + return followers + for subdir, dirs, files in os.walk(baseDir + '/accounts'): + for account in dirs: + filename = os.path.join(subdir, account) + '/' + followFile + if account == handle or account.startswith('inbox@'): + continue + if not os.path.isfile(filename): + continue + with open(filename, 'r') as followingfile: + for followingHandle in followingfile: + followingHandle2 = followingHandle.replace('\n', '') + followingHandle2 = followingHandle2.replace('\r', '') + if followingHandle2 == handle: + if account not in followers: + followers.append(account) + break + return followers + + def removeIdEnding(idStr: str) -> str: """Removes endings such as /activity and /undo """ From 351cb6ad144e8f9c5c1473b8cb0b423573ce63ee Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 14:33:44 +0100 Subject: [PATCH 13/63] Filenames for followers --- follow.py | 4 ++-- posts.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/follow.py b/follow.py index 014458237..f717e77a1 100644 --- a/follow.py +++ b/follow.py @@ -118,9 +118,9 @@ def getMutualsOfPerson(baseDir: str, i.e. accounts which they follow and which also follow back """ followers = \ - getFollowersOfPerson(baseDir, nickname, domain, 'followers') + getFollowersOfPerson(baseDir, nickname, domain, 'followers.txt') following = \ - getFollowersOfPerson(baseDir, nickname, domain, 'following') + getFollowersOfPerson(baseDir, nickname, domain, 'following.txt') mutuals = [] for handle in following: if handle in followers: diff --git a/posts.py b/posts.py index 9f637f84f..32709891a 100644 --- a/posts.py +++ b/posts.py @@ -3338,9 +3338,9 @@ def getNonMutualsOfPerson(baseDir: str, i.e. accounts which follow you but you don't follow them """ followers = \ - getFollowersOfPerson(baseDir, nickname, domain, 'followers') + getFollowersOfPerson(baseDir, nickname, domain, 'followers.txt') following = \ - getFollowersOfPerson(baseDir, nickname, domain, 'following') + getFollowersOfPerson(baseDir, nickname, domain, 'following.txt') nonMutuals = [] for handle in following: if handle not in followers: @@ -3357,6 +3357,7 @@ def checkDomains(session, baseDir: str, """ nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain) if not nonMutuals: + print('No non-mutual followers were found') return followerWarningFilename = baseDir + '/accounts/followerWarnings.txt' updateFollowerWarnings = False From b1672fb9d56dc1f0d5e2d280d0a9cede67bfa950 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 15:14:59 +0100 Subject: [PATCH 14/63] Return lists --- follow.py | 6 +++--- posts.py | 6 +++--- utils.py | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/follow.py b/follow.py index f717e77a1..f641e916e 100644 --- a/follow.py +++ b/follow.py @@ -8,7 +8,7 @@ __status__ = "Production" from pprint import pprint import os -from utils import getFollowersOfPerson +from utils import getFollowersList from utils import validNickname from utils import domainPermitted from utils import getDomainFromActor @@ -118,9 +118,9 @@ def getMutualsOfPerson(baseDir: str, i.e. accounts which they follow and which also follow back """ followers = \ - getFollowersOfPerson(baseDir, nickname, domain, 'followers.txt') + getFollowersList(baseDir, nickname, domain, 'followers.txt') following = \ - getFollowersOfPerson(baseDir, nickname, domain, 'following.txt') + getFollowersList(baseDir, nickname, domain, 'following.txt') mutuals = [] for handle in following: if handle in followers: diff --git a/posts.py b/posts.py index 32709891a..3044d0151 100644 --- a/posts.py +++ b/posts.py @@ -30,7 +30,7 @@ from session import postJsonString from session import postImage from webfinger import webfingerHandle from httpsig import createSignedHeader -from utils import getFollowersOfPerson +from utils import getFollowersList from utils import isEvil from utils import removeIdEnding from utils import siteIsActive @@ -3338,9 +3338,9 @@ def getNonMutualsOfPerson(baseDir: str, i.e. accounts which follow you but you don't follow them """ followers = \ - getFollowersOfPerson(baseDir, nickname, domain, 'followers.txt') + getFollowersList(baseDir, nickname, domain, 'followers.txt') following = \ - getFollowersOfPerson(baseDir, nickname, domain, 'following.txt') + getFollowersList(baseDir, nickname, domain, 'following.txt') nonMutuals = [] for handle in following: if handle not in followers: diff --git a/utils.py b/utils.py index 2be770b0a..014c5bc20 100644 --- a/utils.py +++ b/utils.py @@ -19,6 +19,25 @@ from calendar import monthrange from followingCalendar import addPersonToCalendar +def getFollowersList(baseDir: str, + nickname: str, domain: str, + followFile='following.txt') -> []: + """Returns a list of followers for the given account + """ + filename = \ + baseDir + '/accounts/' + nickname + '@' + domain + '/' + followFile + + if not os.path.isfile(filename): + return [] + + with open(filename, "r") as f: + lines = f.readlines() + for i in range(len(lines)): + lines[i] = lines[i].strip() + return lines + return [] + + def getFollowersOfPerson(baseDir: str, nickname: str, domain: str, followFile='following.txt') -> []: From 549c5314423bea17b6824d4280ae8f91fbe5a084 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 15:23:33 +0100 Subject: [PATCH 15/63] Use handles --- posts.py | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/posts.py b/posts.py index 3044d0151..42d0769cf 100644 --- a/posts.py +++ b/posts.py @@ -3369,32 +3369,41 @@ def checkDomains(session, baseDir: str, if singleCheck: # checks a single random non-mutual index = random.randrange(0, len(nonMutuals)) - domainName = nonMutuals[index] - blockedDomains = \ - getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, - proxyType, port, httpPrefix, - debug, projectVersion, []) - if blockedDomains: - if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' - updateFollowerWarnings = True - else: - # checks all non-mutuals - for domainName in nonMutuals: - if domainName in followerWarningStr: - continue + handle = nonMutuals[index] + if '@' in handle: + nonMutualNickname = handle.split('@')[0] + nonMutualDomain = handle.split('@')[1].strip() blockedDomains = \ getPublicPostDomainsBlocked(session, baseDir, - nickname, domain, + nonMutualNickname, + nonMutualDomain, proxyType, port, httpPrefix, debug, projectVersion, []) if blockedDomains: - print(domainName) + if len(blockedDomains) > maxBlockedDomains: + followerWarningStr += handle + '\n' + updateFollowerWarnings = True + else: + # checks all non-mutuals + for handle in nonMutuals: + if '@' not in handle: + continue + if handle in followerWarningStr: + continue + nonMutualNickname = handle.split('@')[0] + nonMutualDomain = handle.split('@')[1].strip() + blockedDomains = \ + getPublicPostDomainsBlocked(session, baseDir, + nonMutualNickname, + nonMutualDomain, + proxyType, port, httpPrefix, + debug, projectVersion, []) + if blockedDomains: + print(handle) for d in blockedDomains: print(' ' + d) if len(blockedDomains) > maxBlockedDomains: - followerWarningStr += domainName + '\n' + followerWarningStr += handle + '\n' updateFollowerWarnings = True if updateFollowerWarnings and followerWarningStr: From 803ac11dba45b619a4326cb103936122427bb558 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 15:33:20 +0100 Subject: [PATCH 16/63] Other way around --- posts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posts.py b/posts.py index 42d0769cf..cffc4d8f6 100644 --- a/posts.py +++ b/posts.py @@ -3342,8 +3342,8 @@ def getNonMutualsOfPerson(baseDir: str, following = \ getFollowersList(baseDir, nickname, domain, 'following.txt') nonMutuals = [] - for handle in following: - if handle not in followers: + for handle in followers: + if handle not in following: nonMutuals.append(handle) return nonMutuals From 3d68d85c0afdacbfe2dd3bf05b87102a337d459f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 15:38:21 +0100 Subject: [PATCH 17/63] Lower maximum --- epicyon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epicyon.py b/epicyon.py index 7654515f9..507e58ab4 100644 --- a/epicyon.py +++ b/epicyon.py @@ -554,7 +554,7 @@ if args.checkDomains: args.port = 80 elif args.gnunet: proxyType = 'gnunet' - maxBlockedDomains = 2 + maxBlockedDomains = 0 checkDomains(None, baseDir, nickname, domain, proxyType, args.port, From b8f122004b783770968ab85927a41387a70b0970 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 25 Sep 2020 15:38:50 +0100 Subject: [PATCH 18/63] Less verbose --- session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.py b/session.py index 1547c0280..26322bad7 100644 --- a/session.py +++ b/session.py @@ -49,7 +49,7 @@ def createSession(proxyType: str): session.proxies = {} session.proxies['http'] = 'socks5h://localhost:7777' session.proxies['https'] = 'socks5h://localhost:7777' - print('New session created with proxy ' + str(proxyType)) + # print('New session created with proxy ' + str(proxyType)) return session From 83ff1b03a3a911d817d45a64b22e11ffab9a61c4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 19:23:43 +0100 Subject: [PATCH 19/63] Rss feeds for hashtags --- blog.py | 6 ++- daemon.py | 67 ++++++++++++++++++++++++++++ webinterface.py | 113 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 183 insertions(+), 3 deletions(-) diff --git a/blog.py b/blog.py index e1db75975..190d6146c 100644 --- a/blog.py +++ b/blog.py @@ -282,7 +282,8 @@ def htmlBlogPostRSS2(authorized: bool, messageLink = postJsonObject['object']['id'].replace('/statuses/', '/') if not restrictToDomain or \ (restrictToDomain and '/' + domain in messageLink): - if postJsonObject['object'].get('summary'): + if postJsonObject['object'].get('summary') and \ + postJsonObject['object'].get('published'): published = postJsonObject['object']['published'] pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") titleStr = postJsonObject['object']['summary'] @@ -307,7 +308,8 @@ def htmlBlogPostRSS3(authorized: bool, messageLink = postJsonObject['object']['id'].replace('/statuses/', '/') if not restrictToDomain or \ (restrictToDomain and '/' + domain in messageLink): - if postJsonObject['object'].get('summary'): + if postJsonObject['object'].get('summary') and \ + postJsonObject['object'].get('published'): published = postJsonObject['object']['published'] pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") titleStr = postJsonObject['object']['summary'] diff --git a/daemon.py b/daemon.py index fe192e17b..9bcd5e07f 100644 --- a/daemon.py +++ b/daemon.py @@ -148,6 +148,7 @@ from webinterface import htmlTermsOfService from webinterface import htmlSkillsSearch from webinterface import htmlHistorySearch from webinterface import htmlHashtagSearch +from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked @@ -4093,6 +4094,60 @@ class PubServer(BaseHTTPRequestHandler): 'login shown done', 'hashtag search') + def _hashtagSearchRSS2(self, callingDomain: str, + path: str, cookie: str, + baseDir: str, httpPrefix: str, + domain: str, domainFull: str, port: int, + onionDomain: str, i2pDomain: str, + GETstartTime, GETtimings: {}): + """Return an RSS 2 feed for a hashtag + """ + hashtag = path.split('/tags/rss2/')[1] + if isBlockedHashtag(baseDir, hashtag): + self._400() + self.server.GETbusy = False + return + nickname = None + if '/users/' in path: + actor = \ + httpPrefix + '://' + domainFull + path + nickname = \ + getNicknameFromActor(actor) + hashtagStr = \ + rssHashtagSearch(nickname, + domain, port, + self.server.recentPostsCache, + self.server.maxRecentPosts, + self.server.translate, + baseDir, hashtag, + maxPostsInFeed, self.server.session, + self.server.cachedWebfingers, + self.server.personCache, + httpPrefix, + self.server.projectVersion, + self.server.YTReplacementDomain) + if hashtagStr: + msg = hashtagStr.encode('utf-8') + self._set_headers('text/xml', len(msg), + cookie, callingDomain) + self._write(msg) + else: + originPathStr = path.split('/tags/rss2/')[0] + originPathStrAbsolute = \ + httpPrefix + '://' + domainFull + originPathStr + if callingDomain.endswith('.onion') and onionDomain: + originPathStrAbsolute = \ + 'http://' + onionDomain + originPathStr + elif (callingDomain.endswith('.i2p') and onionDomain): + originPathStrAbsolute = \ + 'http://' + i2pDomain + originPathStr + self._redirect_headers(originPathStrAbsolute + '/search', + cookie, callingDomain) + self.server.GETbusy = False + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'login shown done', + 'hashtag rss feed') + def _announceButton(self, callingDomain: str, path: str, baseDir: str, cookie: str, proxyType: str, @@ -8068,6 +8123,18 @@ class PubServer(BaseHTTPRequestHandler): # hashtag search if self.path.startswith('/tags/') or \ (authorized and '/tags/' in self.path): + if self.path.startswith('/tags/rss2/'): + self._hashtagSearchRSS2(callingDomain, + self.path, cookie, + self.server.baseDir, + self.server.httpPrefix, + self.server.domain, + self.server.domainFull, + self.server.port, + self.server.onionDomain, + self.server.i2pDomain, + GETstartTime, GETtimings) + return self._hashtagSearch(callingDomain, self.path, cookie, self.server.baseDir, diff --git a/webinterface.py b/webinterface.py index 32ded79de..f9942ebb3 100644 --- a/webinterface.py +++ b/webinterface.py @@ -787,7 +787,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, else: postFields = postId.split(' ') if len(postFields) != 3: - index = +1 + index += 1 continue nickname = postFields[1] postId = postFields[2] @@ -833,6 +833,117 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, return hashtagSearchForm +def rss2TagHeader(hashtag: str, httpPrefix: str, domainFull: str) -> str: + rssStr = "" + rssStr += "" + rssStr += '' + rssStr += ' #' + hashtag + '' + rssStr += ' ' + httpPrefix + '://' + domainFull + \ + '/tags/rss2/' + hashtag + '' + return rssStr + + +def rss2TagFooter() -> str: + rssStr = '' + rssStr += '' + return rssStr + + +def rssHashtagSearch(nickname: str, domain: str, port: int, + recentPostsCache: {}, maxRecentPosts: int, + translate: {}, + baseDir: str, hashtag: str, + postsPerPage: int, + session, wfRequest: {}, personCache: {}, + httpPrefix: str, projectVersion: str, + YTReplacementDomain: str) -> str: + """Show an rss feed for a hashtag + """ + if hashtag.startswith('#'): + hashtag = hashtag[1:] + hashtag = urllib.parse.unquote(hashtag) + hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' + if not os.path.isfile(hashtagIndexFile): + if hashtag != hashtag.lower(): + hashtag = hashtag.lower() + hashtagIndexFile = baseDir + '/tags/' + hashtag + '.txt' + if not os.path.isfile(hashtagIndexFile): + print('WARN: hashtag file not found ' + hashtagIndexFile) + return None + + # check that the directory for the nickname exists + if nickname: + if not os.path.isdir(baseDir + '/accounts/' + + nickname + '@' + domain): + nickname = None + + # read the index + lines = [] + with open(hashtagIndexFile, "r") as f: + lines = f.readlines() + if not lines: + return None + + domainFull = domain + if port: + if port != 80 and port != 443: + domainFull = domain + ':' + str(port) + + maxFeedLength = 10 + hashtagFeed = \ + rss2TagHeader(hashtag, httpPrefix, domainFull) + for index in range(len(lines)): + postId = lines[index].strip('\n').strip('\r') + if ' ' not in postId: + nickname = getNicknameFromActor(postId) + if not nickname: + index += 1 + if index >= maxFeedLength: + break + continue + else: + postFields = postId.split(' ') + if len(postFields) != 3: + index += 1 + if index >= maxFeedLength: + break + continue + nickname = postFields[1] + postId = postFields[2] + postFilename = locatePost(baseDir, nickname, domain, postId) + if not postFilename: + index += 1 + if index >= maxFeedLength: + break + continue + postJsonObject = loadJson(postFilename) + if postJsonObject: + if not isPublicPost(postJsonObject): + index += 1 + if index >= maxFeedLength: + break + continue + # add to feed + if postJsonObject['object'].get('id') and \ + postJsonObject['object'].get('published'): + messageLink = \ + postJsonObject['object']['id'].replace('/statuses/', '/') + published = postJsonObject['object']['published'] + pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") + rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") + hashtagFeed += ' ' + hashtagFeed += \ + ' ' + messageLink + '' + hashtagFeed += \ + ' ' + rssDateStr + '' + hashtagFeed += ' ' + index += 1 + if index >= maxFeedLength: + break + + return hashtagFeed + rss2TagFooter() + + def htmlSkillsSearch(translate: {}, baseDir: str, httpPrefix: str, skillsearch: str, instanceOnly: bool, From fddbb1f2f8133ee12048b580ba8fe4cb822b06ae Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 19:45:25 +0100 Subject: [PATCH 20/63] Include post content in hashtag feed --- webinterface.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/webinterface.py b/webinterface.py index f9942ebb3..598d99670 100644 --- a/webinterface.py +++ b/webinterface.py @@ -924,16 +924,21 @@ def rssHashtagSearch(nickname: str, domain: str, port: int, break continue # add to feed - if postJsonObject['object'].get('id') and \ + if postJsonObject['object'].get('content') and \ postJsonObject['object'].get('published'): - messageLink = \ - postJsonObject['object']['id'].replace('/statuses/', '/') published = postJsonObject['object']['published'] pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") hashtagFeed += ' ' + if postJsonObject['object'].get('summary'): + hashtagFeed += \ + ' ' + \ + postJsonObject['object']['summary'] + \ + '' hashtagFeed += \ - ' ' + messageLink + '' + ' ' + \ + postJsonObject['object']['content'] + \ + '' hashtagFeed += \ ' ' + rssDateStr + '' hashtagFeed += ' ' From aa42b423bd226c2c1ac35aeb0abd1da2bf1b371b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 19:49:06 +0100 Subject: [PATCH 21/63] Include post author in hashtag feed --- webinterface.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webinterface.py b/webinterface.py index 598d99670..910506c5f 100644 --- a/webinterface.py +++ b/webinterface.py @@ -925,11 +925,16 @@ def rssHashtagSearch(nickname: str, domain: str, port: int, continue # add to feed if postJsonObject['object'].get('content') and \ + postJsonObject['object'].get('attributedTo') and \ postJsonObject['object'].get('published'): published = postJsonObject['object']['published'] pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") hashtagFeed += ' ' + hashtagFeed += \ + ' ' + \ + postJsonObject['object']['attributedTo'] + \ + '' if postJsonObject['object'].get('summary'): hashtagFeed += \ ' ' + \ From b9d57a510c334daafc2e68d6627ad8fdce25d360 Mon Sep 17 00:00:00 2001 From: Bob Mottram <bob@freedombone.net> Date: Sat, 26 Sep 2020 19:53:10 +0100 Subject: [PATCH 22/63] Add attachments to tag rss feed --- webinterface.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webinterface.py b/webinterface.py index 910506c5f..a50713496 100644 --- a/webinterface.py +++ b/webinterface.py @@ -946,6 +946,12 @@ def rssHashtagSearch(nickname: str, domain: str, port: int, '</description>' hashtagFeed += \ ' <pubDate>' + rssDateStr + '</pubDate>' + if postJsonObject['object'].get('attachment'): + for attach in postJsonObject['object']['attachment']: + if not attach.get('url'): + continue + hashtagFeed += \ + ' <link>' + attach['url'] + '</link>' hashtagFeed += ' </item>' index += 1 if index >= maxFeedLength: From 5c327fbab8a0b0ec4f5eb5675f0eef5136c7bd57 Mon Sep 17 00:00:00 2001 From: Bob Mottram <bob@freedombone.net> Date: Sat, 26 Sep 2020 19:57:34 +0100 Subject: [PATCH 23/63] html formatting within rss --- webinterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webinterface.py b/webinterface.py index a50713496..bbad26575 100644 --- a/webinterface.py +++ b/webinterface.py @@ -941,9 +941,9 @@ def rssHashtagSearch(nickname: str, domain: str, port: int, postJsonObject['object']['summary'] + \ '' hashtagFeed += \ - ' ' + \ + ' ' + ']]>' hashtagFeed += \ ' ' + rssDateStr + '' if postJsonObject['object'].get('attachment'): From f3d0a962c0d4e210a5873754388e052508be8b8a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:14:04 +0100 Subject: [PATCH 24/63] RSS link on hashtag feed --- webinterface.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webinterface.py b/webinterface.py index bbad26575..7f4c1b639 100644 --- a/webinterface.py +++ b/webinterface.py @@ -766,6 +766,12 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, hashtagSearchForm += '
\n' + \ '

#' + hashtag + '

\n' + '
\n' + # RSS link for hashtag feed + hashtagSearchForm += '
' + hashtagSearchForm += 'RSS 2.0
' + if startIndex > 0: # previous page link hashtagSearchForm += \ From d81ef01150aa1c7d59815f44605ae11e25ffedea Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:16:14 +0100 Subject: [PATCH 25/63] Image style --- webinterface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 7f4c1b639..d7464a8ec 100644 --- a/webinterface.py +++ b/webinterface.py @@ -768,7 +768,8 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' - hashtagSearchForm += 'RSS 2.0
' From fda03baacd567b9c51e0bfb7b4099e26290148fe Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:17:25 +0100 Subject: [PATCH 26/63] Image style --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index d7464a8ec..0e43269d2 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From e625e6a1a2965b6c2ae088919b0ebe25c2e875d4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:18:22 +0100 Subject: [PATCH 27/63] Image style --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 0e43269d2..7218441be 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From 296f7f37de6a878d1e626262e067c955f27251d0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:21:15 +0100 Subject: [PATCH 28/63] Minimum width of rss icon --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 7218441be..41f6cff54 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From 0b899e066896eaa42a7cfebc6414eb437a77981c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:25:35 +0100 Subject: [PATCH 29/63] Minimum width of rss icon --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index 41f6cff54..c1984484f 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From b33fdd5e393eab51ec254e144b8876d3037dc0e7 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:28:26 +0100 Subject: [PATCH 30/63] Minimum width of rss icon --- webinterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webinterface.py b/webinterface.py index c1984484f..dff96f2e9 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,7 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From c3ccc3e170899dd74b440719f8359eeae71993d1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 26 Sep 2020 20:51:22 +0100 Subject: [PATCH 31/63] Icon style --- blog.py | 6 ++++-- webinterface.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/blog.py b/blog.py index 190d6146c..bc76be907 100644 --- a/blog.py +++ b/blog.py @@ -360,13 +360,15 @@ def htmlBlogPost(authorized: bool, blogStr += '' - blogStr += 'RSS 2.0' blogStr += '' - blogStr += 'RSS 3.0' diff --git a/webinterface.py b/webinterface.py index dff96f2e9..b814b8230 100644 --- a/webinterface.py +++ b/webinterface.py @@ -769,7 +769,8 @@ def htmlHashtagSearch(nickname: str, domain: str, port: int, # RSS link for hashtag feed hashtagSearchForm += '
' hashtagSearchForm += \ - 'RSS 2.0
' From 087618008dd3852a57b9d67508c6a8391452138b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 27 Sep 2020 12:19:34 +0100 Subject: [PATCH 32/63] Rss icons --- img/icons/indymedia/rss.png | Bin 7095 -> 5246 bytes img/icons/indymedia/rss3.png | Bin 5439 -> 2889 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/img/icons/indymedia/rss.png b/img/icons/indymedia/rss.png index 533453fc7f86e4645a9432fb2ee66a611830fdc3..db86c3c21d9148936da9e4eaabcdf7e3011eb1f2 100644 GIT binary patch delta 5214 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 Yt|JrCswdEJ00000NkvXXu0jG}f^0<-8UO$Q literal 7095 zcmXxpRa6wr-v{ue8zcp3co0EALg|L3Q9?SUrKP(>Sh_?Sq(hc;>F#bRr4gjNW8VE= zyytx8J8>~rGw00tO@!K8c>>&LxDW`0Kv6+f1KCUduVJGhGpjy$9|9rW@OiK6rt!g( z!P&*h>Z83SgPXUrC4;5cM=J=#YZ0FQ(c@1QaVXdz6GE*<>+ifZc8uTHei_e~XunXM zq+2JS{sq1HhaHOLZ2M#5sKhm?e}f9Fu^@e_H%CpzJ;rIs9h7bd`hGbBH>BAq*Ehpw zTMr={Y~%Uufg=arfg3m1C&6<<_TaXKZTw}0D3*`07wyE}NUYP=HuSWmIj4SHNyX?& zp!D0lcj?kn+y-Lv^u&qCGxq7-Lt+4bP7Zb&`{+PKi|epKU=|o|MGXXY_fhvpUgLXI zM^S5?K6mAzA%2Z5a~tmdToWlw?QMJ#+TdY{sMqtrdl{$=pU=RY+Wb5Fj-mpU?ss|~ zTbZsdzEHryj*BsLxpu$x@P{3v`s6voE^p;-=I)W=!e3TD)wtX&fl*i~H z;U87pQCV61Fvofi(GsS7mxlLrz9N7=PyQ~LGw^ob+hp^Ej?wPak5Do~(9?p4L2Uk| z9=$`2jt7^4GA$l~ZTX*@RbkpHo~A)2qhoQQB%@El0y%@1EpyP6%86GfC%kEQ ztwUFH6fvW3;9B%4udn*qk(21O(UsWwyykf8`QmSgICHe%)Iff;@S@xFI~%j2H0w0? zI?K@zHf`Ncg~>X4?g)a}x|I+8Ssu%lb4$xczD>8&b0%)1;zq8;nm#50o-x*{W0f+x zlZx-h|E1!6i?w!#NQzwSv-6`DkCj^MxZ^|JIC#qhtn}TZDZ(s`{#-6K$BjLxuc&YY z&InxP2wTrI0r_ow_+6vZikpMdcHhpt*q5@MziDlbGCwB^3d}31?u@-Jvk&&$>ii4& z8|}GDV}(lhBX1U2>h&XMX^lpHIT5};wdG0Ue=yEJT$&PpSJ~oeD0X~EFyleXGrM5z z@bb|3irt$Kv--cl(TU4+*Skj5bPg-{pXaaVubY>8r7ArFnjWtw@7JhX$D5m5bPn{d zP7S+99HQ%N*Y_vO6M5rnyjd@-s{<%$cJ_zAt(g~m(xH1ZNk&QOKmRhW#WwYx?dB`@H6d`0BDM9Y(KR1N#DHHcqWSqmJ=x?bdk>{+7qT zFXvlUu0-!0In@^YN*ijQ8(1G|IuDfI6%hSNS;ksyw{85vVRyD7kuKq5Ui@OF=#B;j z2ddHeN`41rh{Wy3CwT3I(JkSJeSP)5HJ7;V<~rALu5bh&+M7j%H*{pLHhJ*Sw)vE6$&m+_-~#{Ocvw&+_`96B3`kXx;*GkE6=3 zmrypUB(bGBsLlB81d0kYRe)5{jv=pX+aMB4)vmzo`)~dq1F#7C;|@nGmt}$eYRaZx zo}aCi20Nd<+iPR zp}5g*W_}6*KkfL1)#aO@$XQ8vKHXlesUP~&R+N!>(%4|MS#;|s*lypP;|w|sl1UC7 z+=!S!2OcQRPuNqsi>e8(VgS zQ#^3Fo9Lqzau!BMX7@K!A1JKKjNtK`dDESG;+d~x_foX>__zF7hQM_s!@G#ISRC5r!yyv`@#%vm5vBOHIK~n(EJ6^~oy%4H08JCa%r0fB%&kcE06#zf0v}g!k$2am()O+$2#gQ)q8

6gR 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 aa`^(6`UybqC}mOr0000 Date: Sun, 27 Sep 2020 13:24:24 +0100 Subject: [PATCH 33/63] Icon style --- img/icons/indymedia/rss.png | Bin 5246 -> 4433 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/icons/indymedia/rss.png b/img/icons/indymedia/rss.png index db86c3c21d9148936da9e4eaabcdf7e3011eb1f2..2c9326ea7940302565d3621126d6adb7cbbe0335 100644 GIT binary patch delta 4346 zcmV!+T-&c^fi{KWebpFf;8&wmS^Q-Swzea!8?zj0pP zzVMjC^^fOGxv%(g4|HF63>b7~&%u1ZNY4A~yt`Lo>bee6E&2UYyT9JYW69<1cMpHY zlYU*&&*uG!n2FL9CJNrK;DY6^5BCbf_1pNZ{PiJop9|p}M{_QednE+q!}cEU*1iwW zhaf+W%;)GokAFS|-?#gG_!P@@$B2WM7o5Bf{t5BBiNkY6{^Nzz+}G_qg1l$hdtJM0 zF%i*pBg&dbbWZg5BtTY~!MvuDR{jc^_`D z#ORkNC*J$v!#P*TQS~Eeo}#|o_L=O>P{`Tv%aFbp{C{&T?#x@ye8UxHo{n3+V=%{8 zzI~ZLUHmIAv-i*tIa{8wVqIQw%`!A|`j@*%h}(DG|2Z1x%p0Z=Uy*9c|8p`kq8MXWO74&k*pX6{Nz}`P?I20M3Q7FMyj+FQcRLk z%1KGC<&a~RoN~@3*W5}du}DcJmr`nJ)z<(AN=-G_QfqC^o5nRXlR zy(w*vHg<=xJfGpgzxQtJKOEq{Xn&y4^Ev8UY*@GvQbvR)fhBNoZtlc=4EniKlU?k2zzjw)ecwkYlXYa+z&M@Bxse`t2hyMd`!b zC#zdc#Tl@fEg;X$(ARKF_S$)jNye8ODY!!Zb6YJjsW2y6lTaqtQm?Y}ZS^hWsU$<{zTy34a1}p&(v!sRMldKH|6Ow~JIa<2%wGS9H-gVfCH*-Mwpc z#};2!>H@v(REgGd8+(8omfSz3+I-11M7F(1D`S;7Cva_Jk|d7cyY2*v>n%BkBm5CQ zJBO6Wb?{H$v3|Xta?n&Vs~)dNL>7zZ!-d>);hg5G?sYm@jSY_#`hV5|3_FbZ+zib+ zPUcNS79TcE0lIwVc{yArq-g*$iG7=%WYpOq=iR9QpgLNES~b?7d4vu3QF4F5CpyFCfeamA)ekud7qSFc)LI z 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 delta 5153 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 P00000NkvXXu0mjf4ZQVm From b064d6f4d65d912931acfdaf8f3185b8bea0a2a4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 27 Sep 2020 19:35:35 +0100 Subject: [PATCH 34/63] Deprecate capabilities module --- acceptreject.py | 20 +--- capabilities.py | 283 ------------------------------------------------ daemon.py | 1 - epicyon.py | 24 +--- follow.py | 32 +----- inbox.py | 220 +++++++------------------------------ person.py | 10 -- posts.py | 111 ++----------------- session.py | 7 -- utils.py | 2 +- 10 files changed, 54 insertions(+), 656 deletions(-) delete mode 100644 capabilities.py diff --git a/acceptreject.py b/acceptreject.py index b3229992a..5105e3996 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -7,8 +7,6 @@ __email__ = "bob@freedombone.net" __status__ = "Production" import os -from capabilities import capabilitiesAccept -from capabilities import capabilitiesGrantedSave from utils import urlPermitted from utils import getDomainFromActor from utils import getNicknameFromActor @@ -19,7 +17,7 @@ from utils import followPerson def createAcceptReject(baseDir: str, federationList: [], nickname: str, domain: str, port: int, toUrl: str, ccUrl: str, httpPrefix: str, - objectJson: {}, ocapJson, acceptType: str) -> {}: + objectJson: {}, acceptType: str) -> {}: """Accepts or rejects something (eg. a follow request or offer) Typically toUrl will be https://www.w3.org/ns/activitystreams#Public and ccUrl might be a specific person favorited or repeated and @@ -48,9 +46,6 @@ def createAcceptReject(baseDir: str, federationList: [], if ccUrl: if len(ccUrl) > 0: newAccept['cc'] = [ccUrl] - # attach capabilities for follow accept - if ocapJson: - newAccept['capabilities'] = ocapJson return newAccept @@ -59,14 +54,10 @@ def createAccept(baseDir: str, federationList: [], toUrl: str, ccUrl: str, httpPrefix: str, objectJson: {}, acceptedCaps=["inbox:write", "objects:read"]) -> {}: - # create capabilities accept - ocapNew = capabilitiesAccept(baseDir, httpPrefix, - nickname, domain, port, - toUrl, True, acceptedCaps) return createAcceptReject(baseDir, federationList, nickname, domain, port, toUrl, ccUrl, httpPrefix, - objectJson, ocapNew, 'Accept') + objectJson, 'Accept') def createReject(baseDir: str, federationList: [], @@ -154,13 +145,6 @@ def acceptFollow(baseDir: str, domain: str, messageJson: {}, if acceptedPort: acceptedDomainFull = acceptedDomain + ':' + str(acceptedPort) - # are capabilities attached? If so then store them - if messageJson.get('capabilities'): - if isinstance(messageJson['capabilities'], dict): - capabilitiesGrantedSave(baseDir, - nickname, acceptedDomainFull, - messageJson['capabilities']) - # has this person already been unfollowed? unfollowedFilename = baseDir + '/accounts/' + \ nickname + '@' + acceptedDomainFull + '/unfollowed.txt' diff --git a/capabilities.py b/capabilities.py deleted file mode 100644 index f8ef2bb60..000000000 --- a/capabilities.py +++ /dev/null @@ -1,283 +0,0 @@ -__filename__ = "capabilities.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" - -import os -from auth import createPassword -from utils import getNicknameFromActor -from utils import getDomainFromActor -from utils import loadJson -from utils import saveJson - - -def getOcapFilename(baseDir: str, - nickname: str, domain: str, - actor: str, subdir: str) -> str: - """Returns the filename for a particular capability accepted or granted - Also creates directories as needed - """ - if not actor: - return None - - if ':' in domain: - domain = domain.split(':')[0] - - if not os.path.isdir(baseDir + '/accounts'): - os.mkdir(baseDir + '/accounts') - - ocDir = baseDir + '/accounts/' + nickname + '@' + domain - if not os.path.isdir(ocDir): - os.mkdir(ocDir) - - ocDir = baseDir + '/accounts/' + nickname + '@' + domain + '/ocap' - if not os.path.isdir(ocDir): - os.mkdir(ocDir) - - ocDir = baseDir + '/accounts/' + \ - nickname + '@' + domain + '/ocap/' + subdir - if not os.path.isdir(ocDir): - os.mkdir(ocDir) - - return baseDir + '/accounts/' + \ - nickname + '@' + domain + '/ocap/' + \ - subdir + '/' + actor.replace('/', '#') + '.json' - - -def CapablePost(postJson: {}, capabilityList: [], debug: bool) -> bool: - """Determines whether a post arriving in the inbox - should be accepted accoring to the list of capabilities - """ - if postJson.get('type'): - # No announces/repeats - if postJson['type'] == 'Announce': - if 'inbox:noannounce' in capabilityList: - if debug: - print('DEBUG: ' + - 'inbox post rejected because inbox:noannounce') - return False - # No likes - if postJson['type'] == 'Like': - if 'inbox:nolike' in capabilityList: - if debug: - print('DEBUG: ' + - 'inbox post rejected because inbox:nolike') - return False - if postJson['type'] == 'Create': - if postJson.get('object'): - # Does this have a reply? - if postJson['object'].get('inReplyTo'): - if postJson['object']['inReplyTo']: - if 'inbox:noreply' in capabilityList: - if debug: - print('DEBUG: ' + - 'inbox post rejected because ' + - 'inbox:noreply') - return False - # are content warnings enforced? - if postJson['object'].get('sensitive'): - if not postJson['object']['sensitive']: - if 'inbox:cw' in capabilityList: - if debug: - print('DEBUG: ' + - 'inbox post rejected because inbox:cw') - return False - # content warning must have non-zero summary - if postJson['object'].get('summary'): - if len(postJson['object']['summary']) < 2: - if 'inbox:cw' in capabilityList: - if debug: - print('DEBUG: ' + - 'inbox post rejected because ' + - 'inbox:cw, summary missing') - return False - if 'inbox:write' in capabilityList: - return True - return True - - -def capabilitiesRequest(baseDir: str, httpPrefix: str, domain: str, - requestedActor: str, requestedDomain: str, - requestedCaps=["inbox:write", "objects:read"]) -> {}: - # This is sent to the capabilities endpoint /caps/new - # which could be instance wide or for a particular person - # This could also be added to a follow activity - ocapId = createPassword(32) - ocapRequest = { - "@context": "https://www.w3.org/ns/activitystreams", - "id": httpPrefix + "://" + requestedDomain + "/caps/request/" + ocapId, - "type": "Request", - "capability": requestedCaps, - "actor": requestedActor - } - return ocapRequest - - -def capabilitiesAccept(baseDir: str, httpPrefix: str, - nickname: str, domain: str, port: int, - acceptedActor: str, saveToFile: bool, - acceptedCaps=["inbox:write", "objects:read"]) -> {}: - # This gets returned to capabilities requester - # This could also be added to a follow Accept activity - - # reject excessively long actors - if len(acceptedActor) > 256: - return None - - fullDomain = domain - if port: - if port != 80 and port != 443: - if ':' not in domain: - fullDomain = domain + ':' + str(port) - - # make directories to store capabilities - ocapFilename = \ - getOcapFilename(baseDir, nickname, fullDomain, acceptedActor, 'accept') - if not ocapFilename: - return None - ocapAccept = None - - # if the capability already exists then load it from file - if os.path.isfile(ocapFilename): - ocapAccept = loadJson(ocapFilename) - # otherwise create a new capability - if not ocapAccept: - acceptedActorNickname = getNicknameFromActor(acceptedActor) - if not acceptedActorNickname: - print('WARN: unable to find nickname in ' + acceptedActor) - return None - acceptedActorDomain, acceptedActorPort = \ - getDomainFromActor(acceptedActor) - if acceptedActorPort: - ocapId = acceptedActorNickname + '@' + acceptedActorDomain + \ - ':' + str(acceptedActorPort) + '#'+createPassword(32) - else: - ocapId = acceptedActorNickname + '@' + acceptedActorDomain + \ - '#' + createPassword(32) - ocapAccept = { - "@context": "https://www.w3.org/ns/activitystreams", - "id": httpPrefix + "://" + fullDomain + "/caps/" + ocapId, - "type": "Capability", - "capability": acceptedCaps, - "scope": acceptedActor, - "actor": httpPrefix + "://" + fullDomain - } - if nickname: - ocapAccept['actor'] = \ - httpPrefix + "://" + fullDomain + '/users/' + nickname - - if saveToFile: - saveJson(ocapAccept, ocapFilename) - return ocapAccept - - -def capabilitiesGrantedSave(baseDir: str, - nickname: str, domain: str, ocap: {}) -> bool: - """A capabilities accept is received, so stor it for - reference when sending to the actor - """ - if not ocap.get('actor'): - return False - ocapFilename = \ - getOcapFilename(baseDir, nickname, domain, ocap['actor'], 'granted') - if not ocapFilename: - return False - saveJson(ocap, ocapFilename) - return True - - -def capabilitiesUpdate(baseDir: str, httpPrefix: str, - nickname: str, domain: str, port: int, - updateActor: str, - updateCaps: []) -> {}: - """Used to sends an update for a change of object capabilities - Note that the capability id gets changed with a new random token - so that the old capabilities can't continue to be used - """ - - # reject excessively long actors - if len(updateActor) > 256: - return None - - fullDomain = domain - if port: - if port != 80 and port != 443: - if ':' not in domain: - fullDomain = domain + ':' + str(port) - - # Get the filename of the capability - ocapFilename = \ - getOcapFilename(baseDir, nickname, fullDomain, updateActor, 'accept') - if not ocapFilename: - return None - - # The capability should already exist for it to be updated - if not os.path.isfile(ocapFilename): - return None - - # create an update activity - ocapUpdate = { - "@context": "https://www.w3.org/ns/activitystreams", - 'type': 'Update', - 'actor': httpPrefix + '://' + fullDomain + '/users/' + nickname, - 'to': [updateActor], - 'cc': [], - 'object': {} - } - - # read the existing capability - ocapJson = loadJson(ocapFilename) - - # set the new capabilities list. eg. ["inbox:write","objects:read"] - ocapJson['capability'] = updateCaps - - # change the id, so that the old capabilities can't continue to be used - updateActorNickname = getNicknameFromActor(updateActor) - if not updateActorNickname: - print('WARN: unable to find nickname in ' + updateActor) - return None - updateActorDomain, updateActorPort = getDomainFromActor(updateActor) - if updateActorPort: - ocapId = updateActorNickname + '@' + updateActorDomain + \ - ':' + str(updateActorPort) + '#' + createPassword(32) - else: - ocapId = updateActorNickname + '@' + updateActorDomain + \ - '#' + createPassword(32) - ocapJson['id'] = httpPrefix + "://" + fullDomain + "/caps/" + ocapId - ocapUpdate['object'] = ocapJson - - # save it again - saveJson(ocapJson, ocapFilename) - - return ocapUpdate - - -def capabilitiesReceiveUpdate(baseDir: str, - nickname: str, domain: str, port: int, - actor: str, - newCapabilitiesId: str, - capabilityList: [], debug: bool) -> bool: - """An update for a capability or the given actor has arrived - """ - ocapFilename = \ - getOcapFilename(baseDir, nickname, domain, actor, 'granted') - if not ocapFilename: - return False - - if not os.path.isfile(ocapFilename): - if debug: - print('DEBUG: capabilities file not found during update') - print(ocapFilename) - return False - - ocapJson = loadJson(ocapFilename) - - if ocapJson: - ocapJson['id'] = newCapabilitiesId - ocapJson['capability'] = capabilityList - - return saveJson(ocapJson, ocapFilename) - return False diff --git a/daemon.py b/daemon.py index 9bcd5e07f..135a00111 100644 --- a/daemon.py +++ b/daemon.py @@ -10299,7 +10299,6 @@ class PubServer(BaseHTTPRequestHandler): self.path.endswith('/inbox') or self.path.endswith('/shares') or self.path.endswith('/moderationaction') or - self.path.endswith('/caps/new') or self.path == '/sharedInbox'): print('Attempt to POST to invalid path ' + self.path) self._400() diff --git a/epicyon.py b/epicyon.py index 507e58ab4..ef8c569b3 100644 --- a/epicyon.py +++ b/epicyon.py @@ -230,26 +230,6 @@ parser.add_argument("--testsnetwork", type=str2bool, nargs='?', parser.add_argument("--testdata", type=str2bool, nargs='?', const=True, default=False, help="Generate some data for testing purposes") -parser.add_argument("--ocap", type=str2bool, nargs='?', - const=True, default=False, - help="Always strictly enforce object capabilities") -parser.add_argument("--noreply", type=str2bool, nargs='?', - const=True, default=False, - help="Default capabilities don't allow replies on posts") -parser.add_argument("--nolike", type=str2bool, nargs='?', - const=True, default=False, - help="Default capabilities don't allow " + - "likes/favourites on posts") -parser.add_argument("--nopics", type=str2bool, nargs='?', - const=True, default=False, - help="Default capabilities don't allow attached pictures") -parser.add_argument("--noannounce", "--norepeat", type=str2bool, nargs='?', - const=True, default=False, - help="Default capabilities don't allow announce/repeat") -parser.add_argument("--cw", type=str2bool, nargs='?', - const=True, default=False, - help="Default capabilities don't allow posts " + - "without content warnings") parser.add_argument('--icon', '--avatar', dest='avatar', type=str, default=None, help='Set the avatar filename for an account') @@ -1920,8 +1900,8 @@ if __name__ == "__main__": port, proxyPort, httpPrefix, federationList, args.maxMentions, args.maxEmoji, args.authenticatedFetch, - args.noreply, args.nolike, args.nopics, - args.noannounce, args.cw, ocapAlways, + False, False, False, + False, False, ocapAlways, proxyType, args.maxReplies, args.domainMaxPostsPerDay, args.accountMaxPostsPerDay, diff --git a/follow.py b/follow.py index f641e916e..5b00d81f1 100644 --- a/follow.py +++ b/follow.py @@ -1045,14 +1045,12 @@ def getFollowersOfActor(baseDir: str, actor: str, debug: bool) -> {}: """In a shared inbox if we receive a post we know who it's from and if it's addressed to followers then we need to get a list of those. This returns a list of account handles which follow the given actor - and also the corresponding capability id if it exists """ if debug: print('DEBUG: getting followers of ' + actor) recipientsDict = {} if ':' not in actor: return recipientsDict - httpPrefix = actor.split(':')[0] nickname = getNicknameFromActor(actor) if not nickname: if debug: @@ -1084,35 +1082,7 @@ def getFollowersOfActor(baseDir: str, actor: str, debug: bool) -> {}: if debug: print('DEBUG: ' + account + ' follows ' + actorHandle) - ocapFilename = baseDir + '/accounts/' + \ - account + '/ocap/accept/' + httpPrefix + \ - ':##' + domain + ':' + str(port) + \ - '#users#' + nickname + '.json' - if debug: - print('DEBUG: checking capabilities of' + account) - if os.path.isfile(ocapFilename): - ocapJson = loadJson(ocapFilename) - if ocapJson: - if ocapJson.get('id'): - if debug: - print('DEBUG: ' + - 'capabilities id found for ' + - account) - - recipientsDict[account] = ocapJson['id'] - else: - if debug: - print('DEBUG: ' + - 'capabilities has no ' + - 'id attribute') - recipientsDict[account] = None - else: - if debug: - print('DEBUG: ' + - 'No capabilities file found for ' + - account + ' granted by ' + actorHandle) - print(ocapFilename) - recipientsDict[account] = None + recipientsDict[account] = None return recipientsDict diff --git a/inbox.py b/inbox.py index e1b0c0b89..74ed927a4 100644 --- a/inbox.py +++ b/inbox.py @@ -40,9 +40,6 @@ from pprint import pprint from cache import getPersonFromCache from cache import storePersonInCache from acceptreject import receiveAcceptReject -from capabilities import getOcapFilename -from capabilities import CapablePost -from capabilities import capabilitiesReceiveUpdate from bookmarks import updateBookmarksCollection from bookmarks import undoBookmarksCollectionEntry from blocking import isBlocked @@ -437,81 +434,12 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str, return filename -def inboxCheckCapabilities(baseDir: str, nickname: str, domain: str, - actor: str, queueFilename: str, queue: [], - queueJson: {}, capabilityId: str, - debug: bool) -> bool: - if nickname == 'inbox': - return True - - ocapFilename = \ - getOcapFilename(baseDir, - queueJson['nickname'], queueJson['domain'], - actor, 'accept') - if not ocapFilename: - return False - if not os.path.isfile(ocapFilename): - if debug: - print('DEBUG: capabilities for ' + - actor + ' do not exist') - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - return False - - oc = loadJson(ocapFilename) - if not oc: - return False - - if not oc.get('id'): - if debug: - print('DEBUG: capabilities for ' + actor + ' do not contain an id') - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - return False - - if oc['id'] != capabilityId: - if debug: - print('DEBUG: capability id mismatch') - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - return False - - if not oc.get('capability'): - if debug: - print('DEBUG: missing capability list') - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - return False - - if not CapablePost(queueJson['post'], oc['capability'], debug): - if debug: - print('DEBUG: insufficient capabilities to write to inbox from ' + - actor) - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - return False - - if debug: - print('DEBUG: object capabilities check success') - return True - - def inboxPostRecipientsAdd(baseDir: str, httpPrefix: str, toList: [], recipientsDict: {}, domainMatch: str, domain: str, actor: str, debug: bool) -> bool: """Given a list of post recipients (toList) from 'to' or 'cc' parameters - populate a recipientsDict with the handle and capabilities id for each + populate a recipientsDict with the handle for each """ followerRecipients = False for recipient in toList: @@ -523,24 +451,7 @@ def inboxPostRecipientsAdd(baseDir: str, httpPrefix: str, toList: [], nickname = recipient.split(domainMatch)[1] handle = nickname+'@'+domain if os.path.isdir(baseDir + '/accounts/' + handle): - # are capabilities granted for this account to the - # sender (actor) of the post? - ocapFilename = \ - baseDir + '/accounts/' + handle + \ - '/ocap/accept/' + actor.replace('/', '#') + '.json' - if os.path.isfile(ocapFilename): - # read the granted capabilities and obtain the id - ocapJson = loadJson(ocapFilename) - if ocapJson: - if ocapJson.get('id'): - # append with the capabilities id - recipientsDict[handle] = ocapJson['id'] - else: - recipientsDict[handle] = None - else: - if debug: - print('DEBUG: ' + ocapFilename + ' not found') - recipientsDict[handle] = None + recipientsDict[handle] = None else: if debug: print('DEBUG: ' + baseDir + '/accounts/' + @@ -1005,24 +916,6 @@ def receiveUpdate(recentPostsCache: {}, session, baseDir: str, print('DEBUG: Profile update was received for ' + messageJson['object']['url']) return True - - if messageJson['object'].get('capability') and \ - messageJson['object'].get('scope'): - nickname = getNicknameFromActor(messageJson['object']['scope']) - if nickname: - domain, tempPort = \ - getDomainFromActor(messageJson['object']['scope']) - - if messageJson['object']['type'] == 'Capability': - capability = messageJson['object']['capability'] - if capabilitiesReceiveUpdate(baseDir, nickname, domain, port, - messageJson['actor'], - messageJson['object']['id'], - capability, - debug): - if debug: - print('DEBUG: An update was received') - return True return False @@ -2124,20 +2017,20 @@ def inboxUpdateIndex(boxname: str, baseDir: str, handle: str, return False -def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, - session, keyId: str, handle: str, messageJson: {}, - baseDir: str, httpPrefix: str, sendThreads: [], - postLog: [], cachedWebfingers: {}, personCache: {}, - queue: [], domain: str, - onionDomain: str, i2pDomain: str, - port: int, proxyType: str, - federationList: [], ocapAlways: bool, debug: bool, - acceptedCaps: [], - queueFilename: str, destinationFilename: str, - maxReplies: int, allowDeletion: bool, - maxMentions: int, maxEmoji: int, translate: {}, - unitTest: bool, YTReplacementDomain: str) -> bool: - """ Anything which needs to be done after capabilities checks have passed +def inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, + session, keyId: str, handle: str, messageJson: {}, + baseDir: str, httpPrefix: str, sendThreads: [], + postLog: [], cachedWebfingers: {}, personCache: {}, + queue: [], domain: str, + onionDomain: str, i2pDomain: str, + port: int, proxyType: str, + federationList: [], ocapAlways: bool, debug: bool, + acceptedCaps: [], + queueFilename: str, destinationFilename: str, + maxReplies: int, allowDeletion: bool, + maxMentions: int, maxEmoji: int, translate: {}, + unitTest: bool, YTReplacementDomain: str) -> bool: + """ Anything which needs to be done after initial checks have passed """ actor = keyId if '#' in actor: @@ -2247,7 +2140,7 @@ def inboxAfterCapabilities(recentPostsCache: {}, maxRecentPosts: int, return False if debug: - print('DEBUG: object capabilities passed') + print('DEBUG: initial checks passed') print('copy queue file from ' + queueFilename + ' to ' + destinationFilename) @@ -2929,10 +2822,6 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, # Copy any posts addressed to followers into the shared inbox # this avoid copying file multiple times to potentially many # individual inboxes - # This obviously bypasses object capabilities and so - # any checking will needs to be handled at the time when inbox - # GET happens on individual accounts. - # See posts.py/createBoxBase if len(recipientsDictFollowers) > 0: sharedInboxPostFilename = \ queueJson['destination'].replace(inboxHandle, inboxHandle) @@ -2943,61 +2832,26 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, for handle, capsId in recipientsDict.items(): destination = \ queueJson['destination'].replace(inboxHandle, handle) - # check that capabilities are accepted - if queueJson['post'].get('capability'): - capabilityIdList = queueJson['post']['capability'] - # does the capability id list within the post - # contain the id of the recipient with this handle? - # Here the capability id begins with the handle, - # so this could also be matched separately, but it's - # probably not necessary - if capsId in capabilityIdList: - inboxAfterCapabilities(recentPostsCache, - maxRecentPosts, - session, keyId, handle, - queueJson['post'], - baseDir, httpPrefix, - sendThreads, postLog, - cachedWebfingers, - personCache, queue, - domain, - onionDomain, i2pDomain, - port, proxyType, - federationList, ocapAlways, - debug, acceptedCaps, - queueFilename, destination, - maxReplies, allowDeletion, - maxMentions, maxEmoji, - translate, unitTest, - YTReplacementDomain) - else: - print('Queue: object capabilities check has failed') - if debug: - pprint(queueJson['post']) - else: - if not ocapAlways: - inboxAfterCapabilities(recentPostsCache, - maxRecentPosts, - session, keyId, handle, - queueJson['post'], - baseDir, httpPrefix, - sendThreads, postLog, - cachedWebfingers, - personCache, queue, - domain, - onionDomain, i2pDomain, - port, proxyType, - federationList, ocapAlways, - debug, acceptedCaps, - queueFilename, destination, - maxReplies, allowDeletion, - maxMentions, maxEmoji, - translate, unitTest, - YTReplacementDomain) - if debug: - pprint(queueJson['post']) - print('No capability list within post') - print('ocapAlways: ' + str(ocapAlways)) + inboxAfterInitial(recentPostsCache, + maxRecentPosts, + session, keyId, handle, + queueJson['post'], + baseDir, httpPrefix, + sendThreads, postLog, + cachedWebfingers, + personCache, queue, + domain, + onionDomain, i2pDomain, + port, proxyType, + federationList, ocapAlways, + debug, acceptedCaps, + queueFilename, destination, + maxReplies, allowDeletion, + maxMentions, maxEmoji, + translate, unitTest, + YTReplacementDomain) + if debug: + pprint(queueJson['post']) print('Queue: Queue post accepted') if os.path.isfile(queueFilename): diff --git a/person.py b/person.py index 071f4beda..cbeca6652 100644 --- a/person.py +++ b/person.py @@ -259,7 +259,6 @@ def createPersonBase(baseDir: str, nickname: str, domain: str, port: int, 'id': personId+'/endpoints', 'sharedInbox': httpPrefix+'://'+domain+'/inbox', }, - 'capabilityAcquisitionEndpoint': httpPrefix+'://'+domain+'/caps/new', 'followers': personId+'/followers', 'following': personId+'/following', 'shares': personId+'/shares', @@ -506,15 +505,6 @@ def createSharedInbox(baseDir: str, nickname: str, domain: str, port: int, True, True, None) -def createCapabilitiesInbox(baseDir: str, nickname: str, - domain: str, port: int, - httpPrefix: str) -> (str, str, {}, {}): - """Generates the capabilities inbox to sign requests - """ - return createPersonBase(baseDir, nickname, domain, port, - httpPrefix, True, True, None) - - def personUpgradeActor(baseDir: str, personJson: {}, handle: str, filename: str) -> None: """Alter the actor to add any new properties diff --git a/posts.py b/posts.py index cffc4d8f6..37d806d50 100644 --- a/posts.py +++ b/posts.py @@ -45,8 +45,6 @@ from utils import validNickname from utils import locatePost from utils import loadJson from utils import saveJson -from capabilities import getOcapFilename -from capabilities import capabilitiesUpdate from media import attachMedia from media import replaceYouTube from content import removeHtml @@ -893,24 +891,12 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int, if not clientToServer: actorUrl = httpPrefix + '://' + domain + '/users/' + nickname - # if capabilities have been granted for this actor - # then get the corresponding id - capabilityIdList = [] - ocapFilename = getOcapFilename(baseDir, nickname, domain, - toUrl, 'granted') - if ocapFilename: - if os.path.isfile(ocapFilename): - oc = loadJson(ocapFilename) - if oc: - if oc.get('id'): - capabilityIdList = [oc['id']] idStr = \ httpPrefix + '://' + domain + '/users/' + nickname + \ '/statuses/' + statusNumber + '/replies' newPost = { '@context': postContext, 'id': newPostId + '/activity', - 'capability': capabilityIdList, 'type': 'Create', 'actor': actorUrl, 'published': published, @@ -1674,20 +1660,13 @@ def sendPost(projectVersion: str, projectVersion, httpPrefix, nickname, domain, postToBox) - # If there are more than one followers on the target domain - # then send to the shared inbox indead of the individual inbox - if nickname == 'capabilities': - inboxUrl = capabilityAcquisition - if not capabilityAcquisition: - return 2 - if not inboxUrl: return 3 if not pubKey: return 4 if not toPersonId: return 5 - # sharedInbox and capabilities are optional + # sharedInbox is optional postJsonObject = \ createPostBase(baseDir, nickname, domain, port, @@ -2003,7 +1982,7 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, else: postToBox = 'outbox' - # get the actor inbox/outbox/capabilities for the To handle + # get the actor inbox/outbox for the To handle (inboxUrl, pubKeyId, pubKey, toPersonId, sharedInboxUrl, capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, @@ -2011,17 +1990,12 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, projectVersion, httpPrefix, nickname, domain, postToBox) - if nickname == 'capabilities': - inboxUrl = capabilityAcquisition - if not capabilityAcquisition: - return 2 - else: - print("inboxUrl: " + str(inboxUrl)) - print("toPersonId: " + str(toPersonId)) - print("sharedInboxUrl: " + str(sharedInboxUrl)) - if inboxUrl: - if inboxUrl.endswith('/actor/inbox'): - inboxUrl = sharedInboxUrl + print("inboxUrl: " + str(inboxUrl)) + print("toPersonId: " + str(toPersonId)) + print("sharedInboxUrl: " + str(sharedInboxUrl)) + if inboxUrl: + if inboxUrl.endswith('/actor/inbox'): + inboxUrl = sharedInboxUrl if not inboxUrl: if debug: @@ -2039,7 +2013,7 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, if debug: print('DEBUG: missing personId') return 5 - # sharedInbox and capabilities are optional + # sharedInbox is optional # get the senders private key privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private', debug) @@ -2791,32 +2765,8 @@ def createSharedInboxIndex(baseDir: str, sharedBoxDir: str, if actorNickname + '@' + actorDomain not in followingHandles: continue - if ocapAlways: - capsList = None - # Note: should this be in the Create or the object of a post? - if postJsonObject.get('capability'): - if isinstance(postJsonObject['capability'], list): - capsList = postJsonObject['capability'] - - # Have capabilities been granted for the sender? - ocapFilename = \ - baseDir + '/accounts/' + handle + '/ocap/granted/' + \ - postJsonObject['actor'].replace('/', '#') + '.json' - if not os.path.isfile(ocapFilename): - continue - - # read the capabilities id - ocapJson = loadJson(ocapFilename, 0) - if not ocapJson: - print('WARN: json load exception createSharedInboxIndex') - else: - if ocapJson.get('id'): - if ocapJson['id'] in capsList: - postsInBoxDict[statusNumber] = sharedInboxFilename - postsCtr += 1 - else: - postsInBoxDict[statusNumber] = sharedInboxFilename - postsCtr += 1 + postsInBoxDict[statusNumber] = sharedInboxFilename + postsCtr += 1 return postsCtr @@ -3413,45 +3363,6 @@ def checkDomains(session, baseDir: str, print(followerWarningStr) -def sendCapabilitiesUpdate(session, baseDir: str, httpPrefix: str, - nickname: str, domain: str, port: int, - followerUrl, updateCaps: [], - sendThreads: [], postLog: [], - cachedWebfingers: {}, personCache: {}, - federationList: [], debug: bool, - projectVersion: str) -> int: - """When the capabilities for a follower are changed this - sends out an update. followerUrl is the actor of the follower. - """ - updateJson = \ - capabilitiesUpdate(baseDir, httpPrefix, - nickname, domain, port, - followerUrl, updateCaps) - - if not updateJson: - return 1 - - if debug: - pprint(updateJson) - print('DEBUG: sending capabilities update from ' + - nickname + '@' + domain + ' port ' + str(port) + - ' to ' + followerUrl) - - clientToServer = False - followerNickname = getNicknameFromActor(followerUrl) - if not followerNickname: - print('WARN: unable to find nickname in ' + followerUrl) - return 1 - followerDomain, followerPort = getDomainFromActor(followerUrl) - return sendSignedJson(updateJson, session, baseDir, - nickname, domain, port, - followerNickname, followerDomain, followerPort, '', - httpPrefix, True, clientToServer, - federationList, - sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) - - def populateRepliesJson(baseDir: str, nickname: str, domain: str, postRepliesFilename: str, authorized: bool, repliesJson: {}) -> None: diff --git a/session.py b/session.py index 26322bad7..9e3a14ec5 100644 --- a/session.py +++ b/session.py @@ -141,13 +141,6 @@ def postJsonString(session, postJsonStr: str, conversions between string and json format don't invalidate the message body digest of http signatures """ - # always allow capability requests - if not capability.startswith('cap'): - # check that we are posting to a permitted domain - if not urlPermitted(inboxUrl, federationList, capability): - print('postJson: ' + inboxUrl + ' not permitted by capabilities') - return None, None - try: postResult = \ session.post(url=inboxUrl, data=postJsonStr, headers=headers) diff --git a/utils.py b/utils.py index 014c5bc20..445ffc5ff 100644 --- a/utils.py +++ b/utils.py @@ -669,7 +669,7 @@ def validNickname(domain: str, nickname: str) -> bool: return False reservedNames = ('inbox', 'dm', 'outbox', 'following', 'public', 'followers', - 'channel', 'capabilities', 'calendar', + 'channel', 'calendar', 'tlreplies', 'tlmedia', 'tlblogs', 'tlevents', 'moderation', 'activity', 'undo', From 6435156179cfccc9d2b5905385c0a70126d16a23 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 27 Sep 2020 20:27:24 +0100 Subject: [PATCH 35/63] Remove capabilities --- acceptreject.py | 5 ++- announce.py | 19 ++++++----- availability.py | 3 +- bookmarks.py | 14 ++++---- daemon.py | 74 +++++++++++++----------------------------- delete.py | 7 ++-- epicyon.py | 7 +--- follow.py | 27 +++++++--------- inbox.py | 36 ++++++--------------- like.py | 16 ++++------ manualapprove.py | 3 +- person.py | 24 ++++++-------- posts.py | 83 +++++++++++++++++------------------------------- roles.py | 3 +- session.py | 28 ++++++---------- shares.py | 8 ++--- skills.py | 3 +- socnet.py | 1 - tests.py | 30 +++++++---------- utils.py | 2 +- webinterface.py | 17 ++++------ 21 files changed, 147 insertions(+), 263 deletions(-) diff --git a/acceptreject.py b/acceptreject.py index 5105e3996..1abb6dd70 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -27,7 +27,7 @@ def createAcceptReject(baseDir: str, federationList: [], if not objectJson.get('actor'): return None - if not urlPermitted(objectJson['actor'], federationList, "inbox:write"): + if not urlPermitted(objectJson['actor'], federationList): return None if port: @@ -52,8 +52,7 @@ def createAcceptReject(baseDir: str, federationList: [], def createAccept(baseDir: str, federationList: [], nickname: str, domain: str, port: int, toUrl: str, ccUrl: str, httpPrefix: str, - objectJson: {}, - acceptedCaps=["inbox:write", "objects:read"]) -> {}: + objectJson: {}) -> {}: return createAcceptReject(baseDir, federationList, nickname, domain, port, toUrl, ccUrl, httpPrefix, diff --git a/announce.py b/announce.py index 5916f55a1..799e8689a 100644 --- a/announce.py +++ b/announce.py @@ -108,7 +108,7 @@ def createAnnounce(session, baseDir: str, federationList: [], followers url objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None if ':' in domain: @@ -231,7 +231,7 @@ def undoAnnounce(session, baseDir: str, federationList: [], objectUrl is typically the url of the message which was repeated, corresponding to url or atomUri in createPostBase """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None if ':' in domain: @@ -391,12 +391,12 @@ def sendAnnounceViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, - sharedInbox, capabilityAcquisition, - avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, - personCache, - projectVersion, httpPrefix, - fromNickname, fromDomain, - postToBox) + sharedInbox, avatarUrl, + displayName) = getPersonBox(baseDir, session, wfRequest, + personCache, + projectVersion, httpPrefix, + fromNickname, fromDomain, + postToBox) if not inboxUrl: if debug: @@ -414,8 +414,7 @@ def sendAnnounceViaServer(baseDir: str, session, 'Content-type': 'application/json', 'Authorization': authHeader } - postResult = postJson(session, newAnnounceJson, [], inboxUrl, - headers, "inbox:write") + postResult = postJson(session, newAnnounceJson, [], inboxUrl, headers) if not postResult: print('WARN: Announce not posted') diff --git a/availability.py b/availability.py index e1415d121..201e0d06e 100644 --- a/availability.py +++ b/availability.py @@ -123,7 +123,6 @@ def sendAvailabilityViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, nickname, @@ -146,7 +145,7 @@ def sendAvailabilityViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = postJson(session, newAvailabilityJson, [], - inboxUrl, headers, "inbox:write") + inboxUrl, headers) if not postResult: print('WARN: failed to post availability') diff --git a/bookmarks.py b/bookmarks.py index 44b951971..43415adb6 100644 --- a/bookmarks.py +++ b/bookmarks.py @@ -234,7 +234,7 @@ def bookmark(recentPostsCache: {}, 'to' might be a specific person (actor) whose post was bookmarked object is typically the url of the message which was bookmarked """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None fullDomain = domain @@ -330,7 +330,7 @@ def undoBookmark(recentPostsCache: {}, 'to' might be a specific person (actor) whose post was bookmarked object is typically the url of the message which was bookmarked """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None fullDomain = domain @@ -457,8 +457,7 @@ def sendBookmarkViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, fromDomain, postToBox) @@ -480,7 +479,7 @@ def sendBookmarkViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = postJson(session, newBookmarkJson, [], - inboxUrl, headers, "inbox:write") + inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) @@ -539,8 +538,7 @@ def sendUndoBookmarkViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, fromDomain, postToBox) @@ -562,7 +560,7 @@ def sendUndoBookmarkViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = postJson(session, newUndoBookmarkJson, [], - inboxUrl, headers, "inbox:write") + inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) diff --git a/daemon.py b/daemon.py index 135a00111..f72ee5228 100644 --- a/daemon.py +++ b/daemon.py @@ -444,7 +444,7 @@ class PubServer(BaseHTTPRequestHandler): 'failed to obtain keyId from signature') return False # is the keyId (actor) valid? - if not urlPermitted(keyId, self.server.federationList, "inbox:read"): + if not urlPermitted(keyId, self.server.federationList): if self.server.debug: print('Authorized fetch failed: ' + keyId + ' is not permitted') @@ -4364,7 +4364,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.postLog, self.server.cachedWebfingers, self.server.personCache, - self.server.acceptedCaps, debug, self.server.projectVersion) originPathStrAbsolute = \ @@ -5233,7 +5232,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.translate, self.server.projectVersion, baseDir, httpPrefix, True, - self.server.ocapAlways, getPerson, 'roles', self.server.session, cachedWebfingers, @@ -5304,7 +5302,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.translate, self.server.projectVersion, baseDir, httpPrefix, True, - self.server.ocapAlways, getPerson, 'skills', self.server.session, cachedWebfingers, @@ -5571,7 +5568,6 @@ class PubServer(BaseHTTPRequestHandler): proxyType: str, cookie: str, debug: str, recentPostsCache: {}, session, - ocapAlways: bool, defaultTimeline: str, maxRecentPosts: int, translate: {}, @@ -5593,8 +5589,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'inbox', - authorized, - ocapAlways) + authorized) if inboxFeed: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', @@ -5621,8 +5616,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInFeed, 'inbox', - authorized, - ocapAlways) + authorized) self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', @@ -5702,8 +5696,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'dm', - authorized, - self.server.ocapAlways) + authorized) if inboxDMFeed: if self._requestHTTP(): nickname = path.replace('/users/', '') @@ -5727,8 +5720,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInFeed, 'dm', - authorized, - self.server.ocapAlways) + authorized) msg = \ htmlInboxDMs(self.server.defaultTimeline, self.server.recentPostsCache, @@ -5803,7 +5795,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'tlreplies', - True, self.server.ocapAlways) + True) if not inboxRepliesFeed: inboxRepliesFeed = [] if self._requestHTTP(): @@ -5828,7 +5820,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInFeed, 'tlreplies', - True, self.server.ocapAlways) + True) msg = \ htmlInboxReplies(self.server.defaultTimeline, self.server.recentPostsCache, @@ -5903,7 +5895,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInMediaFeed, 'tlmedia', - True, self.server.ocapAlways) + True) if not inboxMediaFeed: inboxMediaFeed = [] if self._requestHTTP(): @@ -5928,7 +5920,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInMediaFeed, 'tlmedia', - True, self.server.ocapAlways) + True) msg = \ htmlInboxMedia(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6003,7 +5995,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInBlogsFeed, 'tlblogs', - True, self.server.ocapAlways) + True) if not inboxBlogsFeed: inboxBlogsFeed = [] if self._requestHTTP(): @@ -6028,7 +6020,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInBlogsFeed, 'tlblogs', - True, self.server.ocapAlways) + True) msg = \ htmlInboxBlogs(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6161,7 +6153,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'tlbookmarks', - authorized, self.server.ocapAlways) + authorized) if bookmarksFeed: if self._requestHTTP(): nickname = path.replace('/users/', '') @@ -6187,8 +6179,7 @@ class PubServer(BaseHTTPRequestHandler): httpPrefix, maxPostsInFeed, 'tlbookmarks', - authorized, - self.server.ocapAlways) + authorized) msg = \ htmlBookmarks(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6265,7 +6256,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'tlevents', - authorized, self.server.ocapAlways) + authorized) print('eventsFeed: ' + str(eventsFeed)) if eventsFeed: if self._requestHTTP(): @@ -6291,8 +6282,7 @@ class PubServer(BaseHTTPRequestHandler): httpPrefix, maxPostsInFeed, 'tlevents', - authorized, - self.server.ocapAlways) + authorized) msg = \ htmlEvents(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6361,8 +6351,7 @@ class PubServer(BaseHTTPRequestHandler): port, path, httpPrefix, maxPostsInFeed, 'outbox', - authorized, - self.server.ocapAlways) + authorized) if outboxFeed: if self._requestHTTP(): nickname = \ @@ -6386,8 +6375,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInFeed, 'outbox', - authorized, - self.server.ocapAlways) + authorized) msg = \ htmlOutbox(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6449,7 +6437,7 @@ class PubServer(BaseHTTPRequestHandler): path, httpPrefix, maxPostsInFeed, 'moderation', - True, self.server.ocapAlways) + True) if moderationFeed: if self._requestHTTP(): nickname = path.replace('/users/', '') @@ -6473,7 +6461,7 @@ class PubServer(BaseHTTPRequestHandler): path + '?page=1', httpPrefix, maxPostsInFeed, 'moderation', - True, self.server.ocapAlways) + True) msg = \ htmlModeration(self.server.defaultTimeline, self.server.recentPostsCache, @@ -6576,7 +6564,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.projectVersion, baseDir, httpPrefix, authorized, - self.server.ocapAlways, getPerson, 'shares', self.server.session, self.server.cachedWebfingers, @@ -6663,7 +6650,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.projectVersion, baseDir, httpPrefix, authorized, - self.server.ocapAlways, getPerson, 'following', self.server.session, self.server.cachedWebfingers, @@ -6750,7 +6736,6 @@ class PubServer(BaseHTTPRequestHandler): baseDir, httpPrefix, authorized, - self.server.ocapAlways, getPerson, 'followers', self.server.session, self.server.cachedWebfingers, @@ -6812,7 +6797,6 @@ class PubServer(BaseHTTPRequestHandler): baseDir, httpPrefix, authorized, - self.server.ocapAlways, getPerson, 'posts', self.server.session, self.server.cachedWebfingers, @@ -8740,7 +8724,6 @@ class PubServer(BaseHTTPRequestHandler): cookie, self.server.debug, self.server.recentPostsCache, self.server.session, - self.server.ocapAlways, self.server.defaultTimeline, self.server.maxRecentPosts, self.server.translate, @@ -10621,7 +10604,7 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, fedList=[], maxMentions=10, maxEmoji=10, authenticatedFetch=False, noreply=False, nolike=False, nopics=False, - noannounce=False, cw=False, ocapAlways=False, + noannounce=False, cw=False, proxyType=None, maxReplies=64, domainMaxPostsPerDay=8640, accountMaxPostsPerDay=864, allowDeletion=False, debug=False, unitTest=False, @@ -10753,7 +10736,6 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, httpd.sendThreads = sendThreads httpd.postLog = [] httpd.maxQueueLength = 64 - httpd.ocapAlways = ocapAlways httpd.allowDeletion = allowDeletion httpd.lastLoginTime = 0 httpd.maxReplies = maxReplies @@ -10761,19 +10743,8 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, httpd.tokensLookup = {} loadTokens(baseDir, httpd.tokens, httpd.tokensLookup) httpd.instanceOnlySkillsSearch = instanceOnlySkillsSearch - httpd.acceptedCaps = ["inbox:write", "objects:read"] # contains threads used to send posts to followers httpd.followersThreads = [] - if noreply: - httpd.acceptedCaps.append('inbox:noreply') - if nolike: - httpd.acceptedCaps.append('inbox:nolike') - if nopics: - httpd.acceptedCaps.append('inbox:nopics') - if noannounce: - httpd.acceptedCaps.append('inbox:noannounce') - if cw: - httpd.acceptedCaps.append('inbox:cw') if not os.path.isdir(baseDir + '/accounts/inbox@' + domain): print('Creating shared inbox: inbox@' + domain) @@ -10844,12 +10815,11 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, httpd.personCache, httpd.inboxQueue, domain, onionDomain, i2pDomain, port, proxyType, httpd.federationList, - httpd.ocapAlways, maxReplies, + maxReplies, domainMaxPostsPerDay, accountMaxPostsPerDay, allowDeletion, debug, maxMentions, maxEmoji, httpd.translate, unitTest, - httpd.YTReplacementDomain, - httpd.acceptedCaps), daemon=True) + httpd.YTReplacementDomain), daemon=True) print('Creating scheduled post thread') httpd.thrPostSchedule = \ threadWithTrace(target=runPostSchedule, diff --git a/delete.py b/delete.py index 1b537d391..751b95aa0 100644 --- a/delete.py +++ b/delete.py @@ -34,7 +34,7 @@ def createDelete(session, baseDir: str, federationList: [], objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None if ':' in domain: @@ -137,8 +137,7 @@ def sendDeleteViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, fromDomain, postToBox) @@ -160,7 +159,7 @@ def sendDeleteViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = \ - postJson(session, newDeleteJson, [], inboxUrl, headers, "inbox:write") + postJson(session, newDeleteJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) diff --git a/epicyon.py b/epicyon.py index ef8c569b3..906872407 100644 --- a/epicyon.py +++ b/epicyon.py @@ -788,7 +788,6 @@ if args.approve: postLog = [] cachedWebfingers = {} personCache = {} - acceptedCaps = [] manualApproveFollowRequest(session, baseDir, httpPrefix, args.nickname, domain, port, @@ -796,7 +795,6 @@ if args.approve: federationList, sendThreads, postLog, cachedWebfingers, personCache, - acceptedCaps, debug, __version__) sys.exit() @@ -1181,9 +1179,6 @@ if args.port: if args.proxyPort: proxyPort = args.proxyPort setConfigParam(baseDir, 'proxyPort', proxyPort) -ocapAlways = False -if args.ocap: - ocapAlways = args.ocap if args.gnunet: httpPrefix = 'gnunet' if args.dat: @@ -1901,7 +1896,7 @@ if __name__ == "__main__": federationList, args.maxMentions, args.maxEmoji, args.authenticatedFetch, False, False, False, - False, False, ocapAlways, + False, False, proxyType, args.maxReplies, args.domainMaxPostsPerDay, args.accountMaxPostsPerDay, diff --git a/follow.py b/follow.py index 5b00d81f1..dc8a7cbda 100644 --- a/follow.py +++ b/follow.py @@ -513,8 +513,7 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str, port: int, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, messageJson: {}, federationList: [], - debug: bool, projectVersion: str, - acceptedCaps=["inbox:write", "objects:read"]) -> bool: + debug: bool, projectVersion: str) -> bool: """Receives a follow request within the POST section of HTTPServer """ if not messageJson['type'].startswith('Follow'): @@ -655,8 +654,7 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str, nicknameToFollow, domainToFollow, port, nickname, domain, fromPort, messageJson['actor'], federationList, - messageJson, acceptedCaps, - sendThreads, postLog, + messageJson, sendThreads, postLog, cachedWebfingers, personCache, debug, projectVersion, True) @@ -666,8 +664,7 @@ def followedAccountAccepts(session, baseDir: str, httpPrefix: str, port: int, nickname: str, domain: str, fromPort: int, personUrl: str, federationList: [], - followJson: {}, acceptedCaps: [], - sendThreads: [], postLog: [], + followJson: {}, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, debug: bool, projectVersion: str, removeFollowActivity: bool): @@ -685,7 +682,7 @@ def followedAccountAccepts(session, baseDir: str, httpPrefix: str, acceptJson = createAccept(baseDir, federationList, nicknameToFollow, domainToFollow, port, personUrl, '', httpPrefix, - followJson, acceptedCaps) + followJson) if debug: pprint(acceptJson) print('DEBUG: sending follow Accept from ' + @@ -908,8 +905,7 @@ def sendFollowRequestViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, fromDomain, postToBox) @@ -931,7 +927,7 @@ def sendFollowRequestViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = \ - postJson(session, newFollowJson, [], inboxUrl, headers, "inbox:write") + postJson(session, newFollowJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) @@ -1007,10 +1003,11 @@ def sendUnfollowRequestViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, - displayName) = getPersonBox(baseDir, session, wfRequest, personCache, - projectVersion, httpPrefix, fromNickname, - fromDomain, postToBox) + avatarUrl, displayName) = getPersonBox(baseDir, session, + wfRequest, personCache, + projectVersion, httpPrefix, + fromNickname, + fromDomain, postToBox) if not inboxUrl: if debug: @@ -1029,7 +1026,7 @@ def sendUnfollowRequestViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = \ - postJson(session, unfollowJson, [], inboxUrl, headers, "inbox:write") + postJson(session, unfollowJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) diff --git a/inbox.py b/inbox.py index 74ed927a4..c61b9320d 100644 --- a/inbox.py +++ b/inbox.py @@ -265,7 +265,7 @@ def inboxPermittedMessage(domain: str, messageJson: {}, if domain in actor: return True - if not urlPermitted(actor, federationList, "inbox:write"): + if not urlPermitted(actor, federationList): return False alwaysAllowedTypes = ('Follow', 'Like', 'Delete', 'Announce') @@ -278,7 +278,7 @@ def inboxPermittedMessage(domain: str, messageJson: {}, inReplyTo = messageJson['object']['inReplyTo'] if not isinstance(inReplyTo, str): return False - if not urlPermitted(inReplyTo, federationList, "inbox:write"): + if not urlPermitted(inReplyTo, federationList): return False return True @@ -652,8 +652,7 @@ def receiveUndo(session, baseDir: str, httpPrefix: str, port: int, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, messageJson: {}, federationList: [], - debug: bool, - acceptedCaps=["inbox:write", "objects:read"]) -> bool: + debug: bool) -> bool: """Receives an undo request within the POST section of HTTPServer """ if not messageJson['type'].startswith('Undo'): @@ -2024,8 +2023,7 @@ def inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, queue: [], domain: str, onionDomain: str, i2pDomain: str, port: int, proxyType: str, - federationList: [], ocapAlways: bool, debug: bool, - acceptedCaps: [], + federationList: [], debug: bool, queueFilename: str, destinationFilename: str, maxReplies: int, allowDeletion: bool, maxMentions: int, maxEmoji: int, translate: {}, @@ -2419,13 +2417,11 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, cachedWebfingers: {}, personCache: {}, queue: [], domain: str, onionDomain: str, i2pDomain: str, port: int, proxyType: str, - federationList: [], - ocapAlways: bool, maxReplies: int, + federationList: [], maxReplies: int, domainMaxPostsPerDay: int, accountMaxPostsPerDay: int, allowDeletion: bool, debug: bool, maxMentions: int, maxEmoji: int, translate: {}, unitTest: bool, - YTReplacementDomain: str, - acceptedCaps=["inbox:write", "objects:read"]) -> None: + YTReplacementDomain: str) -> None: """Processes received items and moves them to the appropriate directories """ @@ -2694,8 +2690,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, personCache, queueJson['post'], federationList, - debug, - acceptedCaps=["inbox:write", "objects:read"]): + debug): print('Queue: Undo accepted from ' + keyId) if os.path.isfile(queueFilename): os.remove(queueFilename) @@ -2712,9 +2707,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, personCache, queueJson['post'], federationList, - debug, projectVersion, - acceptedCaps=["inbox:write", - "objects:read"]): + debug, projectVersion): if os.path.isfile(queueFilename): os.remove(queueFilename) if len(queue) > 0: @@ -2810,15 +2803,6 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, pprint(recipientsDictFollowers) print('*************************************') - if queueJson['post'].get('capability'): - if not isinstance(queueJson['post']['capability'], list): - print('Queue: capability on post should be a list') - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - continue - # Copy any posts addressed to followers into the shared inbox # this avoid copying file multiple times to potentially many # individual inboxes @@ -2843,8 +2827,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, domain, onionDomain, i2pDomain, port, proxyType, - federationList, ocapAlways, - debug, acceptedCaps, + federationList, + debug, queueFilename, destination, maxReplies, allowDeletion, maxMentions, maxEmoji, diff --git a/like.py b/like.py index d63709616..4a16d0fe3 100644 --- a/like.py +++ b/like.py @@ -63,7 +63,7 @@ def like(recentPostsCache: {}, 'to' might be a specific person (actor) whose post was liked object is typically the url of the message which was liked """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None fullDomain = domain @@ -162,7 +162,7 @@ def undolike(recentPostsCache: {}, 'to' might be a specific person (actor) whose post was liked object is typically the url of the message which was liked """ - if not urlPermitted(objectUrl, federationList, "inbox:write"): + if not urlPermitted(objectUrl, federationList): return None fullDomain = domain @@ -267,8 +267,7 @@ def sendLikeViaServer(baseDir: str, session, postToBox = 'outbox' # get the actor inbox for the To handle - (inboxUrl, pubKeyId, pubKey, fromPersonId, - sharedInbox, capabilityAcquisition, + (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -291,8 +290,7 @@ def sendLikeViaServer(baseDir: str, session, 'Content-type': 'application/json', 'Authorization': authHeader } - postResult = postJson(session, newLikeJson, [], inboxUrl, - headers, "inbox:write") + postResult = postJson(session, newLikeJson, [], inboxUrl, headers) if not postResult: print('WARN: POST announce failed for c2s to ' + inboxUrl) return 5 @@ -352,8 +350,7 @@ def sendUndoLikeViaServer(baseDir: str, session, postToBox = 'outbox' # get the actor inbox for the To handle - (inboxUrl, pubKeyId, pubKey, fromPersonId, - sharedInbox, capabilityAcquisition, + (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, @@ -375,8 +372,7 @@ def sendUndoLikeViaServer(baseDir: str, session, 'Content-type': 'application/json', 'Authorization': authHeader } - postResult = postJson(session, newUndoLikeJson, [], inboxUrl, - headers, "inbox:write") + postResult = postJson(session, newUndoLikeJson, [], inboxUrl, headers) if not postResult: print('WARN: POST announce failed for c2s to ' + inboxUrl) return 5 diff --git a/manualapprove.py b/manualapprove.py index ea3740f25..b4252bc6a 100644 --- a/manualapprove.py +++ b/manualapprove.py @@ -85,7 +85,6 @@ def manualApproveFollowRequest(session, baseDir: str, federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, - acceptedCaps: [], debug: bool, projectVersion: str) -> None: """Manually approve a follow request @@ -142,7 +141,7 @@ def manualApproveFollowRequest(session, baseDir: str, approvePort, followJson['actor'], federationList, - followJson, acceptedCaps, + followJson, sendThreads, postLog, cachedWebfingers, personCache, debug, projectVersion, False) diff --git a/person.py b/person.py index cbeca6652..2f63341da 100644 --- a/person.py +++ b/person.py @@ -326,8 +326,6 @@ def createPersonBase(baseDir: str, nickname: str, domain: str, port: int, if not os.path.isdir(baseDir + peopleSubdir + '/' + handle + '/outbox'): os.mkdir(baseDir + peopleSubdir + '/' + handle + '/outbox') - if not os.path.isdir(baseDir + peopleSubdir + '/' + handle + '/ocap'): - os.mkdir(baseDir + peopleSubdir + '/' + handle + '/ocap') if not os.path.isdir(baseDir + peopleSubdir + '/' + handle + '/queue'): os.mkdir(baseDir + peopleSubdir + '/' + handle + '/queue') filename = baseDir + peopleSubdir + '/' + handle + '.json' @@ -588,7 +586,7 @@ def personLookup(domain: str, path: str, baseDir: str) -> {}: def personBoxJson(recentPostsCache: {}, session, baseDir: str, domain: str, port: int, path: str, httpPrefix: str, noOfItems: int, boxname: str, - authorized: bool, ocapAlways: bool) -> {}: + authorized: bool) -> {}: """Obtain the inbox/outbox/moderation feed for the given person """ if boxname != 'inbox' and boxname != 'dm' and \ @@ -634,38 +632,36 @@ def personBoxJson(recentPostsCache: {}, return createInbox(recentPostsCache, session, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, pageNumber) + noOfItems, headerOnly, pageNumber) elif boxname == 'dm': return createDMTimeline(recentPostsCache, session, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, pageNumber) + noOfItems, headerOnly, pageNumber) elif boxname == 'tlbookmarks' or boxname == 'bookmarks': return createBookmarksTimeline(session, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, + noOfItems, headerOnly, pageNumber) elif boxname == 'tlevents': return createEventsTimeline(recentPostsCache, session, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, + noOfItems, headerOnly, pageNumber) elif boxname == 'tlreplies': return createRepliesTimeline(recentPostsCache, session, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, + noOfItems, headerOnly, pageNumber) elif boxname == 'tlmedia': return createMediaTimeline(session, baseDir, nickname, domain, port, - httpPrefix, - noOfItems, headerOnly, ocapAlways, + httpPrefix, noOfItems, headerOnly, pageNumber) elif boxname == 'tlblogs': return createBlogsTimeline(session, baseDir, nickname, domain, port, - httpPrefix, - noOfItems, headerOnly, ocapAlways, + httpPrefix, noOfItems, headerOnly, pageNumber) elif boxname == 'outbox': return createOutbox(session, baseDir, nickname, domain, port, @@ -682,7 +678,7 @@ def personBoxJson(recentPostsCache: {}, def personInboxJson(recentPostsCache: {}, baseDir: str, domain: str, port: int, path: str, - httpPrefix: str, noOfItems: int, ocapAlways: bool) -> []: + httpPrefix: str, noOfItems: int) -> []: """Obtain the inbox feed for the given person Authentication is expected to have already happened """ @@ -719,7 +715,7 @@ def personInboxJson(recentPostsCache: {}, return None return createInbox(recentPostsCache, baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, ocapAlways, pageNumber) + noOfItems, headerOnly, pageNumber) def setDisplayNickname(baseDir: str, nickname: str, domain: str, diff --git a/posts.py b/posts.py index 37d806d50..0022ce332 100644 --- a/posts.py +++ b/posts.py @@ -208,7 +208,7 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, else: personUrl = httpPrefix + '://' + domain + '/users/' + nickname if not personUrl: - return None, None, None, None, None, None, None, None + return None, None, None, None, None, None, None personJson = \ getPersonFromCache(baseDir, personUrl, personCache, True) if not personJson: @@ -226,7 +226,7 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, projectVersion, httpPrefix, domain) if not personJson: print('Unable to get actor') - return None, None, None, None, None, None, None, None + return None, None, None, None, None, None, None boxJson = None if not personJson.get(boxName): if personJson.get('endpoints'): @@ -236,7 +236,7 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, boxJson = personJson[boxName] if not boxJson: - return None, None, None, None, None, None, None, None + return None, None, None, None, None, None, None personId = None if personJson.get('id'): @@ -255,9 +255,6 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, if personJson.get('endpoints'): if personJson['endpoints'].get('sharedInbox'): sharedInbox = personJson['endpoints']['sharedInbox'] - capabilityAcquisition = None - if personJson.get('capabilityAcquisitionEndpoint'): - capabilityAcquisition = personJson['capabilityAcquisitionEndpoint'] avatarUrl = None if personJson.get('icon'): if personJson['icon'].get('url'): @@ -269,7 +266,7 @@ def getPersonBox(baseDir: str, session, wfRequest: {}, storePersonInCache(baseDir, personUrl, personJson, personCache, True) return boxJson, pubKeyId, pubKey, personId, sharedInbox, \ - capabilityAcquisition, avatarUrl, displayName + avatarUrl, displayName def getPosts(session, outboxUrl: str, maxPosts: int, @@ -1061,11 +1058,9 @@ def outboxMessageCreateWrap(httpPrefix: str, cc = [] if messageJson.get('cc'): cc = messageJson['cc'] - capabilityUrl = [] newPost = { "@context": "https://www.w3.org/ns/activitystreams", 'id': newPostId + '/activity', - 'capability': capabilityUrl, 'type': 'Create', 'actor': httpPrefix + '://' + domain + '/users/' + nickname, 'published': published, @@ -1569,7 +1564,7 @@ def threadSendPost(session, postJsonStr: str, federationList: [], postResult, unauthorized = \ postJsonString(session, postJsonStr, federationList, inboxUrl, signatureHeaderJson, - "inbox:write", debug) + debug) except Exception as e: print('ERROR: postJsonString failed ' + str(e)) if unauthorized: @@ -1654,7 +1649,6 @@ def sendPost(projectVersion: str, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, toPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -1772,7 +1766,6 @@ def sendPostViaServer(projectVersion: str, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -1838,7 +1831,7 @@ def sendPostViaServer(projectVersion: str, } postResult = \ postImage(session, attachImageFilename, [], - inboxUrl, headers, "inbox:write") + inboxUrl, headers) if not postResult: if debug: print('DEBUG: Failed to upload image') @@ -1851,7 +1844,7 @@ def sendPostViaServer(projectVersion: str, } postResult = \ postJsonString(session, json.dumps(postJsonObject), [], - inboxUrl, headers, "inbox:write", debug) + inboxUrl, headers, debug) if not postResult: if debug: print('DEBUG: POST failed for c2s to '+inboxUrl) @@ -1983,8 +1976,7 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, postToBox = 'outbox' # get the actor inbox/outbox for the To handle - (inboxUrl, pubKeyId, pubKey, toPersonId, sharedInboxUrl, - capabilityAcquisition, avatarUrl, + (inboxUrl, pubKeyId, pubKey, toPersonId, sharedInboxUrl, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -2447,75 +2439,69 @@ def sendToFollowersThread(session, baseDir: str, def createInbox(recentPostsCache: {}, session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, - ocapAlways: bool, pageNumber=None) -> {}: + pageNumber=None) -> {}: return createBoxIndexed(recentPostsCache, session, baseDir, 'inbox', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, - ocapAlways, pageNumber) + pageNumber) def createBookmarksTimeline(session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed({}, session, baseDir, 'tlbookmarks', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, - True, ocapAlways, pageNumber) + True, pageNumber) def createEventsTimeline(recentPostsCache: {}, session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed(recentPostsCache, session, baseDir, 'tlevents', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, - True, ocapAlways, pageNumber) + True, pageNumber) def createDMTimeline(recentPostsCache: {}, session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed(recentPostsCache, session, baseDir, 'dm', nickname, domain, port, httpPrefix, itemsPerPage, - headerOnly, True, ocapAlways, pageNumber) + headerOnly, True, pageNumber) def createRepliesTimeline(recentPostsCache: {}, session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed(recentPostsCache, session, baseDir, 'tlreplies', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, - ocapAlways, pageNumber) + pageNumber) def createBlogsTimeline(session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed({}, session, baseDir, 'tlblogs', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, - ocapAlways, pageNumber) + pageNumber) def createMediaTimeline(session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, - headerOnly: bool, ocapAlways: bool, - pageNumber=None) -> {}: + headerOnly: bool, pageNumber=None) -> {}: return createBoxIndexed({}, session, baseDir, 'tlmedia', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, True, - ocapAlways, pageNumber) + pageNumber) def createOutbox(session, baseDir: str, nickname: str, domain: str, @@ -2530,7 +2516,7 @@ def createOutbox(session, baseDir: str, nickname: str, domain: str, def createModeration(baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, - ocapAlways: bool, pageNumber=None) -> {}: + pageNumber=None) -> {}: boxDir = createPersonDir(nickname, domain, baseDir, 'inbox') boxname = 'moderation' @@ -2728,8 +2714,7 @@ def createBoxIndex(boxDir: str, postsInBoxDict: {}) -> int: def createSharedInboxIndex(baseDir: str, sharedBoxDir: str, postsInBoxDict: {}, postsCtr: int, - nickname: str, domain: str, - ocapAlways: bool) -> int: + nickname: str, domain: str) -> int: """ Creates an index for the given shared inbox """ handle = nickname + '@' + domain @@ -2819,7 +2804,7 @@ def createBoxIndexed(recentPostsCache: {}, session, baseDir: str, boxname: str, nickname: str, domain: str, port: int, httpPrefix: str, itemsPerPage: int, headerOnly: bool, authorized: bool, - ocapAlways: bool, pageNumber=None) -> {}: + pageNumber=None) -> {}: """Constructs the box feed for a person with the given nickname """ if not authorized or not pageNumber: @@ -2958,10 +2943,6 @@ def createBoxIndexed(recentPostsCache: {}, except BaseException: continue - # remove any capability so that it's not displayed - if p.get('capability'): - del p['capability'] - # Don't show likes, replies or shares (announces) to # unauthorized viewers if not authorized: @@ -3179,7 +3160,6 @@ def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str, (personUrl, pubKeyId, pubKey, personId, shaedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -3225,7 +3205,6 @@ def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str, (personUrl, pubKeyId, pubKey, personId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -3728,8 +3707,7 @@ def sendBlockViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, @@ -3751,8 +3729,7 @@ def sendBlockViaServer(baseDir: str, session, 'Content-type': 'application/json', 'Authorization': authHeader } - postResult = postJson(session, newBlockJson, [], inboxUrl, - headers, "inbox:write") + postResult = postJson(session, newBlockJson, [], inboxUrl, headers) if not postResult: print('WARN: Unable to post block') @@ -3817,8 +3794,7 @@ def sendUndoBlockViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, - fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, + fromPersonId, sharedInbox, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, fromDomain, postToBox) @@ -3839,8 +3815,7 @@ def sendUndoBlockViaServer(baseDir: str, session, 'Content-type': 'application/json', 'Authorization': authHeader } - postResult = postJson(session, newBlockJson, [], inboxUrl, - headers, "inbox:write") + postResult = postJson(session, newBlockJson, [], inboxUrl, headers) if not postResult: print('WARN: Unable to post block') diff --git a/roles.py b/roles.py index f2bc57374..9cfa1cc7a 100644 --- a/roles.py +++ b/roles.py @@ -291,7 +291,6 @@ def sendRoleViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, @@ -315,7 +314,7 @@ def sendRoleViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = \ - postJson(session, newRoleJson, [], inboxUrl, headers, "inbox:write") + postJson(session, newRoleJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to '+inboxUrl) diff --git a/session.py b/session.py index 9e3a14ec5..81a5c423f 100644 --- a/session.py +++ b/session.py @@ -93,16 +93,13 @@ def getJson(session, url: str, headers: {}, params: {}, def postJson(session, postJsonObject: {}, federationList: [], - inboxUrl: str, headers: {}, capability: str) -> str: + inboxUrl: str, headers: {}) -> str: """Post a json message to the inbox of another person - Supplying a capability, such as "inbox:write" """ - # always allow capability requests - if not capability.startswith('cap'): - # check that we are posting to a permitted domain - if not urlPermitted(inboxUrl, federationList, capability): - print('postJson: ' + inboxUrl + ' not permitted') - return None + # check that we are posting to a permitted domain + if not urlPermitted(inboxUrl, federationList): + print('postJson: ' + inboxUrl + ' not permitted') + return None try: postResult = \ @@ -132,10 +129,8 @@ def postJsonString(session, postJsonStr: str, federationList: [], inboxUrl: str, headers: {}, - capability: str, debug: bool) -> (bool, bool): """Post a json message string to the inbox of another person - Supplying a capability, such as "inbox:write" The second boolean returned is true if the send is unauthorized NOTE: Here we post a string rather than the original json so that conversions between string and json format don't invalidate @@ -174,16 +169,13 @@ def postJsonString(session, postJsonStr: str, def postImage(session, attachImageFilename: str, federationList: [], - inboxUrl: str, headers: {}, capability: str) -> str: + inboxUrl: str, headers: {}) -> str: """Post an image to the inbox of another person or outbox via c2s - Supplying a capability, such as "inbox:write" """ - # always allow capability requests - if not capability.startswith('cap'): - # check that we are posting to a permitted domain - if not urlPermitted(inboxUrl, federationList, capability): - print('postJson: ' + inboxUrl + ' not permitted') - return None + # check that we are posting to a permitted domain + if not urlPermitted(inboxUrl, federationList): + print('postJson: ' + inboxUrl + ' not permitted') + return None if not (attachImageFilename.endswith('.jpg') or attachImageFilename.endswith('.jpeg') or diff --git a/shares.py b/shares.py index be5ca8c32..48b01fba2 100644 --- a/shares.py +++ b/shares.py @@ -380,7 +380,6 @@ def sendShareViaServer(baseDir, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, @@ -405,7 +404,7 @@ def sendShareViaServer(baseDir, session, postResult = \ postImage(session, imageFilename, [], inboxUrl.replace('/' + postToBox, '/shares'), - headers, "inbox:write") + headers) headers = { 'host': fromDomain, @@ -413,7 +412,7 @@ def sendShareViaServer(baseDir, session, 'Authorization': authHeader } postResult = \ - postJson(session, newShareJson, [], inboxUrl, headers, "inbox:write") + postJson(session, newShareJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) @@ -483,7 +482,6 @@ def sendUndoShareViaServer(baseDir: str, session, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, fromNickname, @@ -506,7 +504,7 @@ def sendUndoShareViaServer(baseDir: str, session, 'Authorization': authHeader } postResult = \ - postJson(session, undoShareJson, [], inboxUrl, headers, "inbox:write") + postJson(session, undoShareJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) diff --git a/skills.py b/skills.py index c50931a78..175e6ab03 100644 --- a/skills.py +++ b/skills.py @@ -152,7 +152,6 @@ def sendSkillViaServer(baseDir: str, session, nickname: str, password: str, # get the actor inbox for the To handle (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, nickname, domain, @@ -175,7 +174,7 @@ def sendSkillViaServer(baseDir: str, session, nickname: str, password: str, 'Authorization': authHeader } postResult = \ - postJson(session, newSkillJson, [], inboxUrl, headers, "inbox:write") + postJson(session, newSkillJson, [], inboxUrl, headers) if not postResult: if debug: print('DEBUG: POST announce failed for c2s to ' + inboxUrl) diff --git a/socnet.py b/socnet.py index fca13dc26..6873d388a 100644 --- a/socnet.py +++ b/socnet.py @@ -65,7 +65,6 @@ def instancesGraph(baseDir: str, handles: str, (personUrl, pubKeyId, pubKey, personId, shaedInbox, - capabilityAcquisition, avatarUrl, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, diff --git a/tests.py b/tests.py index 7996c3ba9..c36403504 100644 --- a/tests.py +++ b/tests.py @@ -239,7 +239,7 @@ def testThreads(): def createServerAlice(path: str, domain: str, port: int, bobAddress: str, federationList: [], hasFollows: bool, hasPosts: bool, - ocapAlways: bool, sendThreads: []): + sendThreads: []): print('Creating test server: Alice on port ' + str(port)) if os.path.isdir(path): shutil.rmtree(path) @@ -296,7 +296,7 @@ def createServerAlice(path: str, domain: str, port: int, "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, ocapAlways, + noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, domainMaxPostsPerDay, accountMaxPostsPerDay, allowDeletion, True, True, False, sendThreads, False, @@ -306,7 +306,7 @@ def createServerAlice(path: str, domain: str, port: int, def createServerBob(path: str, domain: str, port: int, aliceAddress: str, federationList: [], hasFollows: bool, hasPosts: bool, - ocapAlways: bool, sendThreads: []): + sendThreads: []): print('Creating test server: Bob on port ' + str(port)) if os.path.isdir(path): shutil.rmtree(path) @@ -364,7 +364,7 @@ def createServerBob(path: str, domain: str, port: int, "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, ocapAlways, + noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, domainMaxPostsPerDay, accountMaxPostsPerDay, allowDeletion, True, True, False, sendThreads, False, @@ -373,7 +373,7 @@ def createServerBob(path: str, domain: str, port: int, def createServerEve(path: str, domain: str, port: int, federationList: [], hasFollows: bool, hasPosts: bool, - ocapAlways: bool, sendThreads: []): + sendThreads: []): print('Creating test server: Eve on port ' + str(port)) if os.path.isdir(path): shutil.rmtree(path) @@ -406,7 +406,7 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, ocapAlways, + noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, allowDeletion, True, True, False, sendThreads, False, False) @@ -427,8 +427,6 @@ def testPostMessageBetweenServers(): shutil.rmtree(baseDir + '/.tests') os.mkdir(baseDir + '/.tests') - ocapAlways = False - # create the servers aliceDir = baseDir + '/.tests/alice' aliceDomain = '127.0.0.50' @@ -454,7 +452,7 @@ def testPostMessageBetweenServers(): threadWithTrace(target=createServerAlice, args=(aliceDir, aliceDomain, alicePort, bobAddress, federationList, False, False, - ocapAlways, aliceSendThreads), + aliceSendThreads), daemon=True) global thrBob @@ -468,7 +466,7 @@ def testPostMessageBetweenServers(): threadWithTrace(target=createServerBob, args=(bobDir, bobDomain, bobPort, aliceAddress, federationList, False, False, - ocapAlways, bobSendThreads), + bobSendThreads), daemon=True) thrAlice.start() @@ -687,8 +685,6 @@ def testFollowBetweenServers(): shutil.rmtree(baseDir + '/.tests') os.mkdir(baseDir + '/.tests') - ocapAlways = False - # create the servers aliceDir = baseDir + '/.tests/alice' aliceDomain = '127.0.0.47' @@ -713,7 +709,7 @@ def testFollowBetweenServers(): threadWithTrace(target=createServerAlice, args=(aliceDir, aliceDomain, alicePort, bobAddress, federationList, False, False, - ocapAlways, aliceSendThreads), + aliceSendThreads), daemon=True) global thrBob @@ -727,7 +723,7 @@ def testFollowBetweenServers(): threadWithTrace(target=createServerBob, args=(bobDir, bobDomain, bobPort, aliceAddress, federationList, False, False, - ocapAlways, bobSendThreads), + bobSendThreads), daemon=True) thrAlice.start() @@ -1246,8 +1242,6 @@ def testClientToServer(): shutil.rmtree(baseDir + '/.tests') os.mkdir(baseDir + '/.tests') - ocapAlways = False - # create the servers aliceDir = baseDir + '/.tests/alice' aliceDomain = '127.0.0.42' @@ -1272,7 +1266,7 @@ def testClientToServer(): threadWithTrace(target=createServerAlice, args=(aliceDir, aliceDomain, alicePort, bobAddress, federationList, False, False, - ocapAlways, aliceSendThreads), + aliceSendThreads), daemon=True) global thrBob @@ -1286,7 +1280,7 @@ def testClientToServer(): threadWithTrace(target=createServerBob, args=(bobDir, bobDomain, bobPort, aliceAddress, federationList, False, False, - ocapAlways, bobSendThreads), + bobSendThreads), daemon=True) thrAlice.start() diff --git a/utils.py b/utils.py index 445ffc5ff..ce3f7012f 100644 --- a/utils.py +++ b/utils.py @@ -242,7 +242,7 @@ def domainPermitted(domain: str, federationList: []): return False -def urlPermitted(url: str, federationList: [], capability: str): +def urlPermitted(url: str, federationList: []): if isEvil(url): return False if not federationList: diff --git a/webinterface.py b/webinterface.py index b814b8230..6a07bd8d8 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2650,7 +2650,7 @@ def htmlFooter() -> str: def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, translate: {}, baseDir: str, httpPrefix: str, - authorized: bool, ocapAlways: bool, + authorized: bool, nickname: str, domain: str, port: int, session, wfRequest: {}, personCache: {}, projectVersion: str, @@ -2671,8 +2671,7 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, str(currPage), httpPrefix, 10, 'outbox', - authorized, - ocapAlways) + authorized) if not outboxFeed: break if len(outboxFeed['orderedItems']) == 0: @@ -2700,7 +2699,7 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str, - authorized: bool, ocapAlways: bool, + authorized: bool, nickname: str, domain: str, port: int, session, wfRequest: {}, personCache: {}, followingJson: {}, projectVersion: str, @@ -2930,7 +2929,7 @@ def htmlProfile(defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, projectVersion: str, baseDir: str, httpPrefix: str, authorized: bool, - ocapAlways: bool, profileJson: {}, selected: str, + profileJson: {}, selected: str, session, wfRequest: {}, personCache: {}, YTReplacementDomain: str, extraJson=None, @@ -3190,14 +3189,14 @@ def htmlProfile(defaultTimeline: str, htmlProfilePosts(recentPostsCache, maxRecentPosts, translate, baseDir, httpPrefix, authorized, - ocapAlways, nickname, domain, port, + nickname, domain, port, session, wfRequest, personCache, projectVersion, YTReplacementDomain) + licenseStr if selected == 'following': profileStr += \ htmlProfileFollowing(translate, baseDir, httpPrefix, - authorized, ocapAlways, nickname, + authorized, nickname, domain, port, session, wfRequest, personCache, extraJson, projectVersion, ["unfollow"], selected, @@ -3205,7 +3204,7 @@ def htmlProfile(defaultTimeline: str, if selected == 'followers': profileStr += \ htmlProfileFollowing(translate, baseDir, httpPrefix, - authorized, ocapAlways, nickname, + authorized, nickname, domain, port, session, wfRequest, personCache, extraJson, projectVersion, ["block"], @@ -3247,7 +3246,6 @@ def individualFollowAsHtml(translate: {}, if domain not in followUrl: (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl2, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, nickname, @@ -4101,7 +4099,6 @@ def individualPostAsHtml(allowDownloads: bool, if fullDomain not in postActor: (inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, - capabilityAcquisition, avatarUrl2, displayName) = getPersonBox(baseDir, session, wfRequest, personCache, projectVersion, httpPrefix, From 3b1e67f87f0042c4c542f3c14342d449387003d0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 27 Sep 2020 20:32:31 +0100 Subject: [PATCH 36/63] Remove capabilities options --- daemon.py | 2 -- epicyon.py | 2 -- tests.py | 18 ------------------ 3 files changed, 22 deletions(-) diff --git a/daemon.py b/daemon.py index f72ee5228..1f5d0d38a 100644 --- a/daemon.py +++ b/daemon.py @@ -10603,8 +10603,6 @@ def runDaemon(blogsInstance: bool, mediaInstance: bool, port=80, proxyPort=80, httpPrefix='https', fedList=[], maxMentions=10, maxEmoji=10, authenticatedFetch=False, - noreply=False, nolike=False, nopics=False, - noannounce=False, cw=False, proxyType=None, maxReplies=64, domainMaxPostsPerDay=8640, accountMaxPostsPerDay=864, allowDeletion=False, debug=False, unitTest=False, diff --git a/epicyon.py b/epicyon.py index 906872407..996200808 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1895,8 +1895,6 @@ if __name__ == "__main__": port, proxyPort, httpPrefix, federationList, args.maxMentions, args.maxEmoji, args.authenticatedFetch, - False, False, False, - False, False, proxyType, args.maxReplies, args.domainMaxPostsPerDay, args.accountMaxPostsPerDay, diff --git a/tests.py b/tests.py index c36403504..fa0de6165 100644 --- a/tests.py +++ b/tests.py @@ -249,11 +249,6 @@ def createServerAlice(path: str, domain: str, port: int, httpPrefix = 'http' proxyType = None password = 'alicepass' - noreply = False - nolike = False - nopics = False - noannounce = False - cw = False useBlurhash = True maxReplies = 64 domainMaxPostsPerDay = 1000 @@ -296,7 +291,6 @@ def createServerAlice(path: str, domain: str, port: int, "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, domainMaxPostsPerDay, accountMaxPostsPerDay, allowDeletion, True, True, False, sendThreads, False, @@ -317,11 +311,6 @@ def createServerBob(path: str, domain: str, port: int, proxyType = None clientToServer = False password = 'bobpass' - noreply = False - nolike = False - nopics = False - noannounce = False - cw = False useBlurhash = False maxReplies = 64 domainMaxPostsPerDay = 1000 @@ -364,7 +353,6 @@ def createServerBob(path: str, domain: str, port: int, "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, domainMaxPostsPerDay, accountMaxPostsPerDay, allowDeletion, True, True, False, sendThreads, False, @@ -383,11 +371,6 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], httpPrefix = 'http' proxyType = None password = 'evepass' - noreply = False - nolike = False - nopics = False - noannounce = False - cw = False maxReplies = 64 allowDeletion = True privateKeyPem, publicKeyPem, person, wfEndpoint = \ @@ -406,7 +389,6 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], "instanceId", False, path, domain, onionDomain, i2pDomain, None, port, port, httpPrefix, federationList, maxMentions, maxEmoji, False, - noreply, nolike, nopics, noannounce, cw, proxyType, maxReplies, allowDeletion, True, True, False, sendThreads, False, False) From 74b78a01f90bb229f1f1194d52702ff175a09c3b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 27 Sep 2020 20:42:44 +0100 Subject: [PATCH 37/63] Remove capabilities from documentation --- README.md | 2 +- README_goals.md | 1 - ocaps.md | 180 ------------------------------------------------ 3 files changed, 1 insertion(+), 182 deletions(-) delete mode 100644 ocaps.md diff --git a/README.md b/README.md index be08f1a32..a010c11e5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Epicyon is a modern [ActivityPub](https://www.w3.org/TR/activitypub) compliant server implementing both S2S and C2S protocols and sutable for installation on single board computers. It includes features such as moderation tools, post expiry, content warnings, image descriptions and perimeter defense against adversaries. It contains *no javascript* and uses HTML+CSS with a Python backend. -[Project Goals](README_goals.md) - [Commandline interface](README_commandline.md) - [Customizations](README_customizations.md) - [Object Capabilities](ocaps.md) - [Code of Conduct](code-of-conduct.md) +[Project Goals](README_goals.md) - [Commandline interface](README_commandline.md) - [Customizations](README_customizations.md) - [Code of Conduct](code-of-conduct.md) Matrix room: **#epicyon:matrix.freedombone.net** diff --git a/README_goals.md b/README_goals.md index d57ba8249..97f8837fd 100644 --- a/README_goals.md +++ b/README_goals.md @@ -15,7 +15,6 @@ * http signatures and basic auth * Compatible with http (onion addresses), https and dat * Minimal dependencies. - * Capabilities based security * Support image blurhashes * Data minimization principle. Configurable post expiry time * Likes and repeats only visible to authorized viewers diff --git a/ocaps.md b/ocaps.md deleted file mode 100644 index 12e422cb4..000000000 --- a/ocaps.md +++ /dev/null @@ -1,180 +0,0 @@ -# Object Capabilities Prototype - -This is one proposed way that OCAP could work. - -## TL;DR - - * Works from person to person, not instance to instance. Actor-oriented capabilities. - * Produces negligible additional network traffic, although see the proviso for shared inbox - * Doesn't require any additional encryption to be performed - * Works in the same way between people on different instances or the same instance - * People can alter what their followers can do on an individual basis - * Leverages the existing follow request mechanism - -## Workflow - -Default capabilities are initially set up when a follow request is made. The Accept activity sent back from a follow request can be received by any instance. A capabilities accept activity is attached to the follow accept. - -``` text - Alice - | - V - Follow Request - | - V - Bob - | - V - Create/store default Capabilities - for Alice - | - V - Follow Accept + default Capabilities - | - V - Alice - | - V - Store Granted Capabilities -``` - -The default capabilities could be *any preferred policy* of the instance. They could be no capabilities at all, read only or full access to everything. - -Example Follow request from **Alice** to **Bob**: - -``` json -{'actor': 'http://alicedomain.net/users/alice', - 'cc': ['https://www.w3.org/ns/activitystreams#Public'], - 'id': 'http://alicedomain.net/users/alice/statuses/1562507338839876', - 'object': 'http://bobdomain.net/users/bob', - 'published': '2019-07-07T13:48:58Z', - 'to': ['http://bobdomain.net/users/bob'], - 'type': 'Follow'} - ``` - -Follow Accept from **Bob** to **Alice** with attached capabilities. - -``` json -{'actor': 'http://bobdomain.net/users/bob', - 'capabilities': {'actor': 'http://bobdomain.net/users/bob', - 'capability': ['inbox:write', 'objects:read'], - 'id': 'http://bobdomain.net/caps/alice@alicedomain.net#rOYtHApyr4ZWDUgEE1KqjhTe0kI3T2wJ', - 'scope': 'http://alicedomain.net/users/alice', - 'type': 'Capability'}, - 'cc': [], - 'object': {'actor': 'http://alicedomain.net/users/alice', - 'cc': ['https://www.w3.org/ns/activitystreams#Public'], - 'id': 'http://alicedomain.net/users/alice/statuses/1562507338839876', - 'object': 'http://bobdomain.net/users/bob', - 'published': '2019-07-07T13:48:58Z', - 'to': ['http://bobdomain.net/users/bob'], - 'type': 'Follow'}, - 'to': ['http://alicedomain.net/users/alice'], - 'type': 'Accept'} -``` - -When posts are subsequently sent from the following instance (server-to-server) they should have the corresponding capability id string attached within the Create wrapper. To handle the *shared inbox* scenario this should be a list rather than a single string. In the above example that would be *['http://bobdomain.net/caps/alice@alicedomain.net#rOYtHApyr4ZWDUgEE1KqjhTe0kI3T2wJ']*. It should contain a random token which is hard to guess by brute force methods. - -NOTE: the token should be random and not a hash of anything. Making it a hash would give an adversary a much better chance of calculating it. - -``` text - Alice - | - V - Send Post - Attach id from Stored Capabilities - granted by Bob - | - V - Bob - | - V - http signature check - | - V - Check Capability id matches - stored capabilities - | - V - Match stored capability scope - against actor on received post - | - V - Check that stored capability - contains inbox:write, etc - | - V - Any other checks - | - V - Accept incoming post -``` - -Subsequently **Bob** could change the stored capabilities for **Alice** in their database, giving the new object a different id. This could be sent back to **Alice** as an **Update** activity with attached capability. - -Bob can send this to Alice, altering *capability* to now include *inbox:noreply*. Notice that the random token at the end of the *id* has changed, so that Alice can't continue to use the old capabilities. - -``` json -{'actor': 'http://bobdomain.net/users/bob', - 'cc': [], - 'object': {'actor': 'http://bobdomain.net/users/bob', - 'capability': ['inbox:write', 'objects:read', 'inbox:noreply'], - 'id': 'http://bobdomain.net/caps/alice@alicedomain.net#53nwZhHipNFCNwrJ2sgE8GPx13SnV23X', - 'scope': 'http://alicedomain.net/users/alice', - 'type': 'Capability'}, - 'to': ['http://alicedomain.net/users/alice'], - 'type': 'Update'} -``` - -Alice then receives this and updates her capabilities granted by Bob to: - -``` json -{'actor': 'http://bobdomain.net/users/bob', - 'capability': ['inbox:write', 'objects:read', 'inbox:noreply'], - 'id': 'http://bobdomain.net/caps/alice@alicedomain.net#53nwZhHipNFCNwrJ2sgE8GPx13SnV23X', - 'scope': 'http://alicedomain.net/users/alice', - 'type': 'Capability'} -``` - -If she sets her system to somehow ignore the update then if capabilities are strictly enforced she will no longer be able to send messages to Bob's inbox. - -Object capabilities can be strictly enforced by adding the **--ocap** option when running the server. The only activities which it is not enforced upon are **Follow** and **Accept**. Anyone can create a follow request or accept updated capabilities. - -## Object capabilities in the shared inbox scenario - -Shared inboxes are obviously essential for any kind of scalability, otherwise there would be vast amounts of duplicated messages being dumped onto the intertubes like a big truck. - -With the shared inbox instead of sending from Alice to 500 of her fans on a different instance - repeatedly sending the same message to individual inboxes - a single message is sent to its shared inbox (which has its own special account called 'inbox') and it then decides how to distribute that. If a list of capability ids is attached to the message which gets sent to the shared inbox then the receiving server can use that. - -When a post arrives in the shared inbox it is checked to see that at least one follower exists for it. If there are only a small number of followers then it is treated like a direct message and copied separately to individual account inboxes after capabilities checks. For larger numbers of followers the capabilities checks are done at the time when the inbox is fetched. This avoids a lot of duplicated storage of posts. - -A potential down side is that for popular accounts with many followers the number of capabilities ids (one for each follower on the receiving server) on a post sent to the shared inbox could be large. However, in terms of bandwidth it may still not be very significant compared to heavyweight websites containing a lot of javascript. - -## Some capabilities - -*inbox:write* - follower can post anything to your inbox - -*inbox:noreply* - follower can't reply to your posts - -*inbox:nolike* - follower can't like your posts - -*inbox:nopics* - follower can't post image links - -*inbox:noannounce* - follower can't send repeats (announce activities) to your inbox - -*inbox:cw* - follower can't post to your inbox unless they include a content warning - -## Object capabilities adversaries - -If **Eve** subsequently learns what the capabilities id is for **Alice** by somehow intercepting the traffic (eg. suppose she works for *Eveflare*) then she can't gain the capabilities of Alice due to the *scope* parameter against which the actors of incoming posts are checked. - -**Eve** could create a post pretending to be from Alice's domain, but the http signature check would fail due to her not having Alice's keys. - -The only scenarios in which Eve might triumph would be if she could also do DNS highjacking and: - - * Bob isn't storing Alice's public key and looks it up repeatedly - * Alice and Bob's instances are foolishly configured to perform *blind key rotation* such that her being in the middle is indistinguishable from expected key changes - -Even if Eve has an account on Alice's instance this won't help her very much unless she can get write access to the database. - -Another scenario is that you grant capabilities to an account on a hostile instance. The hostile instance then shares the resulting token with all other accounts on it. Potentially those other accounts might be able to gain capabilities which they havn't been granted *but only if they also have identical signing keys*. Checking for public key duplication on the instance granting capabilities could mitigate this. At the point at which a capabilities request is made are there any other known accounts with the same public key? Since actors are public it would also be possible to automatically scan for the existence of instances with duplicated signing keys. From 526ccad163118278d43f6a9171891fa67fb2e5ce Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 10:55:28 +0100 Subject: [PATCH 38/63] Show a sending screen when post is sent --- daemon.py | 26 ++++++++++++--- epicyon-send.css | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ theme.py | 2 +- webinterface.py | 21 +++++++++++- 4 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 epicyon-send.css diff --git a/daemon.py b/daemon.py index 1f5d0d38a..6ef14f9a7 100644 --- a/daemon.py +++ b/daemon.py @@ -152,6 +152,7 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked +from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare @@ -921,6 +922,7 @@ class PubServer(BaseHTTPRequestHandler): if postToNickname: print('Posting to nickname ' + postToNickname) self.postToNickname = postToNickname + return postMessageToOutbox(messageJson, self.postToNickname, self.server, self.server.baseDir, self.server.httpPrefix, @@ -4048,7 +4050,8 @@ class PubServer(BaseHTTPRequestHandler): if '?page=' in hashtag: hashtag = hashtag.split('?page=')[0] if isBlockedHashtag(baseDir, hashtag): - msg = htmlHashtagBlocked(baseDir).encode('utf-8') + msg = htmlHashtagBlocked(baseDir, + self.server.translate).encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) self.server.GETbusy = False @@ -9114,7 +9117,8 @@ class PubServer(BaseHTTPRequestHandler): etag, callingDomain) def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, - length: int, postBytes, boundary: str) -> int: + length: int, postBytes, boundary: str, + callingDomain: str) -> int: # Note: this needs to happen synchronously # 0=this is not a new post # 1=new post success @@ -9255,6 +9259,14 @@ class PubServer(BaseHTTPRequestHandler): privateEvent = False else: privateEvent = True + + # show a sending post screen + msg = \ + htmlSendingPost(self.server.baseDir, + self.server.translate).encode('utf-8') + self._login_headers('text/html', len(msg), callingDomain) + self._write(msg) + if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -9274,6 +9286,7 @@ class PubServer(BaseHTTPRequestHandler): if messageJson: if fields['schedulePost']: return 1 + if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, self.server.httpPrefix, @@ -9668,7 +9681,8 @@ class PubServer(BaseHTTPRequestHandler): return 1 return -1 - def _receiveNewPost(self, postType: str, path: str) -> int: + def _receiveNewPost(self, postType: str, path: str, + callingDomain: str) -> int: """A new post has been created This creates a thread to send the new post """ @@ -9769,7 +9783,8 @@ class PubServer(BaseHTTPRequestHandler): print('Creating new post from: ' + newPostThreadName) self._receiveNewPostProcess(postType, path, headers, length, - postBytes, boundary) + postBytes, boundary, + callingDomain) return pageNumber def _cryptoAPIreadHandle(self): @@ -10229,7 +10244,8 @@ class PubServer(BaseHTTPRequestHandler): elif currPostType == 'newevent': postRedirect = 'tlevents' - pageNumber = self._receiveNewPost(currPostType, self.path) + pageNumber = \ + self._receiveNewPost(currPostType, self.path, callingDomain) if pageNumber: nickname = self.path.split('/users/')[1] if '/' in nickname: diff --git a/epicyon-send.css b/epicyon-send.css new file mode 100644 index 000000000..4a5d03304 --- /dev/null +++ b/epicyon-send.css @@ -0,0 +1,86 @@ +@charset "UTF-8"; + +:root { + --main-bg-color: #282c37; + --link-bg-color: #282c37; + --main-bg-color-darker: #232c37; + --main-bg-color-report: #221c27; + --main-header-color-roles: #282237; + --main-fg-color: #dddddd; + --main-link-color: #999; + --main-visited-color: #888; + --border-color: #505050; + --font-size-header: 18px; + --font-color-header: #ccc; + --font-size: 22px; + --text-entry-foreground: #ccc; + --text-entry-background: #111; + --time-color: #aaa; + --button-text: #FFFFFF; + --button-background: #999; + --button-selected: #666; + --focus-color: white; +} + +@font-face { + font-family: 'Bedstead'; + font-style: italic; + font-weight: normal; + font-display: block; + src: url('./fonts/bedstead.otf') format('opentype'); +} +@font-face { + font-family: 'Bedstead'; + font-style: normal; + font-weight: normal; + font-display: block; + src: url('./fonts/bedstead.otf') format('opentype'); +} + +body, html { + background-color: var(--main-bg-color); + color: var(--main-fg-color); + + height: 100%; + font-family: Arial, Helvetica, sans-serif; + max-width: 80%; + min-width: 600px; + margin: 0 auto; + font-size: var(--font-size); +} + +a, u { + color: var(--main-fg-color); +} + +a:visited{ + color: var(--main-visited-color); + background: var(--link-bg-color); + font-weight: bold; +} + +a:link { + color: var(--main-link-color); + background: var(--link-bg-color); + font-weight: bold; +} + +a:focus { + border: 2px solid var(--focus-color); +} + +.screentitle { + font-size: 30px; + font-family: Arial, Helvetica, sans-serif; +} + +div { + height: 300px; + width: 400px; + background: var(--main-bg-color); + position: fixed; + top: 50%; + left: 50%; + margin-top: -100px; + margin-left: -200px; +} diff --git a/theme.py b/theme.py index 1c092e630..c32d64a76 100644 --- a/theme.py +++ b/theme.py @@ -15,7 +15,7 @@ from shutil import copyfile def getThemeFiles() -> []: return ('epicyon.css', 'login.css', 'follow.css', 'suspended.css', 'calendar.css', 'blog.css', - 'options.css', 'search.css') + 'options.css', 'search.css', 'send.css') def getThemesList() -> []: diff --git a/webinterface.py b/webinterface.py index 6a07bd8d8..2f814981d 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1984,7 +1984,7 @@ def htmlAbout(baseDir: str, httpPrefix: str, return aboutForm -def htmlHashtagBlocked(baseDir: str) -> str: +def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: """Show the screen for a blocked hashtag """ blockedHashtagForm = '' @@ -2003,6 +2003,25 @@ def htmlHashtagBlocked(baseDir: str) -> str: return blockedHashtagForm +def htmlSendingPost(baseDir: str, translate: {}) -> str: + """A wait screen while sending a post + """ + sendForm = '' + cssFilename = baseDir + '/epicyon-send.css' + if os.path.isfile(baseDir + '/send.css'): + cssFilename = baseDir + '/send.css' + with open(cssFilename, 'r') as cssFile: + blockedHashtagCSS = cssFile.read() + sendForm = htmlHeader(cssFilename, blockedHashtagCSS) + sendForm += '

\n' + sendForm += '

Sending Post

\n' + sendForm += \ + '

Please wait

\n' + sendForm += '
\n' + sendForm += htmlFooter() + return sendForm + + def htmlSuspended(baseDir: str) -> str: """Show the screen for suspended accounts """ From 34d26e3ce86ad8103a3c0024ae69bf58e1915c88 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 11:07:04 +0100 Subject: [PATCH 39/63] Remove sending post screen --- daemon.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/daemon.py b/daemon.py index 6ef14f9a7..5b9eab209 100644 --- a/daemon.py +++ b/daemon.py @@ -9261,11 +9261,11 @@ class PubServer(BaseHTTPRequestHandler): privateEvent = True # show a sending post screen - msg = \ - htmlSendingPost(self.server.baseDir, - self.server.translate).encode('utf-8') - self._login_headers('text/html', len(msg), callingDomain) - self._write(msg) + # msg = \ + # htmlSendingPost(self.server.baseDir, + # self.server.translate).encode('utf-8') + # self._login_headers('text/html', len(msg), callingDomain) + # self._write(msg) if postType == 'newpost': messageJson = \ @@ -9544,8 +9544,7 @@ class PubServer(BaseHTTPRequestHandler): if messageJson: if fields['schedulePost']: return 1 -# if self.server.debug: - print('DEBUG: new DM to ' + + print('Sending new DM to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, From eff4c42fa4199f262e7fc59e523affcb5a869542 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 11:11:10 +0100 Subject: [PATCH 40/63] Fix outbox --- posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posts.py b/posts.py index 0022ce332..53c5d3bae 100644 --- a/posts.py +++ b/posts.py @@ -2511,7 +2511,7 @@ def createOutbox(session, baseDir: str, nickname: str, domain: str, return createBoxIndexed({}, session, baseDir, 'outbox', nickname, domain, port, httpPrefix, itemsPerPage, headerOnly, authorized, - False, pageNumber) + pageNumber) def createModeration(baseDir: str, nickname: str, domain: str, port: int, From 2bbd39fcd902136ff2e12fafa954e952380d78b9 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 11:54:41 +0100 Subject: [PATCH 41/63] Extra translations --- daemon.py | 2 +- outbox.py | 3 +-- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/zh.json | 3 ++- webinterface.py | 4 +++- 18 files changed, 35 insertions(+), 19 deletions(-) diff --git a/daemon.py b/daemon.py index 5b9eab209..2eb044dcc 100644 --- a/daemon.py +++ b/daemon.py @@ -152,7 +152,7 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked -from webinterface import htmlSendingPost +# from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare diff --git a/outbox.py b/outbox.py index 4254bb101..56016b683 100644 --- a/outbox.py +++ b/outbox.py @@ -39,8 +39,7 @@ from shares import outboxUndoShareUpload def postMessageToOutbox(messageJson: {}, postToNickname: str, server, baseDir: str, httpPrefix: str, domain: str, domainFull: str, - onionDomain: str, i2pDomain: str, - port: int, + onionDomain: str, i2pDomain: str, port: int, recentPostsCache: {}, followersThreads: [], federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, diff --git a/translations/ar.json b/translations/ar.json index a27a5c6d2..b519fa594 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -286,5 +286,6 @@ "Don't show the Like button": "لا تظهر زر أعجبني", "Autogenerated Hashtags": "علامات التجزئة المُنشأة تلقائيًا", "Autogenerated Content Warnings": "تحذيرات المحتوى المُنشأ تلقائيًا", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag محظور" } diff --git a/translations/ca.json b/translations/ca.json index b24e0a819..62736ad2d 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -286,5 +286,6 @@ "Don't show the Like button": "No mostreu el botó M'agrada", "Autogenerated Hashtags": "Hashtags autogenerats", "Autogenerated Content Warnings": "Advertiments de contingut autogenerats", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag bloquejat" } diff --git a/translations/cy.json b/translations/cy.json index 65d011d4b..89f52d7ee 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Peidiwch â dangos y botwm Hoffi", "Autogenerated Hashtags": "Hashtags awtogeneiddiedig", "Autogenerated Content Warnings": "Rhybuddion Cynnwys Autogenerated", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag wedi'i Blocio" } diff --git a/translations/de.json b/translations/de.json index 3ca9eee82..e3bc529b6 100644 --- a/translations/de.json +++ b/translations/de.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Zeigen Sie nicht die Schaltfläche \"Gefällt mir\" an", "Autogenerated Hashtags": "Automatisch generierte Hashtags", "Autogenerated Content Warnings": "Warnungen vor automatisch generierten Inhalten", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag blockiert" } diff --git a/translations/en.json b/translations/en.json index b93dfd158..c85bbfe85 100644 --- a/translations/en.json +++ b/translations/en.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Don't show the Like button", "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/es.json b/translations/es.json index 150aed26f..6fe9c141b 100644 --- a/translations/es.json +++ b/translations/es.json @@ -286,5 +286,6 @@ "Don't show the Like button": "No mostrar el botón Me gusta", "Autogenerated Hashtags": "Hashtags autogenerados", "Autogenerated Content Warnings": "Advertencias de contenido generado automáticamente", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/fr.json b/translations/fr.json index 09be80db8..4a1fb6cb0 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Ne pas afficher le bouton J'aime", "Autogenerated Hashtags": "Hashtags générés automatiquement", "Autogenerated Content Warnings": "Avertissements de contenu générés automatiquement", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag bloqué" } diff --git a/translations/ga.json b/translations/ga.json index 7434193c1..1c1266bae 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Ná taispeáin an cnaipe Cosúil", "Autogenerated Hashtags": "Hashtags uathghinte", "Autogenerated Content Warnings": "Rabhaidh Ábhar Uathghinte", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag Blocáilte" } diff --git a/translations/hi.json b/translations/hi.json index 59a9a1efd..bff59966f 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -286,5 +286,6 @@ "Don't show the Like button": "लाइक बटन न दिखाएं", "Autogenerated Hashtags": "ऑटोजेनरेटेड हैशटैग", "Autogenerated Content Warnings": "स्वतः प्राप्त सामग्री चेतावनी", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "हैशटैग अवरुद्ध" } diff --git a/translations/it.json b/translations/it.json index 7bc918965..08ff2618c 100644 --- a/translations/it.json +++ b/translations/it.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Non mostrare il pulsante Mi piace", "Autogenerated Hashtags": "Hashtag generati automaticamente", "Autogenerated Content Warnings": "Avvisi sui contenuti generati automaticamente", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag bloccato" } diff --git a/translations/ja.json b/translations/ja.json index 98f47d53e..382b43cc3 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -286,5 +286,6 @@ "Don't show the Like button": "「いいね!」ボタンを表示しない", "Autogenerated Hashtags": "自動生成されたハッシュタグ", "Autogenerated Content Warnings": "自動生成されたコンテンツの警告", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "ハッシュタグがブロックされました" } diff --git a/translations/oc.json b/translations/oc.json index aeeeac9b8..9a0cf46e9 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -282,5 +282,6 @@ "Don't show the Like button": "Don't show the Like button", "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/pt.json b/translations/pt.json index c8167d622..72314d67e 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Não mostrar o botão Curtir", "Autogenerated Hashtags": "Hashtags autogeradas", "Autogenerated Content Warnings": "Avisos de conteúdo gerado automaticamente", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/ru.json b/translations/ru.json index daa47b268..cb7a0b211 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -286,5 +286,6 @@ "Don't show the Like button": "Не показывать кнопку \"Нравится\"", "Autogenerated Hashtags": "Автоматически сгенерированные хештеги", "Autogenerated Content Warnings": "Автоматические предупреждения о содержании", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "Хештег заблокирован" } diff --git a/translations/zh.json b/translations/zh.json index d43831e42..a171135f0 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -286,5 +286,6 @@ "Don't show the Like button": "不显示“赞”按钮", "Autogenerated Hashtags": "自动生成的标签", "Autogenerated Content Warnings": "自动生成的内容警告", - "Indymedia": "Indymedia" + "Indymedia": "Indymedia", + "Hashtag Blocked": "标签被阻止" } diff --git a/webinterface.py b/webinterface.py index 2f814981d..cee91edf3 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1995,7 +1995,9 @@ def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: blockedHashtagCSS = cssFile.read() blockedHashtagForm = htmlHeader(cssFilename, blockedHashtagCSS) blockedHashtagForm += '
\n' - blockedHashtagForm += '

Hashtag Blocked

\n' + blockedHashtagForm += \ + '

' + \ + translate['Hashtag Blocked'] + '

\n' blockedHashtagForm += \ '

See Terms of Service

\n' blockedHashtagForm += '
\n' From 3f689f062da6d01c6ab75d4b962ccda1836e3e5a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 12:08:40 +0100 Subject: [PATCH 42/63] Extra translations --- translations/ar.json | 4 +++- translations/ca.json | 4 +++- translations/cy.json | 4 +++- translations/de.json | 4 +++- translations/en.json | 4 +++- translations/es.json | 4 +++- translations/fr.json | 4 +++- translations/ga.json | 4 +++- translations/hi.json | 4 +++- translations/it.json | 4 +++- translations/ja.json | 4 +++- translations/oc.json | 4 +++- translations/pt.json | 4 +++- translations/ru.json | 4 +++- translations/zh.json | 4 +++- webinterface.py | 13 ++++++++----- 16 files changed, 53 insertions(+), 20 deletions(-) diff --git a/translations/ar.json b/translations/ar.json index b519fa594..8bc009ea1 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "علامات التجزئة المُنشأة تلقائيًا", "Autogenerated Content Warnings": "تحذيرات المحتوى المُنشأ تلقائيًا", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag محظور" + "Hashtag Blocked": "Hashtag محظور", + "Sending Post": "إرسال آخر", + "Please wait": "ارجوك انتظر" } diff --git a/translations/ca.json b/translations/ca.json index 62736ad2d..051525ec8 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogenerats", "Autogenerated Content Warnings": "Advertiments de contingut autogenerats", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloquejat" + "Hashtag Blocked": "Hashtag bloquejat", + "Sending Post": "S'està enviant el missatge", + "Please wait": "Espereu, si us plau" } diff --git a/translations/cy.json b/translations/cy.json index 89f52d7ee..3a04f108c 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags awtogeneiddiedig", "Autogenerated Content Warnings": "Rhybuddion Cynnwys Autogenerated", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag wedi'i Blocio" + "Hashtag Blocked": "Hashtag wedi'i Blocio", + "Sending Post": "Post Anfon", + "Please wait": "Arhoswch os gwelwch yn dda" } diff --git a/translations/de.json b/translations/de.json index e3bc529b6..d3d097525 100644 --- a/translations/de.json +++ b/translations/de.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Automatisch generierte Hashtags", "Autogenerated Content Warnings": "Warnungen vor automatisch generierten Inhalten", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag blockiert" + "Hashtag Blocked": "Hashtag blockiert", + "Sending Post": "Post senden", + "Please wait": "Warten Sie mal" } diff --git a/translations/en.json b/translations/en.json index c85bbfe85..688d4d8a8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked" + "Hashtag Blocked": "Hashtag Blocked", + "Sending Post": "Sending Post", + "Please wait": "Please wait" } diff --git a/translations/es.json b/translations/es.json index 6fe9c141b..fecc20223 100644 --- a/translations/es.json +++ b/translations/es.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogenerados", "Autogenerated Content Warnings": "Advertencias de contenido generado automáticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada" + "Hashtag Blocked": "Hashtag bloqueada", + "Sending Post": "Envío de publicación", + "Please wait": "por favor espera" } diff --git a/translations/fr.json b/translations/fr.json index 4a1fb6cb0..53f4d7c3f 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags générés automatiquement", "Autogenerated Content Warnings": "Avertissements de contenu générés automatiquement", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqué" + "Hashtag Blocked": "Hashtag bloqué", + "Sending Post": "Envoi du message", + "Please wait": "S'il vous plaît, attendez" } diff --git a/translations/ga.json b/translations/ga.json index 1c1266bae..90392720b 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags uathghinte", "Autogenerated Content Warnings": "Rabhaidh Ábhar Uathghinte", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocáilte" + "Hashtag Blocked": "Hashtag Blocáilte", + "Sending Post": "Post Seolta", + "Please wait": "Fán le do thoil" } diff --git a/translations/hi.json b/translations/hi.json index bff59966f..7ca0f0055 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "ऑटोजेनरेटेड हैशटैग", "Autogenerated Content Warnings": "स्वतः प्राप्त सामग्री चेतावनी", "Indymedia": "Indymedia", - "Hashtag Blocked": "हैशटैग अवरुद्ध" + "Hashtag Blocked": "हैशटैग अवरुद्ध", + "Sending Post": "पोस्ट भेज रहा है", + "Please wait": "कृपया प्रतीक्षा कीजिये" } diff --git a/translations/it.json b/translations/it.json index 08ff2618c..5e9536646 100644 --- a/translations/it.json +++ b/translations/it.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtag generati automaticamente", "Autogenerated Content Warnings": "Avvisi sui contenuti generati automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloccato" + "Hashtag Blocked": "Hashtag bloccato", + "Sending Post": "Invio di post", + "Please wait": "attendere prego" } diff --git a/translations/ja.json b/translations/ja.json index 382b43cc3..3c5cc3fc9 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "自動生成されたハッシュタグ", "Autogenerated Content Warnings": "自動生成されたコンテンツの警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "ハッシュタグがブロックされました" + "Hashtag Blocked": "ハッシュタグがブロックされました", + "Sending Post": "投稿を送信する", + "Please wait": "お待ちください" } diff --git a/translations/oc.json b/translations/oc.json index 9a0cf46e9..edf65b994 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -283,5 +283,7 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked" + "Hashtag Blocked": "Hashtag Blocked", + "Sending Post": "Sending Post", + "Please wait": "Please wait" } diff --git a/translations/pt.json b/translations/pt.json index 72314d67e..b2c82b634 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogeradas", "Autogenerated Content Warnings": "Avisos de conteúdo gerado automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada" + "Hashtag Blocked": "Hashtag bloqueada", + "Sending Post": "Enviando postagem", + "Please wait": "Por favor, espere" } diff --git a/translations/ru.json b/translations/ru.json index cb7a0b211..2308cddc7 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Автоматически сгенерированные хештеги", "Autogenerated Content Warnings": "Автоматические предупреждения о содержании", "Indymedia": "Indymedia", - "Hashtag Blocked": "Хештег заблокирован" + "Hashtag Blocked": "Хештег заблокирован", + "Sending Post": "Отправка сообщения", + "Please wait": "пожалуйста, подождите" } diff --git a/translations/zh.json b/translations/zh.json index a171135f0..f572b9c3f 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "自动生成的标签", "Autogenerated Content Warnings": "自动生成的内容警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "标签被阻止" + "Hashtag Blocked": "标签被阻止", + "Sending Post": "发送帖子", + "Please wait": "请稍候" } diff --git a/webinterface.py b/webinterface.py index cee91edf3..a30c232db 100644 --- a/webinterface.py +++ b/webinterface.py @@ -1999,7 +1999,8 @@ def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: '

' + \ translate['Hashtag Blocked'] + '

\n' blockedHashtagForm += \ - '

See Terms of Service

\n' + '

See ' + \ + translate['Terms of Service'] + '

\n' blockedHashtagForm += '\n' blockedHashtagForm += htmlFooter() return blockedHashtagForm @@ -2013,12 +2014,14 @@ def htmlSendingPost(baseDir: str, translate: {}) -> str: if os.path.isfile(baseDir + '/send.css'): cssFilename = baseDir + '/send.css' with open(cssFilename, 'r') as cssFile: - blockedHashtagCSS = cssFile.read() - sendForm = htmlHeader(cssFilename, blockedHashtagCSS) + sendingCSS = cssFile.read() + sendForm = htmlHeader(cssFilename, sendingCSS) sendForm += '
\n' - sendForm += '

Sending Post

\n' sendForm += \ - '

Please wait

\n' + '

' + \ + translate['Sending Post'] + '

\n' + sendForm += \ + '

' + translate['Please wait'] + '

\n' sendForm += '
\n' sendForm += htmlFooter() return sendForm From 8f8f57285fb98fd8ff33c462946bfbf0e2fa776c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 13:28:57 +0100 Subject: [PATCH 43/63] Preserve headers --- daemon.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/daemon.py b/daemon.py index 2eb044dcc..e41905e15 100644 --- a/daemon.py +++ b/daemon.py @@ -9261,11 +9261,13 @@ class PubServer(BaseHTTPRequestHandler): privateEvent = True # show a sending post screen - # msg = \ - # htmlSendingPost(self.server.baseDir, - # self.server.translate).encode('utf-8') - # self._login_headers('text/html', len(msg), callingDomain) - # self._write(msg) + currHeaders = self.headers.copy() + msg = \ + htmlSendingPost(self.server.baseDir, + self.server.translate).encode('utf-8') + self._login_headers('text/html', len(msg), callingDomain) + self._write(msg) + self.headers = currHeaders.copy() if postType == 'newpost': messageJson = \ From 0ba1cc0ead480cff81183b131606b1cc337f7517 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 13:39:40 +0100 Subject: [PATCH 44/63] Get and set header fields --- daemon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index e41905e15..28573c87b 100644 --- a/daemon.py +++ b/daemon.py @@ -9261,13 +9261,14 @@ class PubServer(BaseHTTPRequestHandler): privateEvent = True # show a sending post screen - currHeaders = self.headers.copy() + currHeaders = self.headers.items() msg = \ htmlSendingPost(self.server.baseDir, self.server.translate).encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) - self.headers = currHeaders.copy() + for fieldName, fieldValue in currHeaders.items(): + self.headers[fieldName] = fieldValue if postType == 'newpost': messageJson = \ From 9534197005b10c325de66e30efc49bc0d717dfa6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 13:41:53 +0100 Subject: [PATCH 45/63] Restore include --- daemon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 28573c87b..e73d187a2 100644 --- a/daemon.py +++ b/daemon.py @@ -152,7 +152,7 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked -# from webinterface import htmlSendingPost +from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare @@ -9262,6 +9262,7 @@ class PubServer(BaseHTTPRequestHandler): # show a sending post screen currHeaders = self.headers.items() + print('currHeaders: ' + str(currHeaders)) msg = \ htmlSendingPost(self.server.baseDir, self.server.translate).encode('utf-8') From 77ff39aa06215ef9a734b8cc401219d4bc75e28d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 13:46:43 +0100 Subject: [PATCH 46/63] Setting headers --- daemon.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index e73d187a2..549121e67 100644 --- a/daemon.py +++ b/daemon.py @@ -9268,7 +9268,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.translate).encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) - for fieldName, fieldValue in currHeaders.items(): + for headerField in currHeaders: + print(str(headerField)) + fieldName = headerField[0] + fieldValue = headerField[1] self.headers[fieldName] = fieldValue if postType == 'newpost': From 915fc1f0baea5f5578d8efbdd2c5e54bd13bb814 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 14:16:21 +0100 Subject: [PATCH 47/63] Return to default timeline after sending post --- daemon.py | 117 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/daemon.py b/daemon.py index 549121e67..320fb6ed2 100644 --- a/daemon.py +++ b/daemon.py @@ -9116,9 +9116,27 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) + def _redirectToBox(self, boxName: str, path: str, + callingDomain: str, cookie: str) -> None: + """Redirects to the given box + """ + if '/users/' not in path: + return + nickname = path.split('/users/')[1] + if '/' in nickname: + nickname = nickname.split('/')[0] + usersPath = '/users/' + nickname + actorStr = path.split('/users/')[0] + usersPath + if callingDomain.endswith('.onion') and self.server.onionDomain: + actorStr = 'http://' + self.server.onionDomain + usersPath + elif (callingDomain.endswith('.i2p') and self.server.i2pDomain): + actorStr = 'http://' + self.server.i2pDomain + usersPath + self._redirect_headers(actorStr + '/' + boxName, + cookie, callingDomain) + def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, - callingDomain: str) -> int: + callingDomain: str, cookie: str) -> int: # Note: this needs to happen synchronously # 0=this is not a new post # 1=new post success @@ -9260,19 +9278,19 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True + # make a copy of the headers + # currHeaders = self.headers.items() # show a sending post screen - currHeaders = self.headers.items() - print('currHeaders: ' + str(currHeaders)) msg = \ htmlSendingPost(self.server.baseDir, self.server.translate).encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) - for headerField in currHeaders: - print(str(headerField)) - fieldName = headerField[0] - fieldValue = headerField[1] - self.headers[fieldName] = fieldValue + # restore headers + # for headerField in currHeaders: + # fieldName = headerField[0] + # fieldValue = headerField[1] + # self.headers[fieldName] = fieldValue if postType == 'newpost': messageJson = \ @@ -9292,6 +9310,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9301,8 +9321,12 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9320,6 +9344,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9328,8 +9354,12 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9398,6 +9428,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9405,6 +9437,8 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9426,6 +9460,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9434,8 +9470,12 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9459,6 +9499,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9467,8 +9509,12 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9519,10 +9565,16 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9550,6 +9602,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9560,8 +9614,12 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9588,16 +9646,24 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9616,13 +9682,21 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 else: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('message'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9631,6 +9705,8 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9650,19 +9726,33 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('category'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('location'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('duration'): + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9684,11 +9774,15 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 + self._redirectToBox(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, - callingDomain: str) -> int: + callingDomain: str, cookie: str) -> int: """A new post has been created This creates a thread to send the new post """ @@ -9790,7 +9884,7 @@ class PubServer(BaseHTTPRequestHandler): self._receiveNewPostProcess(postType, path, headers, length, postBytes, boundary, - callingDomain) + callingDomain, cookie) return pageNumber def _cryptoAPIreadHandle(self): @@ -10251,7 +10345,8 @@ class PubServer(BaseHTTPRequestHandler): postRedirect = 'tlevents' pageNumber = \ - self._receiveNewPost(currPostType, self.path, callingDomain) + self._receiveNewPost(currPostType, self.path, + callingDomain, cookie) if pageNumber: nickname = self.path.split('/users/')[1] if '/' in nickname: From 673d7f6075dfeeaf60b005a0799a37b31ef92d98 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 14:19:52 +0100 Subject: [PATCH 48/63] Restore headers --- daemon.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/daemon.py b/daemon.py index 320fb6ed2..cc18ba52d 100644 --- a/daemon.py +++ b/daemon.py @@ -9279,7 +9279,7 @@ class PubServer(BaseHTTPRequestHandler): privateEvent = True # make a copy of the headers - # currHeaders = self.headers.items() + currHeaders = self.headers.items() # show a sending post screen msg = \ htmlSendingPost(self.server.baseDir, @@ -9287,10 +9287,10 @@ class PubServer(BaseHTTPRequestHandler): self._login_headers('text/html', len(msg), callingDomain) self._write(msg) # restore headers - # for headerField in currHeaders: - # fieldName = headerField[0] - # fieldValue = headerField[1] - # self.headers[fieldName] = fieldValue + for headerField in currHeaders: + fieldName = headerField[0] + fieldValue = headerField[1] + self.headers[fieldName] = fieldValue if postType == 'newpost': messageJson = \ From 5c350d167172ceb7011a8f00da76df20ab6f2365 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 14:23:38 +0100 Subject: [PATCH 49/63] Full actor path --- daemon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index cc18ba52d..cfda4e523 100644 --- a/daemon.py +++ b/daemon.py @@ -9126,7 +9126,8 @@ class PubServer(BaseHTTPRequestHandler): if '/' in nickname: nickname = nickname.split('/')[0] usersPath = '/users/' + nickname - actorStr = path.split('/users/')[0] + usersPath + actorStr = self.server.httpPrefix + '://' + self.server.domainFull + \ + path.split('/users/')[0] + usersPath if callingDomain.endswith('.onion') and self.server.onionDomain: actorStr = 'http://' + self.server.onionDomain + usersPath elif (callingDomain.endswith('.i2p') and self.server.i2pDomain): From f20c7a841d108e45d63acb567ecc0e201cd758d1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 14:35:10 +0100 Subject: [PATCH 50/63] More debug --- daemon.py | 185 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 107 insertions(+), 78 deletions(-) diff --git a/daemon.py b/daemon.py index cfda4e523..8a18b818e 100644 --- a/daemon.py +++ b/daemon.py @@ -9116,8 +9116,8 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) - def _redirectToBox(self, boxName: str, path: str, - callingDomain: str, cookie: str) -> None: + def _redirectAfterPost(self, boxName: str, path: str, + callingDomain: str, cookie: str) -> None: """Redirects to the given box """ if '/users/' not in path: @@ -9132,6 +9132,7 @@ class PubServer(BaseHTTPRequestHandler): actorStr = 'http://' + self.server.onionDomain + usersPath elif (callingDomain.endswith('.i2p') and self.server.i2pDomain): actorStr = 'http://' + self.server.i2pDomain + usersPath + print('Redirecting to: ' + actorStr + '/' + boxName) self._redirect_headers(actorStr + '/' + boxName, cookie, callingDomain) @@ -9311,8 +9312,9 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9322,12 +9324,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9345,8 +9349,9 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9355,12 +9360,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9429,8 +9436,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9438,8 +9446,8 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9461,8 +9469,9 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9471,12 +9480,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9500,8 +9511,9 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9510,12 +9522,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9566,16 +9580,19 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9603,8 +9620,9 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9615,12 +9633,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9647,24 +9667,28 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9683,21 +9707,24 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 if not fields.get('message'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9706,8 +9733,8 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9727,33 +9754,35 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('category'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('location'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('duration'): - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9775,11 +9804,11 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 - self._redirectToBox(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._redirectAfterPost(self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, From 963aeb41a96b1ad19ac5248fee057a2a1122cfe4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:10:54 +0100 Subject: [PATCH 51/63] Redirect to the inbox --- daemon.py | 365 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 215 insertions(+), 150 deletions(-) diff --git a/daemon.py b/daemon.py index 8a18b818e..8105804bc 100644 --- a/daemon.py +++ b/daemon.py @@ -5594,9 +5594,10 @@ class PubServer(BaseHTTPRequestHandler): maxPostsInFeed, 'inbox', authorized) if inboxFeed: - self._benchmarkGETtimings(GETstartTime, GETtimings, - 'show status done', - 'show inbox json') + if GETstartTime: + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox json') if self._requestHTTP(): nickname = path.replace('/users/', '') nickname = nickname.replace('/inbox', '') @@ -5620,10 +5621,11 @@ class PubServer(BaseHTTPRequestHandler): httpPrefix, maxPostsInFeed, 'inbox', authorized) - self._benchmarkGETtimings(GETstartTime, - GETtimings, - 'show status done', - 'show inbox page') + if GETstartTime: + self._benchmarkGETtimings(GETstartTime, + GETtimings, + 'show status done', + 'show inbox page') msg = htmlInbox(defaultTimeline, recentPostsCache, maxRecentPosts, @@ -5642,16 +5644,18 @@ class PubServer(BaseHTTPRequestHandler): projectVersion, self._isMinimal(nickname), YTReplacementDomain) - self._benchmarkGETtimings(GETstartTime, GETtimings, - 'show status done', - 'show inbox html') + if GETstartTime: + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox html') msg = msg.encode('utf-8') self._set_headers('text/html', len(msg), cookie, callingDomain) self._write(msg) - self._benchmarkGETtimings(GETstartTime, GETtimings, - 'show status done', - 'show inbox') + if GETstartTime: + self._benchmarkGETtimings(GETstartTime, GETtimings, + 'show status done', + 'show inbox') else: # don't need authenticated fetch here because # there is already the authorization check @@ -9116,29 +9120,61 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) - def _redirectAfterPost(self, boxName: str, path: str, - callingDomain: str, cookie: str) -> None: + def _showSendingScreen(self, authorized: bool, callingDomain: str) -> None: + """Shows a 'sending post' wait screen + """ + if self.server.defaultTimeline != 'inbox': + return + # make a copy of the headers + currHeaders = self.headers.items() + + # show a sending post screen + msg = \ + htmlSendingPost(self.server.baseDir, + self.server.translate).encode('utf-8') + self._login_headers('text/html', len(msg), callingDomain) + self._write(msg) + + # restore headers + for headerField in currHeaders: + fieldName = headerField[0] + fieldValue = headerField[1] + self.headers[fieldName] = fieldValue + + def _clearSendingScreen(self, authorized: bool, + boxName: str, path: str, + callingDomain: str, cookie: str) -> None: """Redirects to the given box """ - if '/users/' not in path: + if boxName != 'inbox': return - nickname = path.split('/users/')[1] - if '/' in nickname: - nickname = nickname.split('/')[0] - usersPath = '/users/' + nickname - actorStr = self.server.httpPrefix + '://' + self.server.domainFull + \ - path.split('/users/')[0] + usersPath - if callingDomain.endswith('.onion') and self.server.onionDomain: - actorStr = 'http://' + self.server.onionDomain + usersPath - elif (callingDomain.endswith('.i2p') and self.server.i2pDomain): - actorStr = 'http://' + self.server.i2pDomain + usersPath - print('Redirecting to: ' + actorStr + '/' + boxName) - self._redirect_headers(actorStr + '/' + boxName, - cookie, callingDomain) + self._showInbox(authorized, + callingDomain, self.path, + self.server.baseDir, + self.server.httpPrefix, + self.server.domain, + self.server.domainFull, + self.server.port, + self.server.onionDomain, + self.server.i2pDomain, + None, None, + self.server.proxyType, + cookie, self.server.debug, + self.server.recentPostsCache, + self.server.session, + self.server.defaultTimeline, + self.server.maxRecentPosts, + self.server.translate, + self.server.cachedWebfingers, + self.server.personCache, + self.server.allowDeletion, + self.server.projectVersion, + self.server.YTReplacementDomain) def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, - callingDomain: str, cookie: str) -> int: + callingDomain: str, cookie: str, + authorized: bool) -> int: # Note: this needs to happen synchronously # 0=this is not a new post # 1=new post success @@ -9280,19 +9316,7 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True - # make a copy of the headers - currHeaders = self.headers.items() - # show a sending post screen - msg = \ - htmlSendingPost(self.server.baseDir, - self.server.translate).encode('utf-8') - self._login_headers('text/html', len(msg), callingDomain) - self._write(msg) - # restore headers - for headerField in currHeaders: - fieldName = headerField[0] - fieldValue = headerField[1] - self.headers[fieldName] = fieldValue + self._showSendingScreen(authorized, callingDomain) if postType == 'newpost': messageJson = \ @@ -9312,9 +9336,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9324,14 +9349,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9349,9 +9376,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9360,14 +9388,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9436,9 +9466,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9446,8 +9477,9 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9469,9 +9501,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9480,14 +9513,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9511,9 +9546,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9522,14 +9558,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9580,19 +9618,22 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9620,9 +9661,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9633,14 +9675,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9667,28 +9711,32 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9707,24 +9755,28 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 if not fields.get('message'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9733,8 +9785,9 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9754,35 +9807,42 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('category'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('location'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('duration'): - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, - cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9804,15 +9864,18 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 - self._redirectAfterPost(self.server.defaultTimeline, - self.path, callingDomain, cookie) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, - callingDomain: str, cookie: str) -> int: + callingDomain: str, cookie: str, + authorized: bool) -> int: """A new post has been created This creates a thread to send the new post """ @@ -9914,7 +9977,8 @@ class PubServer(BaseHTTPRequestHandler): self._receiveNewPostProcess(postType, path, headers, length, postBytes, boundary, - callingDomain, cookie) + callingDomain, cookie, + authorized) return pageNumber def _cryptoAPIreadHandle(self): @@ -10376,7 +10440,8 @@ class PubServer(BaseHTTPRequestHandler): pageNumber = \ self._receiveNewPost(currPostType, self.path, - callingDomain, cookie) + callingDomain, cookie, + authorized) if pageNumber: nickname = self.path.split('/users/')[1] if '/' in nickname: From 622d62c26f26928f3d18adeed219b08e396f3464 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:18:46 +0100 Subject: [PATCH 52/63] Check that message exists --- daemon.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/daemon.py b/daemon.py index 8105804bc..ab4450ed2 100644 --- a/daemon.py +++ b/daemon.py @@ -5648,10 +5648,13 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', 'show inbox html') - msg = msg.encode('utf-8') - self._set_headers('text/html', len(msg), - cookie, callingDomain) - self._write(msg) + + if msg: + msg = msg.encode('utf-8') + self._set_headers('text/html', len(msg), + cookie, callingDomain) + self._write(msg) + if GETstartTime: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', From 68a1a710feaed83155deeac01cdca319776fa0ec Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:20:36 +0100 Subject: [PATCH 53/63] Check for http --- daemon.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemon.py b/daemon.py index ab4450ed2..429637f93 100644 --- a/daemon.py +++ b/daemon.py @@ -9128,6 +9128,8 @@ class PubServer(BaseHTTPRequestHandler): """ if self.server.defaultTimeline != 'inbox': return + if not self._requestHTTP(): + return # make a copy of the headers currHeaders = self.headers.items() @@ -9151,6 +9153,8 @@ class PubServer(BaseHTTPRequestHandler): """ if boxName != 'inbox': return + if not self._requestHTTP(): + return self._showInbox(authorized, callingDomain, self.path, self.server.baseDir, From 71e2e4cf9d7e9f1f6f8acd619c609d8d1c7e034a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:33:09 +0100 Subject: [PATCH 54/63] Debug --- daemon.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/daemon.py b/daemon.py index 429637f93..61b3b10c8 100644 --- a/daemon.py +++ b/daemon.py @@ -5581,8 +5581,11 @@ class PubServer(BaseHTTPRequestHandler): YTReplacementDomain: str) -> bool: """Shows the inbox timeline """ + print('Inbox test 1') if '/users/' in path: + print('Inbox test 2') if authorized: + print('Inbox test 3') inboxFeed = \ personBoxJson(recentPostsCache, session, @@ -5594,11 +5597,13 @@ class PubServer(BaseHTTPRequestHandler): maxPostsInFeed, 'inbox', authorized) if inboxFeed: + print('Inbox test 4') if GETstartTime: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', 'show inbox json') if self._requestHTTP(): + print('Inbox test 5') nickname = path.replace('/users/', '') nickname = nickname.replace('/inbox', '') pageNumber = 1 @@ -5610,6 +5615,7 @@ class PubServer(BaseHTTPRequestHandler): else: pageNumber = 1 if 'page=' not in path: + print('Inbox test 6') # if no page was specified then show the first inboxFeed = \ personBoxJson(recentPostsCache, @@ -5650,6 +5656,7 @@ class PubServer(BaseHTTPRequestHandler): 'show inbox html') if msg: + print('Inbox test 7') msg = msg.encode('utf-8') self._set_headers('text/html', len(msg), cookie, callingDomain) @@ -5660,6 +5667,7 @@ class PubServer(BaseHTTPRequestHandler): 'show status done', 'show inbox') else: + print('Inbox test 8') # don't need authenticated fetch here because # there is already the authorization check msg = json.dumps(inboxFeed, ensure_ascii=False) @@ -5670,12 +5678,14 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return True else: + print('Inbox test 9') if debug: nickname = path.replace('/users/', '') nickname = nickname.replace('/inbox', '') print('DEBUG: ' + nickname + ' was not authorized to access ' + path) if path != '/inbox': + print('Inbox test 10') # not the shared inbox if debug: print('DEBUG: GET access to inbox is unauthorized') From 4d7be05ffe41518cfaf6ee97c29088e9616e8c29 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:39:28 +0100 Subject: [PATCH 55/63] flush --- daemon.py | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon.py b/daemon.py index 61b3b10c8..5964bb08f 100644 --- a/daemon.py +++ b/daemon.py @@ -9149,6 +9149,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.translate).encode('utf-8') self._login_headers('text/html', len(msg), callingDomain) self._write(msg) + self.wfile.flush() # restore headers for headerField in currHeaders: From 5b3d674886a6378dfede7aea385452ea53c69cf7 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 15:44:32 +0100 Subject: [PATCH 56/63] Clear screen before post --- daemon.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 5964bb08f..99e90a777 100644 --- a/daemon.py +++ b/daemon.py @@ -9334,7 +9334,11 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True - self._showSendingScreen(authorized, callingDomain) + # self._showSendingScreen(authorized, callingDomain) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) if postType == 'newpost': messageJson = \ From 1c0770120c7c213808b30819120064c201cae8bf Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 16:10:08 +0100 Subject: [PATCH 57/63] Remove wait screen --- daemon.py | 205 ------------------------------------------- translations/ar.json | 4 +- translations/ca.json | 4 +- translations/cy.json | 4 +- translations/de.json | 4 +- translations/en.json | 4 +- translations/es.json | 4 +- translations/fr.json | 4 +- translations/ga.json | 4 +- translations/hi.json | 4 +- translations/it.json | 4 +- translations/ja.json | 4 +- translations/oc.json | 4 +- translations/pt.json | 4 +- translations/ru.json | 4 +- translations/zh.json | 4 +- webinterface.py | 21 ----- 17 files changed, 15 insertions(+), 271 deletions(-) diff --git a/daemon.py b/daemon.py index 99e90a777..4881fca5c 100644 --- a/daemon.py +++ b/daemon.py @@ -152,7 +152,6 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked -from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare @@ -9133,62 +9132,6 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) - def _showSendingScreen(self, authorized: bool, callingDomain: str) -> None: - """Shows a 'sending post' wait screen - """ - if self.server.defaultTimeline != 'inbox': - return - if not self._requestHTTP(): - return - # make a copy of the headers - currHeaders = self.headers.items() - - # show a sending post screen - msg = \ - htmlSendingPost(self.server.baseDir, - self.server.translate).encode('utf-8') - self._login_headers('text/html', len(msg), callingDomain) - self._write(msg) - self.wfile.flush() - - # restore headers - for headerField in currHeaders: - fieldName = headerField[0] - fieldValue = headerField[1] - self.headers[fieldName] = fieldValue - - def _clearSendingScreen(self, authorized: bool, - boxName: str, path: str, - callingDomain: str, cookie: str) -> None: - """Redirects to the given box - """ - if boxName != 'inbox': - return - if not self._requestHTTP(): - return - self._showInbox(authorized, - callingDomain, self.path, - self.server.baseDir, - self.server.httpPrefix, - self.server.domain, - self.server.domainFull, - self.server.port, - self.server.onionDomain, - self.server.i2pDomain, - None, None, - self.server.proxyType, - cookie, self.server.debug, - self.server.recentPostsCache, - self.server.session, - self.server.defaultTimeline, - self.server.maxRecentPosts, - self.server.translate, - self.server.cachedWebfingers, - self.server.personCache, - self.server.allowDeletion, - self.server.projectVersion, - self.server.YTReplacementDomain) - def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, callingDomain: str, cookie: str, @@ -9334,12 +9277,6 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True - # self._showSendingScreen(authorized, callingDomain) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) - if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -9358,10 +9295,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9371,16 +9304,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9398,10 +9323,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9410,16 +9331,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9488,10 +9401,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9499,9 +9408,6 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9523,10 +9429,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9535,16 +9437,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9568,10 +9462,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9580,16 +9470,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9640,22 +9522,10 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9683,10 +9553,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9697,16 +9563,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9733,32 +9591,16 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9777,28 +9619,13 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 if not fields.get('message'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9807,9 +9634,6 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9829,42 +9653,19 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('category'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('location'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('duration'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9886,13 +9687,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return 1 - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, diff --git a/translations/ar.json b/translations/ar.json index 8bc009ea1..b519fa594 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "علامات التجزئة المُنشأة تلقائيًا", "Autogenerated Content Warnings": "تحذيرات المحتوى المُنشأ تلقائيًا", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag محظور", - "Sending Post": "إرسال آخر", - "Please wait": "ارجوك انتظر" + "Hashtag Blocked": "Hashtag محظور" } diff --git a/translations/ca.json b/translations/ca.json index 051525ec8..62736ad2d 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogenerats", "Autogenerated Content Warnings": "Advertiments de contingut autogenerats", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloquejat", - "Sending Post": "S'està enviant el missatge", - "Please wait": "Espereu, si us plau" + "Hashtag Blocked": "Hashtag bloquejat" } diff --git a/translations/cy.json b/translations/cy.json index 3a04f108c..89f52d7ee 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags awtogeneiddiedig", "Autogenerated Content Warnings": "Rhybuddion Cynnwys Autogenerated", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag wedi'i Blocio", - "Sending Post": "Post Anfon", - "Please wait": "Arhoswch os gwelwch yn dda" + "Hashtag Blocked": "Hashtag wedi'i Blocio" } diff --git a/translations/de.json b/translations/de.json index d3d097525..e3bc529b6 100644 --- a/translations/de.json +++ b/translations/de.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Automatisch generierte Hashtags", "Autogenerated Content Warnings": "Warnungen vor automatisch generierten Inhalten", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag blockiert", - "Sending Post": "Post senden", - "Please wait": "Warten Sie mal" + "Hashtag Blocked": "Hashtag blockiert" } diff --git a/translations/en.json b/translations/en.json index 688d4d8a8..c85bbfe85 100644 --- a/translations/en.json +++ b/translations/en.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked", - "Sending Post": "Sending Post", - "Please wait": "Please wait" + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/es.json b/translations/es.json index fecc20223..6fe9c141b 100644 --- a/translations/es.json +++ b/translations/es.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogenerados", "Autogenerated Content Warnings": "Advertencias de contenido generado automáticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada", - "Sending Post": "Envío de publicación", - "Please wait": "por favor espera" + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/fr.json b/translations/fr.json index 53f4d7c3f..4a1fb6cb0 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags générés automatiquement", "Autogenerated Content Warnings": "Avertissements de contenu générés automatiquement", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqué", - "Sending Post": "Envoi du message", - "Please wait": "S'il vous plaît, attendez" + "Hashtag Blocked": "Hashtag bloqué" } diff --git a/translations/ga.json b/translations/ga.json index 90392720b..1c1266bae 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags uathghinte", "Autogenerated Content Warnings": "Rabhaidh Ábhar Uathghinte", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocáilte", - "Sending Post": "Post Seolta", - "Please wait": "Fán le do thoil" + "Hashtag Blocked": "Hashtag Blocáilte" } diff --git a/translations/hi.json b/translations/hi.json index 7ca0f0055..bff59966f 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "ऑटोजेनरेटेड हैशटैग", "Autogenerated Content Warnings": "स्वतः प्राप्त सामग्री चेतावनी", "Indymedia": "Indymedia", - "Hashtag Blocked": "हैशटैग अवरुद्ध", - "Sending Post": "पोस्ट भेज रहा है", - "Please wait": "कृपया प्रतीक्षा कीजिये" + "Hashtag Blocked": "हैशटैग अवरुद्ध" } diff --git a/translations/it.json b/translations/it.json index 5e9536646..08ff2618c 100644 --- a/translations/it.json +++ b/translations/it.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtag generati automaticamente", "Autogenerated Content Warnings": "Avvisi sui contenuti generati automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloccato", - "Sending Post": "Invio di post", - "Please wait": "attendere prego" + "Hashtag Blocked": "Hashtag bloccato" } diff --git a/translations/ja.json b/translations/ja.json index 3c5cc3fc9..382b43cc3 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "自動生成されたハッシュタグ", "Autogenerated Content Warnings": "自動生成されたコンテンツの警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "ハッシュタグがブロックされました", - "Sending Post": "投稿を送信する", - "Please wait": "お待ちください" + "Hashtag Blocked": "ハッシュタグがブロックされました" } diff --git a/translations/oc.json b/translations/oc.json index edf65b994..9a0cf46e9 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -283,7 +283,5 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked", - "Sending Post": "Sending Post", - "Please wait": "Please wait" + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/pt.json b/translations/pt.json index b2c82b634..72314d67e 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogeradas", "Autogenerated Content Warnings": "Avisos de conteúdo gerado automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada", - "Sending Post": "Enviando postagem", - "Please wait": "Por favor, espere" + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/ru.json b/translations/ru.json index 2308cddc7..cb7a0b211 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Автоматически сгенерированные хештеги", "Autogenerated Content Warnings": "Автоматические предупреждения о содержании", "Indymedia": "Indymedia", - "Hashtag Blocked": "Хештег заблокирован", - "Sending Post": "Отправка сообщения", - "Please wait": "пожалуйста, подождите" + "Hashtag Blocked": "Хештег заблокирован" } diff --git a/translations/zh.json b/translations/zh.json index f572b9c3f..a171135f0 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "自动生成的标签", "Autogenerated Content Warnings": "自动生成的内容警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "标签被阻止", - "Sending Post": "发送帖子", - "Please wait": "请稍候" + "Hashtag Blocked": "标签被阻止" } diff --git a/webinterface.py b/webinterface.py index a30c232db..200ae4c1b 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2006,27 +2006,6 @@ def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: return blockedHashtagForm -def htmlSendingPost(baseDir: str, translate: {}) -> str: - """A wait screen while sending a post - """ - sendForm = '' - cssFilename = baseDir + '/epicyon-send.css' - if os.path.isfile(baseDir + '/send.css'): - cssFilename = baseDir + '/send.css' - with open(cssFilename, 'r') as cssFile: - sendingCSS = cssFile.read() - sendForm = htmlHeader(cssFilename, sendingCSS) - sendForm += '
\n' - sendForm += \ - '

' + \ - translate['Sending Post'] + '

\n' - sendForm += \ - '

' + translate['Please wait'] + '

\n' - sendForm += '
\n' - sendForm += htmlFooter() - return sendForm - - def htmlSuspended(baseDir: str) -> str: """Show the screen for suspended accounts """ From bb391c8f98a515fce92339df7571fc5568f67aa8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 17:25:22 +0100 Subject: [PATCH 58/63] Revert "Remove wait screen" This reverts commit 1c0770120c7c213808b30819120064c201cae8bf. --- daemon.py | 205 +++++++++++++++++++++++++++++++++++++++++++ translations/ar.json | 4 +- translations/ca.json | 4 +- translations/cy.json | 4 +- translations/de.json | 4 +- translations/en.json | 4 +- translations/es.json | 4 +- translations/fr.json | 4 +- translations/ga.json | 4 +- translations/hi.json | 4 +- translations/it.json | 4 +- translations/ja.json | 4 +- translations/oc.json | 4 +- translations/pt.json | 4 +- translations/ru.json | 4 +- translations/zh.json | 4 +- webinterface.py | 21 +++++ 17 files changed, 271 insertions(+), 15 deletions(-) diff --git a/daemon.py b/daemon.py index 4881fca5c..99e90a777 100644 --- a/daemon.py +++ b/daemon.py @@ -152,6 +152,7 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked +from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare @@ -9132,6 +9133,62 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) + def _showSendingScreen(self, authorized: bool, callingDomain: str) -> None: + """Shows a 'sending post' wait screen + """ + if self.server.defaultTimeline != 'inbox': + return + if not self._requestHTTP(): + return + # make a copy of the headers + currHeaders = self.headers.items() + + # show a sending post screen + msg = \ + htmlSendingPost(self.server.baseDir, + self.server.translate).encode('utf-8') + self._login_headers('text/html', len(msg), callingDomain) + self._write(msg) + self.wfile.flush() + + # restore headers + for headerField in currHeaders: + fieldName = headerField[0] + fieldValue = headerField[1] + self.headers[fieldName] = fieldValue + + def _clearSendingScreen(self, authorized: bool, + boxName: str, path: str, + callingDomain: str, cookie: str) -> None: + """Redirects to the given box + """ + if boxName != 'inbox': + return + if not self._requestHTTP(): + return + self._showInbox(authorized, + callingDomain, self.path, + self.server.baseDir, + self.server.httpPrefix, + self.server.domain, + self.server.domainFull, + self.server.port, + self.server.onionDomain, + self.server.i2pDomain, + None, None, + self.server.proxyType, + cookie, self.server.debug, + self.server.recentPostsCache, + self.server.session, + self.server.defaultTimeline, + self.server.maxRecentPosts, + self.server.translate, + self.server.cachedWebfingers, + self.server.personCache, + self.server.allowDeletion, + self.server.projectVersion, + self.server.YTReplacementDomain) + def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, callingDomain: str, cookie: str, @@ -9277,6 +9334,12 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True + # self._showSendingScreen(authorized, callingDomain) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) + if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -9295,6 +9358,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9304,8 +9371,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9323,6 +9398,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9331,8 +9410,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9401,6 +9488,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9408,6 +9499,9 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9429,6 +9523,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9437,8 +9535,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9462,6 +9568,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9470,8 +9580,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9522,10 +9640,22 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9553,6 +9683,10 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9563,8 +9697,16 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9591,16 +9733,32 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9619,13 +9777,28 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 else: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 if not fields.get('message'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9634,6 +9807,9 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9653,19 +9829,42 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return 1 + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('category'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('location'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if not fields.get('duration'): + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, + cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9687,7 +9886,13 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return 1 + self._clearSendingScreen(authorized, + self.server.defaultTimeline, + self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, diff --git a/translations/ar.json b/translations/ar.json index b519fa594..8bc009ea1 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "علامات التجزئة المُنشأة تلقائيًا", "Autogenerated Content Warnings": "تحذيرات المحتوى المُنشأ تلقائيًا", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag محظور" + "Hashtag Blocked": "Hashtag محظور", + "Sending Post": "إرسال آخر", + "Please wait": "ارجوك انتظر" } diff --git a/translations/ca.json b/translations/ca.json index 62736ad2d..051525ec8 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogenerats", "Autogenerated Content Warnings": "Advertiments de contingut autogenerats", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloquejat" + "Hashtag Blocked": "Hashtag bloquejat", + "Sending Post": "S'està enviant el missatge", + "Please wait": "Espereu, si us plau" } diff --git a/translations/cy.json b/translations/cy.json index 89f52d7ee..3a04f108c 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags awtogeneiddiedig", "Autogenerated Content Warnings": "Rhybuddion Cynnwys Autogenerated", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag wedi'i Blocio" + "Hashtag Blocked": "Hashtag wedi'i Blocio", + "Sending Post": "Post Anfon", + "Please wait": "Arhoswch os gwelwch yn dda" } diff --git a/translations/de.json b/translations/de.json index e3bc529b6..d3d097525 100644 --- a/translations/de.json +++ b/translations/de.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Automatisch generierte Hashtags", "Autogenerated Content Warnings": "Warnungen vor automatisch generierten Inhalten", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag blockiert" + "Hashtag Blocked": "Hashtag blockiert", + "Sending Post": "Post senden", + "Please wait": "Warten Sie mal" } diff --git a/translations/en.json b/translations/en.json index c85bbfe85..688d4d8a8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked" + "Hashtag Blocked": "Hashtag Blocked", + "Sending Post": "Sending Post", + "Please wait": "Please wait" } diff --git a/translations/es.json b/translations/es.json index 6fe9c141b..fecc20223 100644 --- a/translations/es.json +++ b/translations/es.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogenerados", "Autogenerated Content Warnings": "Advertencias de contenido generado automáticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada" + "Hashtag Blocked": "Hashtag bloqueada", + "Sending Post": "Envío de publicación", + "Please wait": "por favor espera" } diff --git a/translations/fr.json b/translations/fr.json index 4a1fb6cb0..53f4d7c3f 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags générés automatiquement", "Autogenerated Content Warnings": "Avertissements de contenu générés automatiquement", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqué" + "Hashtag Blocked": "Hashtag bloqué", + "Sending Post": "Envoi du message", + "Please wait": "S'il vous plaît, attendez" } diff --git a/translations/ga.json b/translations/ga.json index 1c1266bae..90392720b 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags uathghinte", "Autogenerated Content Warnings": "Rabhaidh Ábhar Uathghinte", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocáilte" + "Hashtag Blocked": "Hashtag Blocáilte", + "Sending Post": "Post Seolta", + "Please wait": "Fán le do thoil" } diff --git a/translations/hi.json b/translations/hi.json index bff59966f..7ca0f0055 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "ऑटोजेनरेटेड हैशटैग", "Autogenerated Content Warnings": "स्वतः प्राप्त सामग्री चेतावनी", "Indymedia": "Indymedia", - "Hashtag Blocked": "हैशटैग अवरुद्ध" + "Hashtag Blocked": "हैशटैग अवरुद्ध", + "Sending Post": "पोस्ट भेज रहा है", + "Please wait": "कृपया प्रतीक्षा कीजिये" } diff --git a/translations/it.json b/translations/it.json index 08ff2618c..5e9536646 100644 --- a/translations/it.json +++ b/translations/it.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtag generati automaticamente", "Autogenerated Content Warnings": "Avvisi sui contenuti generati automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloccato" + "Hashtag Blocked": "Hashtag bloccato", + "Sending Post": "Invio di post", + "Please wait": "attendere prego" } diff --git a/translations/ja.json b/translations/ja.json index 382b43cc3..3c5cc3fc9 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "自動生成されたハッシュタグ", "Autogenerated Content Warnings": "自動生成されたコンテンツの警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "ハッシュタグがブロックされました" + "Hashtag Blocked": "ハッシュタグがブロックされました", + "Sending Post": "投稿を送信する", + "Please wait": "お待ちください" } diff --git a/translations/oc.json b/translations/oc.json index 9a0cf46e9..edf65b994 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -283,5 +283,7 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked" + "Hashtag Blocked": "Hashtag Blocked", + "Sending Post": "Sending Post", + "Please wait": "Please wait" } diff --git a/translations/pt.json b/translations/pt.json index 72314d67e..b2c82b634 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Hashtags autogeradas", "Autogenerated Content Warnings": "Avisos de conteúdo gerado automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada" + "Hashtag Blocked": "Hashtag bloqueada", + "Sending Post": "Enviando postagem", + "Please wait": "Por favor, espere" } diff --git a/translations/ru.json b/translations/ru.json index cb7a0b211..2308cddc7 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "Автоматически сгенерированные хештеги", "Autogenerated Content Warnings": "Автоматические предупреждения о содержании", "Indymedia": "Indymedia", - "Hashtag Blocked": "Хештег заблокирован" + "Hashtag Blocked": "Хештег заблокирован", + "Sending Post": "Отправка сообщения", + "Please wait": "пожалуйста, подождите" } diff --git a/translations/zh.json b/translations/zh.json index a171135f0..f572b9c3f 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -287,5 +287,7 @@ "Autogenerated Hashtags": "自动生成的标签", "Autogenerated Content Warnings": "自动生成的内容警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "标签被阻止" + "Hashtag Blocked": "标签被阻止", + "Sending Post": "发送帖子", + "Please wait": "请稍候" } diff --git a/webinterface.py b/webinterface.py index 200ae4c1b..a30c232db 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2006,6 +2006,27 @@ def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: return blockedHashtagForm +def htmlSendingPost(baseDir: str, translate: {}) -> str: + """A wait screen while sending a post + """ + sendForm = '' + cssFilename = baseDir + '/epicyon-send.css' + if os.path.isfile(baseDir + '/send.css'): + cssFilename = baseDir + '/send.css' + with open(cssFilename, 'r') as cssFile: + sendingCSS = cssFile.read() + sendForm = htmlHeader(cssFilename, sendingCSS) + sendForm += '
\n' + sendForm += \ + '

' + \ + translate['Sending Post'] + '

\n' + sendForm += \ + '

' + translate['Please wait'] + '

\n' + sendForm += '
\n' + sendForm += htmlFooter() + return sendForm + + def htmlSuspended(baseDir: str) -> str: """Show the screen for suspended accounts """ From 6111170dfefd6426efc5c0d19fc603036b47acb1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 17:33:39 +0100 Subject: [PATCH 59/63] Simplify wait screen --- daemon.py | 211 ++---------------------------------------------------- 1 file changed, 7 insertions(+), 204 deletions(-) diff --git a/daemon.py b/daemon.py index 99e90a777..ece9c72c1 100644 --- a/daemon.py +++ b/daemon.py @@ -9133,62 +9133,6 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers_head(mediaFileType, fileLength, etag, callingDomain) - def _showSendingScreen(self, authorized: bool, callingDomain: str) -> None: - """Shows a 'sending post' wait screen - """ - if self.server.defaultTimeline != 'inbox': - return - if not self._requestHTTP(): - return - # make a copy of the headers - currHeaders = self.headers.items() - - # show a sending post screen - msg = \ - htmlSendingPost(self.server.baseDir, - self.server.translate).encode('utf-8') - self._login_headers('text/html', len(msg), callingDomain) - self._write(msg) - self.wfile.flush() - - # restore headers - for headerField in currHeaders: - fieldName = headerField[0] - fieldValue = headerField[1] - self.headers[fieldName] = fieldValue - - def _clearSendingScreen(self, authorized: bool, - boxName: str, path: str, - callingDomain: str, cookie: str) -> None: - """Redirects to the given box - """ - if boxName != 'inbox': - return - if not self._requestHTTP(): - return - self._showInbox(authorized, - callingDomain, self.path, - self.server.baseDir, - self.server.httpPrefix, - self.server.domain, - self.server.domainFull, - self.server.port, - self.server.onionDomain, - self.server.i2pDomain, - None, None, - self.server.proxyType, - cookie, self.server.debug, - self.server.recentPostsCache, - self.server.session, - self.server.defaultTimeline, - self.server.maxRecentPosts, - self.server.translate, - self.server.cachedWebfingers, - self.server.personCache, - self.server.allowDeletion, - self.server.projectVersion, - self.server.YTReplacementDomain) - def _receiveNewPostProcess(self, postType: str, path: str, headers: {}, length: int, postBytes, boundary: str, callingDomain: str, cookie: str, @@ -9334,12 +9278,6 @@ class PubServer(BaseHTTPRequestHandler): else: privateEvent = True - # self._showSendingScreen(authorized, callingDomain) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) - if postType == 'newpost': messageJson = \ createPublicPost(self.server.baseDir, @@ -9358,10 +9296,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): @@ -9371,16 +9305,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newblog': messageJson = \ @@ -9398,10 +9324,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9410,16 +9332,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'editblogpost': print('Edited blog post received') @@ -9488,10 +9402,6 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain) saveJson(postJsonObject, postFilename) print('Edited blog post, resaved ' + postFilename) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: print('Edited blog post, unable to load json for ' + @@ -9499,9 +9409,6 @@ class PubServer(BaseHTTPRequestHandler): else: print('Edited blog post not found ' + str(fields['postUrl'])) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 elif postType == 'newunlisted': messageJson = \ @@ -9523,10 +9430,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9535,16 +9438,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newfollowers': messageJson = \ @@ -9568,10 +9463,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -9580,16 +9471,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newevent': # A Mobilizon-type event is posted @@ -9640,22 +9523,10 @@ class PubServer(BaseHTTPRequestHandler): fields['ticketUrl']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newdm': messageJson = None @@ -9683,10 +9554,6 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 print('Sending new DM to ' + str(messageJson['object']['to'])) @@ -9697,16 +9564,8 @@ class PubServer(BaseHTTPRequestHandler): messageJson, self.server.maxReplies, self.server.debug) - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newreminder': messageJson = None @@ -9733,32 +9592,16 @@ class PubServer(BaseHTTPRequestHandler): fields['location']) if messageJson: if fields['schedulePost']: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 print('DEBUG: new reminder to ' + str(messageJson['object']['to'])) if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newreport': if attachmentMediaType: if attachmentMediaType != 'image': - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 # So as to be sure that this only goes to moderators # and not accounts being reported we disable any @@ -9777,28 +9620,13 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, fields['subject']) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 else: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 elif postType == 'newquestion': if not fields.get('duration'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 if not fields.get('message'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 # questionStr = fields['message'] qOptions = [] @@ -9807,9 +9635,6 @@ class PubServer(BaseHTTPRequestHandler): qOptions.append(fields['questionOption' + str(questionCtr)]) if not qOptions: - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 messageJson = \ createQuestionPost(self.server.baseDir, @@ -9829,42 +9654,19 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: new Question') if self._postToOutbox(messageJson, __version__, nickname): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return 1 - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 elif postType == 'newshare': if not fields.get('itemType'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('category'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('location'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if not fields.get('duration'): - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 if attachmentMediaType: if attachmentMediaType != 'image': - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, - cookie) return -1 durationStr = fields['duration'] if durationStr: @@ -9886,13 +9688,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(filename): os.remove(filename) self.postToNickname = nickname - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return 1 - self._clearSendingScreen(authorized, - self.server.defaultTimeline, - self.path, callingDomain, cookie) return -1 def _receiveNewPost(self, postType: str, path: str, @@ -9915,6 +9711,13 @@ class PubServer(BaseHTTPRequestHandler): print('New post begins: ' + postType + ' ' + path) + msg = \ + htmlSendingPost(self.server.baseDir, + self.server.translate).encode('utf-8') + self._login_headers('text/html', len(msg), callingDomain) + self._write(msg) + self.wfile.flush() + if '?page=' in path: pageNumberStr = path.split('?page=')[1] if '?' in pageNumberStr: From 7bc08225538ae754bdbda03a8db2f008077b7f50 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 17:38:40 +0100 Subject: [PATCH 60/63] Remove debug --- daemon.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/daemon.py b/daemon.py index ece9c72c1..de3b608d3 100644 --- a/daemon.py +++ b/daemon.py @@ -5581,11 +5581,8 @@ class PubServer(BaseHTTPRequestHandler): YTReplacementDomain: str) -> bool: """Shows the inbox timeline """ - print('Inbox test 1') if '/users/' in path: - print('Inbox test 2') if authorized: - print('Inbox test 3') inboxFeed = \ personBoxJson(recentPostsCache, session, @@ -5597,13 +5594,11 @@ class PubServer(BaseHTTPRequestHandler): maxPostsInFeed, 'inbox', authorized) if inboxFeed: - print('Inbox test 4') if GETstartTime: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', 'show inbox json') if self._requestHTTP(): - print('Inbox test 5') nickname = path.replace('/users/', '') nickname = nickname.replace('/inbox', '') pageNumber = 1 @@ -5615,7 +5610,6 @@ class PubServer(BaseHTTPRequestHandler): else: pageNumber = 1 if 'page=' not in path: - print('Inbox test 6') # if no page was specified then show the first inboxFeed = \ personBoxJson(recentPostsCache, @@ -5656,7 +5650,6 @@ class PubServer(BaseHTTPRequestHandler): 'show inbox html') if msg: - print('Inbox test 7') msg = msg.encode('utf-8') self._set_headers('text/html', len(msg), cookie, callingDomain) @@ -5667,7 +5660,6 @@ class PubServer(BaseHTTPRequestHandler): 'show status done', 'show inbox') else: - print('Inbox test 8') # don't need authenticated fetch here because # there is already the authorization check msg = json.dumps(inboxFeed, ensure_ascii=False) @@ -5678,14 +5670,12 @@ class PubServer(BaseHTTPRequestHandler): self.server.GETbusy = False return True else: - print('Inbox test 9') if debug: nickname = path.replace('/users/', '') nickname = nickname.replace('/inbox', '') print('DEBUG: ' + nickname + ' was not authorized to access ' + path) if path != '/inbox': - print('Inbox test 10') # not the shared inbox if debug: print('DEBUG: GET access to inbox is unauthorized') From 71a2799397867f7acf8d5e5029bac5fcd0cd293e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 17:52:49 +0100 Subject: [PATCH 61/63] Send headers without response --- daemon.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index de3b608d3..90e184b02 100644 --- a/daemon.py +++ b/daemon.py @@ -483,6 +483,16 @@ class PubServer(BaseHTTPRequestHandler): return True return False + def _headers_without_response(self, fileFormat: str, length: int, + callingDomain: str) -> None: + self.send_header('Content-type', fileFormat) + self.send_header('Content-Length', str(length)) + self.send_header('Host', callingDomain) + self.send_header('WWW-Authenticate', + 'title="Login to Epicyon", Basic realm="epicyon"') + self.send_header('X-Robots-Tag', 'noindex') + self.end_headers() + def _login_headers(self, fileFormat: str, length: int, callingDomain: str) -> None: self.send_response(200) @@ -9704,7 +9714,7 @@ class PubServer(BaseHTTPRequestHandler): msg = \ htmlSendingPost(self.server.baseDir, self.server.translate).encode('utf-8') - self._login_headers('text/html', len(msg), callingDomain) + self._headers_without_response('text/html', len(msg), callingDomain) self._write(msg) self.wfile.flush() From 6b09ff0edda4bb87d4fb8264c80bc6a41d2a8485 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 18:03:54 +0100 Subject: [PATCH 62/63] Wait screen isn't possible without keeping the socket open and getting into fancy javascript stuff --- daemon.py | 18 ---------- epicyon-send.css | 86 -------------------------------------------- theme.py | 2 +- translations/ar.json | 4 +-- translations/ca.json | 4 +-- translations/cy.json | 4 +-- translations/de.json | 4 +-- translations/en.json | 4 +-- translations/es.json | 4 +-- translations/fr.json | 4 +-- translations/ga.json | 4 +-- translations/hi.json | 4 +-- translations/it.json | 4 +-- translations/ja.json | 4 +-- translations/oc.json | 4 +-- translations/pt.json | 4 +-- translations/ru.json | 4 +-- translations/zh.json | 4 +-- webinterface.py | 21 ----------- 19 files changed, 16 insertions(+), 171 deletions(-) delete mode 100644 epicyon-send.css diff --git a/daemon.py b/daemon.py index 90e184b02..ed6278765 100644 --- a/daemon.py +++ b/daemon.py @@ -152,7 +152,6 @@ from webinterface import rssHashtagSearch from webinterface import htmlModerationInfo from webinterface import htmlSearchSharedItems from webinterface import htmlHashtagBlocked -from webinterface import htmlSendingPost from shares import getSharesFeedForPerson from shares import addShare from shares import removeShare @@ -483,16 +482,6 @@ class PubServer(BaseHTTPRequestHandler): return True return False - def _headers_without_response(self, fileFormat: str, length: int, - callingDomain: str) -> None: - self.send_header('Content-type', fileFormat) - self.send_header('Content-Length', str(length)) - self.send_header('Host', callingDomain) - self.send_header('WWW-Authenticate', - 'title="Login to Epicyon", Basic realm="epicyon"') - self.send_header('X-Robots-Tag', 'noindex') - self.end_headers() - def _login_headers(self, fileFormat: str, length: int, callingDomain: str) -> None: self.send_response(200) @@ -9711,13 +9700,6 @@ class PubServer(BaseHTTPRequestHandler): print('New post begins: ' + postType + ' ' + path) - msg = \ - htmlSendingPost(self.server.baseDir, - self.server.translate).encode('utf-8') - self._headers_without_response('text/html', len(msg), callingDomain) - self._write(msg) - self.wfile.flush() - if '?page=' in path: pageNumberStr = path.split('?page=')[1] if '?' in pageNumberStr: diff --git a/epicyon-send.css b/epicyon-send.css deleted file mode 100644 index 4a5d03304..000000000 --- a/epicyon-send.css +++ /dev/null @@ -1,86 +0,0 @@ -@charset "UTF-8"; - -:root { - --main-bg-color: #282c37; - --link-bg-color: #282c37; - --main-bg-color-darker: #232c37; - --main-bg-color-report: #221c27; - --main-header-color-roles: #282237; - --main-fg-color: #dddddd; - --main-link-color: #999; - --main-visited-color: #888; - --border-color: #505050; - --font-size-header: 18px; - --font-color-header: #ccc; - --font-size: 22px; - --text-entry-foreground: #ccc; - --text-entry-background: #111; - --time-color: #aaa; - --button-text: #FFFFFF; - --button-background: #999; - --button-selected: #666; - --focus-color: white; -} - -@font-face { - font-family: 'Bedstead'; - font-style: italic; - font-weight: normal; - font-display: block; - src: url('./fonts/bedstead.otf') format('opentype'); -} -@font-face { - font-family: 'Bedstead'; - font-style: normal; - font-weight: normal; - font-display: block; - src: url('./fonts/bedstead.otf') format('opentype'); -} - -body, html { - background-color: var(--main-bg-color); - color: var(--main-fg-color); - - height: 100%; - font-family: Arial, Helvetica, sans-serif; - max-width: 80%; - min-width: 600px; - margin: 0 auto; - font-size: var(--font-size); -} - -a, u { - color: var(--main-fg-color); -} - -a:visited{ - color: var(--main-visited-color); - background: var(--link-bg-color); - font-weight: bold; -} - -a:link { - color: var(--main-link-color); - background: var(--link-bg-color); - font-weight: bold; -} - -a:focus { - border: 2px solid var(--focus-color); -} - -.screentitle { - font-size: 30px; - font-family: Arial, Helvetica, sans-serif; -} - -div { - height: 300px; - width: 400px; - background: var(--main-bg-color); - position: fixed; - top: 50%; - left: 50%; - margin-top: -100px; - margin-left: -200px; -} diff --git a/theme.py b/theme.py index c32d64a76..1c092e630 100644 --- a/theme.py +++ b/theme.py @@ -15,7 +15,7 @@ from shutil import copyfile def getThemeFiles() -> []: return ('epicyon.css', 'login.css', 'follow.css', 'suspended.css', 'calendar.css', 'blog.css', - 'options.css', 'search.css', 'send.css') + 'options.css', 'search.css') def getThemesList() -> []: diff --git a/translations/ar.json b/translations/ar.json index 8bc009ea1..b519fa594 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "علامات التجزئة المُنشأة تلقائيًا", "Autogenerated Content Warnings": "تحذيرات المحتوى المُنشأ تلقائيًا", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag محظور", - "Sending Post": "إرسال آخر", - "Please wait": "ارجوك انتظر" + "Hashtag Blocked": "Hashtag محظور" } diff --git a/translations/ca.json b/translations/ca.json index 051525ec8..62736ad2d 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogenerats", "Autogenerated Content Warnings": "Advertiments de contingut autogenerats", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloquejat", - "Sending Post": "S'està enviant el missatge", - "Please wait": "Espereu, si us plau" + "Hashtag Blocked": "Hashtag bloquejat" } diff --git a/translations/cy.json b/translations/cy.json index 3a04f108c..89f52d7ee 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags awtogeneiddiedig", "Autogenerated Content Warnings": "Rhybuddion Cynnwys Autogenerated", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag wedi'i Blocio", - "Sending Post": "Post Anfon", - "Please wait": "Arhoswch os gwelwch yn dda" + "Hashtag Blocked": "Hashtag wedi'i Blocio" } diff --git a/translations/de.json b/translations/de.json index d3d097525..e3bc529b6 100644 --- a/translations/de.json +++ b/translations/de.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Automatisch generierte Hashtags", "Autogenerated Content Warnings": "Warnungen vor automatisch generierten Inhalten", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag blockiert", - "Sending Post": "Post senden", - "Please wait": "Warten Sie mal" + "Hashtag Blocked": "Hashtag blockiert" } diff --git a/translations/en.json b/translations/en.json index 688d4d8a8..c85bbfe85 100644 --- a/translations/en.json +++ b/translations/en.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked", - "Sending Post": "Sending Post", - "Please wait": "Please wait" + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/es.json b/translations/es.json index fecc20223..6fe9c141b 100644 --- a/translations/es.json +++ b/translations/es.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogenerados", "Autogenerated Content Warnings": "Advertencias de contenido generado automáticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada", - "Sending Post": "Envío de publicación", - "Please wait": "por favor espera" + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/fr.json b/translations/fr.json index 53f4d7c3f..4a1fb6cb0 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags générés automatiquement", "Autogenerated Content Warnings": "Avertissements de contenu générés automatiquement", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqué", - "Sending Post": "Envoi du message", - "Please wait": "S'il vous plaît, attendez" + "Hashtag Blocked": "Hashtag bloqué" } diff --git a/translations/ga.json b/translations/ga.json index 90392720b..1c1266bae 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags uathghinte", "Autogenerated Content Warnings": "Rabhaidh Ábhar Uathghinte", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocáilte", - "Sending Post": "Post Seolta", - "Please wait": "Fán le do thoil" + "Hashtag Blocked": "Hashtag Blocáilte" } diff --git a/translations/hi.json b/translations/hi.json index 7ca0f0055..bff59966f 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "ऑटोजेनरेटेड हैशटैग", "Autogenerated Content Warnings": "स्वतः प्राप्त सामग्री चेतावनी", "Indymedia": "Indymedia", - "Hashtag Blocked": "हैशटैग अवरुद्ध", - "Sending Post": "पोस्ट भेज रहा है", - "Please wait": "कृपया प्रतीक्षा कीजिये" + "Hashtag Blocked": "हैशटैग अवरुद्ध" } diff --git a/translations/it.json b/translations/it.json index 5e9536646..08ff2618c 100644 --- a/translations/it.json +++ b/translations/it.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtag generati automaticamente", "Autogenerated Content Warnings": "Avvisi sui contenuti generati automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloccato", - "Sending Post": "Invio di post", - "Please wait": "attendere prego" + "Hashtag Blocked": "Hashtag bloccato" } diff --git a/translations/ja.json b/translations/ja.json index 3c5cc3fc9..382b43cc3 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "自動生成されたハッシュタグ", "Autogenerated Content Warnings": "自動生成されたコンテンツの警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "ハッシュタグがブロックされました", - "Sending Post": "投稿を送信する", - "Please wait": "お待ちください" + "Hashtag Blocked": "ハッシュタグがブロックされました" } diff --git a/translations/oc.json b/translations/oc.json index edf65b994..9a0cf46e9 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -283,7 +283,5 @@ "Autogenerated Hashtags": "Autogenerated Hashtags", "Autogenerated Content Warnings": "Autogenerated Content Warnings", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag Blocked", - "Sending Post": "Sending Post", - "Please wait": "Please wait" + "Hashtag Blocked": "Hashtag Blocked" } diff --git a/translations/pt.json b/translations/pt.json index b2c82b634..72314d67e 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Hashtags autogeradas", "Autogenerated Content Warnings": "Avisos de conteúdo gerado automaticamente", "Indymedia": "Indymedia", - "Hashtag Blocked": "Hashtag bloqueada", - "Sending Post": "Enviando postagem", - "Please wait": "Por favor, espere" + "Hashtag Blocked": "Hashtag bloqueada" } diff --git a/translations/ru.json b/translations/ru.json index 2308cddc7..cb7a0b211 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "Автоматически сгенерированные хештеги", "Autogenerated Content Warnings": "Автоматические предупреждения о содержании", "Indymedia": "Indymedia", - "Hashtag Blocked": "Хештег заблокирован", - "Sending Post": "Отправка сообщения", - "Please wait": "пожалуйста, подождите" + "Hashtag Blocked": "Хештег заблокирован" } diff --git a/translations/zh.json b/translations/zh.json index f572b9c3f..a171135f0 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -287,7 +287,5 @@ "Autogenerated Hashtags": "自动生成的标签", "Autogenerated Content Warnings": "自动生成的内容警告", "Indymedia": "Indymedia", - "Hashtag Blocked": "标签被阻止", - "Sending Post": "发送帖子", - "Please wait": "请稍候" + "Hashtag Blocked": "标签被阻止" } diff --git a/webinterface.py b/webinterface.py index a30c232db..200ae4c1b 100644 --- a/webinterface.py +++ b/webinterface.py @@ -2006,27 +2006,6 @@ def htmlHashtagBlocked(baseDir: str, translate: {}) -> str: return blockedHashtagForm -def htmlSendingPost(baseDir: str, translate: {}) -> str: - """A wait screen while sending a post - """ - sendForm = '' - cssFilename = baseDir + '/epicyon-send.css' - if os.path.isfile(baseDir + '/send.css'): - cssFilename = baseDir + '/send.css' - with open(cssFilename, 'r') as cssFile: - sendingCSS = cssFile.read() - sendForm = htmlHeader(cssFilename, sendingCSS) - sendForm += '
\n' - sendForm += \ - '

' + \ - translate['Sending Post'] + '

\n' - sendForm += \ - '

' + translate['Please wait'] + '

\n' - sendForm += '
\n' - sendForm += htmlFooter() - return sendForm - - def htmlSuspended(baseDir: str) -> str: """Show the screen for suspended accounts """ From c1a03228ceeb7eff9e17432ddb5266b329d36086 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 28 Sep 2020 18:11:48 +0100 Subject: [PATCH 63/63] Extra parameter --- person.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/person.py b/person.py index 2f63341da..d00724294 100644 --- a/person.py +++ b/person.py @@ -671,7 +671,7 @@ def personBoxJson(recentPostsCache: {}, elif boxname == 'moderation': return createModeration(baseDir, nickname, domain, port, httpPrefix, - noOfItems, headerOnly, authorized, + noOfItems, headerOnly, pageNumber) return None