From 939e053e5b7fd36929b42cecda12a895ba32ba55 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 22 Jun 2021 16:45:59 +0100 Subject: [PATCH] Function to check that activitypub posts have an object dictionary --- acceptreject.py | 3 +- announce.py | 5 +- blocking.py | 125 ++++++++++++------------- bookmarks.py | 21 +---- daemon.py | 44 ++++----- desktop_client.py | 36 +++----- follow.py | 5 +- git.py | 5 +- happening.py | 5 +- inbox.py | 227 +++++++++++++++++++--------------------------- like.py | 9 +- newswire.py | 9 +- outbox.py | 33 +++---- posts.py | 41 +++------ question.py | 9 +- schedule.py | 8 +- shares.py | 9 +- speaker.py | 5 +- utils.py | 53 ++++------- webapp_profile.py | 3 +- 20 files changed, 274 insertions(+), 381 deletions(-) diff --git a/acceptreject.py b/acceptreject.py index 5bbba45ec..fe88202ed 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -15,6 +15,7 @@ from utils import getDomainFromActor from utils import getNicknameFromActor from utils import domainPermitted from utils import followPerson +from utils import hasObjectDict def _createAcceptReject(baseDir: str, federationList: [], @@ -73,7 +74,7 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {}, federationList: [], debug: bool) -> None: """Receiving a follow Accept activity """ - if not messageJson.get('object'): + if not hasObjectDict(messageJson): return if not messageJson['object'].get('type'): return diff --git a/announce.py b/announce.py index 0f41fb0f1..0d61efb8a 100644 --- a/announce.py +++ b/announce.py @@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net" __status__ = "Production" __module_group__ = "ActivityPub" +from utils import hasObjectDict from utils import removeIdEnding from utils import hasUsersPath from utils import getFullDomain @@ -356,9 +357,7 @@ def outboxUndoAnnounce(recentPostsCache: {}, return if not messageJson['type'] == 'Undo': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): if debug: print('DEBUG: undo like object is not dict') return diff --git a/blocking.py b/blocking.py index 1ef3bed20..af139a8c3 100644 --- a/blocking.py +++ b/blocking.py @@ -11,6 +11,7 @@ import os import json import time from datetime import datetime +from utils import hasObjectDict from utils import isAccountDir from utils import getCachedPostFilename from utils import loadJson @@ -372,11 +373,7 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str, if debug: print('DEBUG: not an undo block') return - if not messageJson.get('object'): - if debug: - print('DEBUG: no object in undo block') - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): if debug: print('DEBUG: undo block object is not string') return @@ -444,41 +441,40 @@ def mutePost(baseDir: str, nickname: str, domain: str, port: int, if not postJsonObject: return - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - domainFull = getFullDomain(domain, port) - actor = httpPrefix + '://' + domainFull + '/users/' + nickname - # does this post have ignores on it from differenent actors? - if not postJsonObject['object'].get('ignores'): - if debug: - print('DEBUG: Adding initial mute to ' + postId) - ignoresJson = { - "@context": "https://www.w3.org/ns/activitystreams", - 'id': postId, - 'type': 'Collection', - "totalItems": 1, - 'items': [{ - 'type': 'Ignore', - 'actor': actor - }] - } - postJsonObject['object']['ignores'] = ignoresJson - else: - if not postJsonObject['object']['ignores'].get('items'): - postJsonObject['object']['ignores']['items'] = [] - itemsList = postJsonObject['object']['ignores']['items'] - for ignoresItem in itemsList: - if ignoresItem.get('actor'): - if ignoresItem['actor'] == actor: - return - newIgnore = { + if hasObjectDict(postJsonObject): + domainFull = getFullDomain(domain, port) + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + # does this post have ignores on it from differenent actors? + if not postJsonObject['object'].get('ignores'): + if debug: + print('DEBUG: Adding initial mute to ' + postId) + ignoresJson = { + "@context": "https://www.w3.org/ns/activitystreams", + 'id': postId, + 'type': 'Collection', + "totalItems": 1, + 'items': [{ 'type': 'Ignore', 'actor': actor - } - igIt = len(itemsList) - itemsList.append(newIgnore) - postJsonObject['object']['ignores']['totalItems'] = igIt - saveJson(postJsonObject, postFilename) + }] + } + postJsonObject['object']['ignores'] = ignoresJson + else: + if not postJsonObject['object']['ignores'].get('items'): + postJsonObject['object']['ignores']['items'] = [] + itemsList = postJsonObject['object']['ignores']['items'] + for ignoresItem in itemsList: + if ignoresItem.get('actor'): + if ignoresItem['actor'] == actor: + return + newIgnore = { + 'type': 'Ignore', + 'actor': actor + } + igIt = len(itemsList) + itemsList.append(newIgnore) + postJsonObject['object']['ignores']['totalItems'] = igIt + saveJson(postJsonObject, postFilename) # remove cached post so that the muted version gets recreated # without its content text and/or image @@ -525,31 +521,30 @@ def unmutePost(baseDir: str, nickname: str, domain: str, port: int, os.remove(muteFilename) print('UNMUTE: ' + muteFilename + ' file removed') - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('ignores'): - domainFull = getFullDomain(domain, port) - actor = httpPrefix + '://' + domainFull + '/users/' + nickname - totalItems = 0 - if postJsonObject['object']['ignores'].get('totalItems'): - totalItems = \ - postJsonObject['object']['ignores']['totalItems'] - itemsList = postJsonObject['object']['ignores']['items'] - for ignoresItem in itemsList: - if ignoresItem.get('actor'): - if ignoresItem['actor'] == actor: - if debug: - print('DEBUG: mute was removed for ' + actor) - itemsList.remove(ignoresItem) - break - if totalItems == 1: - if debug: - print('DEBUG: mute was removed from post') - del postJsonObject['object']['ignores'] - else: - igItLen = len(postJsonObject['object']['ignores']['items']) - postJsonObject['object']['ignores']['totalItems'] = igItLen - saveJson(postJsonObject, postFilename) + if hasObjectDict(postJsonObject): + if postJsonObject['object'].get('ignores'): + domainFull = getFullDomain(domain, port) + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + totalItems = 0 + if postJsonObject['object']['ignores'].get('totalItems'): + totalItems = \ + postJsonObject['object']['ignores']['totalItems'] + itemsList = postJsonObject['object']['ignores']['items'] + for ignoresItem in itemsList: + if ignoresItem.get('actor'): + if ignoresItem['actor'] == actor: + if debug: + print('DEBUG: mute was removed for ' + actor) + itemsList.remove(ignoresItem) + break + if totalItems == 1: + if debug: + print('DEBUG: mute was removed from post') + del postJsonObject['object']['ignores'] + else: + igItLen = len(postJsonObject['object']['ignores']['items']) + postJsonObject['object']['ignores']['totalItems'] = igItLen + saveJson(postJsonObject, postFilename) # remove cached post so that the muted version gets recreated # with its content text and/or image @@ -646,9 +641,7 @@ def outboxUndoMute(baseDir: str, httpPrefix: str, return if not messageJson['type'] == 'Undo': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return if not messageJson['object'].get('type'): return diff --git a/bookmarks.py b/bookmarks.py index a2b595d91..2e4de3c19 100644 --- a/bookmarks.py +++ b/bookmarks.py @@ -22,6 +22,7 @@ from utils import locatePost from utils import getCachedPostFilename from utils import loadJson from utils import saveJson +from utils import hasObjectDict from posts import getPersonBox from session import postJson @@ -68,13 +69,11 @@ def undoBookmarksCollectionEntry(recentPostsCache: {}, return if postJsonObject['type'] != 'Create': return - if not postJsonObject.get('object'): + if not hasObjectDict(postJsonObject): if debug: print('DEBUG: bookmarked post has no object ' + str(postJsonObject)) return - if not isinstance(postJsonObject['object'], dict): - return if not postJsonObject['object'].get('bookmarks'): return if not isinstance(postJsonObject['object']['bookmarks'], dict): @@ -123,9 +122,7 @@ def bookmarkedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool: def _noOfBookmarks(postJsonObject: {}) -> int: """Returns the number of bookmarks ona given post """ - if not postJsonObject.get('object'): - return 0 - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return 0 if not postJsonObject['object'].get('bookmarks'): return 0 @@ -527,7 +524,7 @@ def outboxBookmark(recentPostsCache: {}, if debug: print('DEBUG: no actor in bookmark Add') return - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: no object in bookmark Add') return @@ -535,10 +532,6 @@ def outboxBookmark(recentPostsCache: {}, if debug: print('DEBUG: no target in bookmark Add') return - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: bookmark Add object is not dict') - return if not messageJson['object'].get('type'): if debug: print('DEBUG: no object type in bookmark Add') @@ -596,7 +589,7 @@ def outboxUndoBookmark(recentPostsCache: {}, if debug: print('DEBUG: no actor in unbookmark Remove') return - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: no object in unbookmark Remove') return @@ -604,10 +597,6 @@ def outboxUndoBookmark(recentPostsCache: {}, if debug: print('DEBUG: no target in unbookmark Remove') return - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: unbookmark Remove object is not dict') - return if not messageJson['object'].get('type'): if debug: print('DEBUG: no object type in bookmark Remove') diff --git a/daemon.py b/daemon.py index 63393695f..b0a71aa35 100644 --- a/daemon.py +++ b/daemon.py @@ -208,6 +208,7 @@ from shares import addShare from shares import removeShare from shares import expireShares from categories import setHashtagCategory +from utils import hasObjectDict from utils import userAgentDomain from utils import isLocalNetworkAddress from utils import permittedDir @@ -1173,28 +1174,27 @@ class PubServer(BaseHTTPRequestHandler): self.server.POSTbusy = False return 3 - if messageJson.get('object'): - if isinstance(messageJson['object'], dict): - stringFields = ( - 'id', 'actor', 'type', 'content', 'published', - 'summary', 'url', 'attributedTo' - ) - for checkField in stringFields: - if not messageJson['object'].get(checkField): - continue - if not isinstance(messageJson['object'][checkField], str): - self._400() - self.server.POSTbusy = False - return 3 - # check that some fields are lists - listFields = ('to', 'cc', 'attachment') - for checkField in listFields: - if not messageJson['object'].get(checkField): - continue - if not isinstance(messageJson['object'][checkField], list): - self._400() - self.server.POSTbusy = False - return 3 + if hasObjectDict(messageJson): + stringFields = ( + 'id', 'actor', 'type', 'content', 'published', + 'summary', 'url', 'attributedTo' + ) + for checkField in stringFields: + if not messageJson['object'].get(checkField): + continue + if not isinstance(messageJson['object'][checkField], str): + self._400() + self.server.POSTbusy = False + return 3 + # check that some fields are lists + listFields = ('to', 'cc', 'attachment') + for checkField in listFields: + if not messageJson['object'].get(checkField): + continue + if not isinstance(messageJson['object'][checkField], list): + self._400() + self.server.POSTbusy = False + return 3 # actor should look like a url if '://' not in messageJson['actor'] or \ diff --git a/desktop_client.py b/desktop_client.py index d27e3d6b3..6382c4d1b 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -16,6 +16,7 @@ import webbrowser import urllib.parse from pathlib import Path from random import randint +from utils import hasObjectDict from utils import getFullDomain from utils import isDM from utils import loadTranslationsFromFile @@ -207,14 +208,13 @@ def _postIsToYou(actor: str, postJsonObject: {}) -> bool: if not toYourActor and postJsonObject.get('cc'): if actor in postJsonObject['cc']: toYourActor = True - if not toYourActor and postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('to'): - if actor in postJsonObject['object']['to']: - toYourActor = True - if not toYourActor and postJsonObject['object'].get('cc'): - if actor in postJsonObject['object']['cc']: - toYourActor = True + if not toYourActor and hasObjectDict(postJsonObject): + if postJsonObject['object'].get('to'): + if actor in postJsonObject['object']['to']: + toYourActor = True + if not toYourActor and postJsonObject['object'].get('cc'): + if actor in postJsonObject['object']['cc']: + toYourActor = True return toYourActor @@ -606,9 +606,7 @@ def _getImageDescription(postJsonObject: {}) -> str: def _showLikesOnPost(postJsonObject: {}, maxLikes: int) -> None: """Shows the likes on a post """ - if not postJsonObject.get('object'): - return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('likes'): return @@ -630,9 +628,7 @@ def _showLikesOnPost(postJsonObject: {}, maxLikes: int) -> None: def _showRepliesOnPost(postJsonObject: {}, maxReplies: int) -> None: """Shows the replies on a post """ - if not postJsonObject.get('object'): - return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('replies'): return @@ -693,7 +689,7 @@ def _readLocalBoxPost(session, nickname: str, domain: str, allowLocalNetworkAccess, recentPostsCache, False) if postJsonObject2: - if postJsonObject2.get('object'): + if hasObjectDict(postJsonObject2): if postJsonObject2['object'].get('attributedTo') and \ postJsonObject2['object'].get('content'): actor = postJsonObject2['object']['attributedTo'] @@ -879,7 +875,7 @@ def _desktopGetBoxPostObject(boxJson: {}, index: int) -> {}: if ctr == index: return postJsonObject continue - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): continue if not postJsonObject['object'].get('published'): continue @@ -991,9 +987,7 @@ def _desktopShowBox(indent: str, ctr += 1 continue - if not postJsonObject.get('object'): - continue - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): continue if not postJsonObject['object'].get('published'): continue @@ -1888,7 +1882,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, if postJsonObject: if postJsonObject.get('id') and \ postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): + if hasObjectDict(postJsonObject): if postJsonObject['object'].get('attributedTo'): blockActor = \ postJsonObject['object']['attributedTo'] @@ -1932,7 +1926,7 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, if postJsonObject and not blockActor: if postJsonObject.get('id') and \ postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): + if hasObjectDict(postJsonObject): if postJsonObject['object'].get('attributedTo'): blockActor = \ postJsonObject['object']['attributedTo'] diff --git a/follow.py b/follow.py index b635c25c3..a26b2372c 100644 --- a/follow.py +++ b/follow.py @@ -9,6 +9,7 @@ __module_group__ = "ActivityPub" from pprint import pprint import os +from utils import hasObjectDict from utils import hasUsersPath from utils import getFullDomain from utils import isSystemAccount @@ -1391,9 +1392,7 @@ def outboxUndoFollow(baseDir: str, messageJson: {}, debug: bool) -> None: return if not messageJson['type'] == 'Undo': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return if not messageJson['object'].get('type'): return diff --git a/git.py b/git.py index 71fdc1442..00f2c9a23 100644 --- a/git.py +++ b/git.py @@ -9,6 +9,7 @@ __module_group__ = "ActivityPub" import os import html +from utils import hasObjectDict def _gitFormatContent(content: str) -> str: @@ -113,9 +114,7 @@ def convertPostToPatch(baseDir: str, nickname: str, domain: str, """Detects whether the given post contains a patch and if so then converts it to a Patch ActivityPub type """ - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('type'): return False diff --git a/happening.py b/happening.py index 028da1416..28c9d6552 100644 --- a/happening.py +++ b/happening.py @@ -16,6 +16,7 @@ from utils import isPublicPost from utils import loadJson from utils import saveJson from utils import locatePost +from utils import hasObjectDict def _validUuid(testUuid: str, version=4): @@ -155,9 +156,7 @@ def _isHappeningPost(postJsonObject: {}) -> bool: """ if not postJsonObject: return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('tag'): return False diff --git a/inbox.py b/inbox.py index a87a15f53..0b1035b12 100644 --- a/inbox.py +++ b/inbox.py @@ -13,6 +13,7 @@ import datetime import time import random from linked_data_sig import verifyJsonSignature +from utils import hasObjectDict from utils import dmAllowedFromDomain from utils import isRecentPost from utils import getConfigParam @@ -91,9 +92,7 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None: """ if not isPublicPost(postJsonObject): return - if not postJsonObject.get('object'): - return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('tag'): return @@ -331,10 +330,8 @@ def inboxPermittedMessage(domain: str, messageJson: {}, alwaysAllowedTypes = ('Follow', 'Join', 'Like', 'Delete', 'Announce') if messageJson['type'] not in alwaysAllowedTypes: - if not messageJson.get('object'): + if not hasObjectDict(messageJson): return True - if not isinstance(messageJson['object'], dict): - return False if messageJson['object'].get('inReplyTo'): inReplyTo = messageJson['object']['inReplyTo'] if not isinstance(inReplyTo, str): @@ -390,40 +387,39 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str, return None postDomain = getFullDomain(postDomain, postPort) - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('inReplyTo'): - if isinstance(postJsonObject['object']['inReplyTo'], str): - inReplyTo = \ - postJsonObject['object']['inReplyTo'] - replyDomain, replyPort = \ - getDomainFromActor(inReplyTo) - if isBlockedDomain(baseDir, replyDomain, blockedCache): - if debug: - print('WARN: post contains reply from ' + - str(actor) + - ' to a blocked domain: ' + replyDomain) - return None - else: - replyNickname = \ - getNicknameFromActor(inReplyTo) - if replyNickname and replyDomain: - if isBlocked(baseDir, nickname, domain, - replyNickname, replyDomain, - blockedCache): - if debug: - print('WARN: post contains reply from ' + - str(actor) + - ' to a blocked account: ' + - replyNickname + '@' + replyDomain) - return None - if postJsonObject['object'].get('content'): - if isinstance(postJsonObject['object']['content'], str): - if isFiltered(baseDir, nickname, domain, - postJsonObject['object']['content']): - if debug: - print('WARN: post was filtered out due to content') - return None + if hasObjectDict(postJsonObject): + if postJsonObject['object'].get('inReplyTo'): + if isinstance(postJsonObject['object']['inReplyTo'], str): + inReplyTo = \ + postJsonObject['object']['inReplyTo'] + replyDomain, replyPort = \ + getDomainFromActor(inReplyTo) + if isBlockedDomain(baseDir, replyDomain, blockedCache): + if debug: + print('WARN: post contains reply from ' + + str(actor) + + ' to a blocked domain: ' + replyDomain) + return None + else: + replyNickname = \ + getNicknameFromActor(inReplyTo) + if replyNickname and replyDomain: + if isBlocked(baseDir, nickname, domain, + replyNickname, replyDomain, + blockedCache): + if debug: + print('WARN: post contains reply from ' + + str(actor) + + ' to a blocked account: ' + + replyNickname + '@' + replyDomain) + return None + if postJsonObject['object'].get('content'): + if isinstance(postJsonObject['object']['content'], str): + if isFiltered(baseDir, nickname, domain, + postJsonObject['object']['content']): + if debug: + print('WARN: post was filtered out due to content') + return None originalPostId = None if postJsonObject.get('id'): if not isinstance(postJsonObject['id'], str): @@ -550,51 +546,50 @@ def _inboxPostRecipients(baseDir: str, postJsonObject: {}, # first get any specific people which the post is addressed to followerRecipients = False - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('to'): - if isinstance(postJsonObject['object']['to'], list): - recipientsList = postJsonObject['object']['to'] - else: - recipientsList = [postJsonObject['object']['to']] - if debug: - print('DEBUG: resolving "to"') - includesFollowers, recipientsDict = \ - _inboxPostRecipientsAdd(baseDir, httpPrefix, - recipientsList, - recipientsDict, - domainMatch, domainBase, - actor, debug) - if includesFollowers: - followerRecipients = True + if hasObjectDict(postJsonObject): + if postJsonObject['object'].get('to'): + if isinstance(postJsonObject['object']['to'], list): + recipientsList = postJsonObject['object']['to'] else: - if debug: - print('DEBUG: inbox post has no "to"') - - if postJsonObject['object'].get('cc'): - if isinstance(postJsonObject['object']['cc'], list): - recipientsList = postJsonObject['object']['cc'] - else: - recipientsList = [postJsonObject['object']['cc']] - includesFollowers, recipientsDict = \ - _inboxPostRecipientsAdd(baseDir, httpPrefix, - recipientsList, - recipientsDict, - domainMatch, domainBase, - actor, debug) - if includesFollowers: - followerRecipients = True - else: - if debug: - print('DEBUG: inbox post has no cc') + recipientsList = [postJsonObject['object']['to']] + if debug: + print('DEBUG: resolving "to"') + includesFollowers, recipientsDict = \ + _inboxPostRecipientsAdd(baseDir, httpPrefix, + recipientsList, + recipientsDict, + domainMatch, domainBase, + actor, debug) + if includesFollowers: + followerRecipients = True else: if debug: - if isinstance(postJsonObject['object'], str): - if '/statuses/' in postJsonObject['object']: - print('DEBUG: inbox item is a link to a post') - else: - if '/users/' in postJsonObject['object']: - print('DEBUG: inbox item is a link to an actor') + print('DEBUG: inbox post has no "to"') + + if postJsonObject['object'].get('cc'): + if isinstance(postJsonObject['object']['cc'], list): + recipientsList = postJsonObject['object']['cc'] + else: + recipientsList = [postJsonObject['object']['cc']] + includesFollowers, recipientsDict = \ + _inboxPostRecipientsAdd(baseDir, httpPrefix, + recipientsList, + recipientsDict, + domainMatch, domainBase, + actor, debug) + if includesFollowers: + followerRecipients = True + else: + if debug: + print('DEBUG: inbox post has no cc') + else: + if debug and postJsonObject.get('object'): + if isinstance(postJsonObject['object'], str): + if '/statuses/' in postJsonObject['object']: + print('DEBUG: inbox item is a link to a post') + else: + if '/users/' in postJsonObject['object']: + print('DEBUG: inbox item is a link to an actor') if postJsonObject.get('to'): if isinstance(postJsonObject['to'], list): @@ -709,14 +704,10 @@ def _receiveUndo(session, baseDir: str, httpPrefix: str, if debug: print('DEBUG: "users" or "profile" missing from actor') return False - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: ' + messageJson['type'] + ' has no object') return False - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: ' + messageJson['type'] + ' object is not a dict') - return False if not messageJson['object'].get('type'): if debug: print('DEBUG: ' + messageJson['type'] + ' has no object type') @@ -884,14 +875,10 @@ def _receiveUpdate(recentPostsCache: {}, session, baseDir: str, if debug: print('DEBUG: ' + messageJson['type'] + ' has no actor') return False - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: ' + messageJson['type'] + ' has no object') return False - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: ' + messageJson['type'] + ' object is not a dict') - return False if not messageJson['object'].get('type'): if debug: print('DEBUG: ' + messageJson['type'] + ' object has no type') @@ -1031,9 +1018,7 @@ def _receiveUndoLike(recentPostsCache: {}, return False if not messageJson.get('actor'): return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return False if not messageJson['object'].get('type'): return False @@ -1095,7 +1080,7 @@ def _receiveBookmark(recentPostsCache: {}, if debug: print('DEBUG: no actor in inbox bookmark Add') return False - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: no object in inbox bookmark Add') return False @@ -1103,10 +1088,6 @@ def _receiveBookmark(recentPostsCache: {}, if debug: print('DEBUG: no target in inbox bookmark Add') return False - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: inbox bookmark Add object is not string') - return False if not messageJson['object'].get('type'): if debug: print('DEBUG: no object type in inbox bookmark Add') @@ -1174,7 +1155,7 @@ def _receiveUndoBookmark(recentPostsCache: {}, if debug: print('DEBUG: no actor in inbox undo bookmark Remove') return False - if not messageJson.get('object'): + if not hasObjectDict(messageJson): if debug: print('DEBUG: no object in inbox undo bookmark Remove') return False @@ -1182,10 +1163,6 @@ def _receiveUndoBookmark(recentPostsCache: {}, if debug: print('DEBUG: no target in inbox undo bookmark Remove') return False - if not isinstance(messageJson['object'], dict): - if debug: - print('DEBUG: inbox Remove bookmark object is not dict') - return False if not messageJson['object'].get('type'): if debug: print('DEBUG: no object type in inbox bookmark Remove') @@ -1439,12 +1416,11 @@ def _receiveAnnounce(recentPostsCache: {}, if isinstance(postJsonObject['attributedTo'], str): lookupActor = postJsonObject['attributedTo'] else: - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('attributedTo'): - attrib = postJsonObject['object']['attributedTo'] - if isinstance(attrib, str): - lookupActor = attrib + if hasObjectDict(postJsonObject): + if postJsonObject['object'].get('attributedTo'): + attrib = postJsonObject['object']['attributedTo'] + if isinstance(attrib, str): + lookupActor = attrib if lookupActor: if hasUsersPath(lookupActor): if '/statuses/' in lookupActor: @@ -1497,9 +1473,7 @@ def _receiveUndoAnnounce(recentPostsCache: {}, return False if not messageJson.get('actor'): return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return False if not messageJson['object'].get('object'): return False @@ -1548,9 +1522,9 @@ def jsonPostAllowsComments(postJsonObject: {}) -> bool: if 'commentsEnabled' in postJsonObject: return postJsonObject['commentsEnabled'] if postJsonObject.get('object'): - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False - if 'commentsEnabled' in postJsonObject['object']: + elif 'commentsEnabled' in postJsonObject['object']: return postJsonObject['object']['commentsEnabled'] return True @@ -1571,9 +1545,7 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str, """ if not messageJson.get('id'): return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return False if not messageJson['object'].get('inReplyTo'): return False @@ -1666,9 +1638,7 @@ def _validPostContent(baseDir: str, nickname: str, domain: str, Check for hellthreads Check number of tags is reasonable """ - if not messageJson.get('object'): - return True - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return True if not messageJson['object'].get('content'): return True @@ -1756,10 +1726,7 @@ def _obtainAvatarForReplyPost(session, baseDir: str, httpPrefix: str, """Tries to obtain the actor for the person being replied to so that their avatar can later be shown """ - if not postJsonObject.get('object'): - return - - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('inReplyTo'): @@ -1821,9 +1788,7 @@ def _alreadyLiked(baseDir: str, nickname: str, domain: str, postJsonObject = loadJson(postFilename, 1) if not postJsonObject: return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('likes'): return False @@ -2039,9 +2004,7 @@ def _inboxUpdateCalendar(baseDir: str, handle: str, """ if not postJsonObject.get('actor'): return - if not postJsonObject.get('object'): - return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('tag'): return diff --git a/like.py b/like.py index 7e5d51374..4c95ad7dd 100644 --- a/like.py +++ b/like.py @@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net" __status__ = "Production" __module_group__ = "ActivityPub" +from utils import hasObjectDict from utils import hasUsersPath from utils import getFullDomain from utils import removeIdEnding @@ -38,9 +39,7 @@ def likedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool: def noOfLikes(postJsonObject: {}) -> int: """Returns the number of likes ona given post """ - if not postJsonObject.get('object'): - return 0 - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return 0 if not postJsonObject['object'].get('likes'): return 0 @@ -354,9 +353,7 @@ def outboxUndoLike(recentPostsCache: {}, return if not messageJson['type'] == 'Undo': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): if debug: print('DEBUG: undo like object is not dict') return diff --git a/newswire.py b/newswire.py index 51c773994..12daecf28 100644 --- a/newswire.py +++ b/newswire.py @@ -18,6 +18,7 @@ from datetime import timezone from collections import OrderedDict from utils import validPostDate from categories import setHashtagCategory +from utils import hasObjectDict from utils import firstParagraphFromString from utils import isPublicPost from utils import locatePost @@ -839,9 +840,7 @@ def _isNewswireBlogPost(postJsonObject: {}) -> bool: """ if not postJsonObject: return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if postJsonObject['object'].get('summary') and \ postJsonObject['object'].get('url') and \ @@ -854,9 +853,7 @@ def _isNewswireBlogPost(postJsonObject: {}) -> bool: def _getHashtagsFromPost(postJsonObject: {}) -> []: """Returns a list of any hashtags within a post """ - if not postJsonObject.get('object'): - return [] - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return [] if not postJsonObject['object'].get('tag'): return [] diff --git a/outbox.py b/outbox.py index 84d3a873c..dae0e8be8 100644 --- a/outbox.py +++ b/outbox.py @@ -16,6 +16,7 @@ from posts import outboxMessageCreateWrap from posts import savePostToBox from posts import sendToFollowersThread from posts import sendToNamedAddresses +from utils import hasObjectDict from utils import getLocalNetworkAddresses from utils import getFullDomain from utils import removeIdEnding @@ -62,9 +63,7 @@ def _outboxPersonReceiveUpdate(recentPostsCache: {}, print("messageJson['type'] " + messageJson['type']) if messageJson['type'] != 'Update': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): if debug: print('DEBUG: c2s actor update object is not dict') return @@ -199,14 +198,13 @@ def postMessageToOutbox(session, translate: {}, # check that the outgoing post doesn't contain any markup # which can be used to implement exploits - if messageJson.get('object'): - if isinstance(messageJson['object'], dict): - if messageJson['object'].get('content'): - if dangerousMarkup(messageJson['object']['content'], - allowLocalNetworkAccess): - print('POST to outbox contains dangerous markup: ' + - str(messageJson)) - return False + if hasObjectDict(messageJson): + if messageJson['object'].get('content'): + if dangerousMarkup(messageJson['object']['content'], + allowLocalNetworkAccess): + print('POST to outbox contains dangerous markup: ' + + str(messageJson)) + return False if messageJson['type'] == 'Create': if not (messageJson.get('id') and @@ -335,13 +333,12 @@ def postMessageToOutbox(session, translate: {}, # if this is a blog post or an event then save to its own box if messageJson['type'] == 'Create': - if messageJson.get('object'): - if isinstance(messageJson['object'], dict): - if messageJson['object'].get('type'): - if messageJson['object']['type'] == 'Article': - outboxName = 'tlblogs' - elif messageJson['object']['type'] == 'Event': - outboxName = 'tlevents' + if hasObjectDict(messageJson): + if messageJson['object'].get('type'): + if messageJson['object']['type'] == 'Article': + outboxName = 'tlblogs' + elif messageJson['object']['type'] == 'Event': + outboxName = 'tlevents' savedFilename = \ savePostToBox(baseDir, diff --git a/posts.py b/posts.py index 0788f4a60..1cf12f793 100644 --- a/posts.py +++ b/posts.py @@ -32,6 +32,7 @@ from session import postImage from webfinger import webfingerHandle from httpsig import createSignedHeader from siteactive import siteIsActive +from utils import hasObjectDict from utils import rejectPostId from utils import removeInvalidChars from utils import fileLastModified @@ -361,11 +362,7 @@ def _getPosts(session, outboxUrl: str, maxPosts: int, if debug: print('Not Create type') continue - if not item.get('object'): - if debug: - print('No object') - continue - if not isinstance(item['object'], dict): + if not hasObjectDict(item): if debug: print('item object is not a dict') continue @@ -561,9 +558,7 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int, i += 1 if i > maxPosts: break - if not item.get('object'): - continue - if not isinstance(item['object'], dict): + if not hasObjectDict(item): continue if item['object'].get('content'): _updateWordFrequency(item['object']['content'], @@ -618,9 +613,7 @@ def _getPostsForBlockedDomains(baseDir: str, i += 1 if i > maxPosts: break - if not item.get('object'): - continue - if not isinstance(item['object'], dict): + if not hasObjectDict(item): continue if item['object'].get('inReplyTo'): if isinstance(item['object']['inReplyTo'], str): @@ -699,10 +692,9 @@ def savePostToBox(baseDir: str, httpPrefix: str, postId: str, httpPrefix + '://' + originalDomain + '/users/' + nickname + \ '/statuses/' + statusNumber postJsonObject['id'] = postId + '/activity' - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - postJsonObject['object']['id'] = postId - postJsonObject['object']['atomUri'] = postId + if hasObjectDict(postJsonObject): + postJsonObject['object']['id'] = postId + postJsonObject['object']['atomUri'] = postId boxDir = createPersonDir(nickname, domain, baseDir, boxname) filename = boxDir + '/' + postId.replace('/', '#') + '.json' @@ -2391,7 +2383,7 @@ def addToField(activityType: str, postJsonObject: {}, toAddress = toAddress.split('/statuses/')[0] postJsonObject['to'] = [toAddress] toFieldAdded = True - elif isinstance(postJsonObject['object'], dict): + elif hasObjectDict(postJsonObject): # add a to field to bookmark add or remove if postJsonObject.get('type') and \ postJsonObject.get('actor') and \ @@ -2590,9 +2582,7 @@ def _sendingProfileUpdate(postJsonObject: {}) -> bool: """ if postJsonObject['type'] != 'Update': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('type'): return False @@ -2970,9 +2960,7 @@ def isImageMedia(session, baseDir: str, httpPrefix: str, postJsonObject = postJsonAnnounce if postJsonObject['type'] != 'Create': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if postJsonObject['object'].get('moderationStatus'): return False @@ -3057,9 +3045,8 @@ def removePostInteractions(postJsonObject: {}, force: bool) -> bool: Returns False if this is a private post """ hasObject = False - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - hasObject = True + if hasObjectDict(postJsonObject): + hasObject = True if hasObject: postObj = postJsonObject['object'] if not force: @@ -3921,9 +3908,7 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str, recentPostsCache: {}, debug: bool) -> {}: """Download the post referenced by an announce """ - if not postJsonObject.get('object'): - return None - if not isinstance(postJsonObject['object'], str): + if not hasObjectDict(postJsonObject): return None # ignore self-boosts if postJsonObject['actor'] in postJsonObject['object']: diff --git a/question.py b/question.py index bdf09aa9e..9008cbd56 100644 --- a/question.py +++ b/question.py @@ -11,6 +11,7 @@ import os from utils import locatePost from utils import loadJson from utils import saveJson +from utils import hasObjectDict def questionUpdateVotes(baseDir: str, nickname: str, domain: str, @@ -18,9 +19,7 @@ def questionUpdateVotes(baseDir: str, nickname: str, domain: str, """ For a given reply update the votes on a question Returns the question json object if the vote totals were changed """ - if not replyJson.get('object'): - return None - if not isinstance(replyJson['object'], dict): + if not hasObjectDict(replyJson): return None if not replyJson['object'].get('inReplyTo'): return None @@ -37,9 +36,7 @@ def questionUpdateVotes(baseDir: str, nickname: str, domain: str, questionJson = loadJson(questionPostFilename) if not questionJson: return None - if not questionJson.get('object'): - return None - if not isinstance(questionJson['object'], dict): + if not hasObjectDict(questionJson): return None if not questionJson['object'].get('type'): return None diff --git a/schedule.py b/schedule.py index 5a8275bb5..4aaaa026d 100644 --- a/schedule.py +++ b/schedule.py @@ -10,6 +10,7 @@ __module_group__ = "Calendar" import os import time import datetime +from utils import hasObjectDict from utils import getStatusNumber from utils import loadJson from outbox import postMessageToOutbox @@ -77,10 +78,9 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd, statusNumber, published = getStatusNumber() if postJsonObject.get('published'): postJsonObject['published'] = published - if postJsonObject.get('object'): - if isinstance(postJsonObject['object'], dict): - if postJsonObject['object'].get('published'): - postJsonObject['published'] = published + if hasObjectDict(postJsonObject): + if postJsonObject['object'].get('published'): + postJsonObject['published'] = published print('Sending scheduled post ' + postId) diff --git a/shares.py b/shares.py index 59e960b1f..c906a4a23 100644 --- a/shares.py +++ b/shares.py @@ -19,6 +19,7 @@ from utils import validNickname from utils import loadJson from utils import saveJson from utils import getImageExtensions +from utils import hasObjectDict from media import processMetaData @@ -524,9 +525,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, return if not messageJson['type'] == 'Add': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return if not messageJson['object'].get('type'): if debug: @@ -583,9 +582,7 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, return if not messageJson['type'] == 'Remove': return - if not messageJson.get('object'): - return - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return if not messageJson['object'].get('type'): if debug: diff --git a/speaker.py b/speaker.py index a04ef3709..dc77f0207 100644 --- a/speaker.py +++ b/speaker.py @@ -21,6 +21,7 @@ from utils import removeHtml from utils import loadJson from utils import saveJson from utils import isPGPEncrypted +from utils import hasObjectDict from content import htmlReplaceQuoteMarks speakerRemoveChars = ('.\n', '. ', ',', ';', '?', '!') @@ -407,9 +408,7 @@ def _postToSpeakerJson(baseDir: str, httpPrefix: str, NOTE: There currently appears to be no standardized json format for speech synthesis """ - if not postJsonObject.get('object'): - return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('content'): return diff --git a/utils.py b/utils.py index 47a1b392f..94234eecc 100644 --- a/utils.py +++ b/utils.py @@ -1231,9 +1231,7 @@ def _isReplyToBlogPost(baseDir: str, nickname: str, domain: str, postJsonObject: str): """Is the given post a reply to a blog post? """ - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('inReplyTo'): return False @@ -1525,9 +1523,7 @@ def isPublicPost(postJsonObject: {}) -> bool: return False if postJsonObject['type'] != 'Create': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('to'): return False @@ -1675,9 +1671,7 @@ def isEventPost(messageJson: {}) -> bool: return False if not messageJson.get('actor'): return False - if not messageJson.get('object'): - return False - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return False if not messageJson['object'].get('type'): return False @@ -1708,9 +1702,7 @@ def isBlogPost(postJsonObject: {}) -> bool: """ if postJsonObject['type'] != 'Create': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('type'): return False @@ -1863,13 +1855,11 @@ def undoLikesCollectionEntry(recentPostsCache: {}, return if postJsonObject['type'] != 'Create': return - if not postJsonObject.get('object'): + if not hasObjectDict(postJsonObject): if debug: pprint(postJsonObject) print('DEBUG: post ' + objectUrl + ' has no object') return - if not isinstance(postJsonObject['object'], dict): - return if not postJsonObject['object'].get('likes'): return if not isinstance(postJsonObject['object']['likes'], dict): @@ -1918,13 +1908,11 @@ def updateLikesCollection(recentPostsCache: {}, if os.path.isfile(cachedPostFilename): os.remove(cachedPostFilename) - if not postJsonObject.get('object'): + if not hasObjectDict(postJsonObject): if debug: pprint(postJsonObject) print('DEBUG: post ' + objectUrl + ' has no object') return - if not isinstance(postJsonObject['object'], dict): - return if not objectUrl.endswith('/likes'): objectUrl = objectUrl + '/likes' if not postJsonObject['object'].get('likes'): @@ -1987,13 +1975,11 @@ def undoAnnounceCollectionEntry(recentPostsCache: {}, return if postJsonObject['type'] != 'Create': return - if not postJsonObject.get('object'): + if not hasObjectDict(postJsonObject): if debug: pprint(postJsonObject) print('DEBUG: post has no object') return - if not isinstance(postJsonObject['object'], dict): - return if not postJsonObject['object'].get('shares'): return if not postJsonObject['object']['shares'].get('items'): @@ -2045,13 +2031,11 @@ def updateAnnounceCollection(recentPostsCache: {}, os.remove(cachedPostFilename) removePostFromCache(postJsonObject, recentPostsCache) - if not postJsonObject.get('object'): + if not hasObjectDict(postJsonObject): if debug: pprint(postJsonObject) print('DEBUG: post ' + postFilename + ' has no object') return - if not isinstance(postJsonObject['object'], dict): - return postUrl = removeIdEnding(postJsonObject['id']) + '/shares' if not postJsonObject['object'].get('shares'): if debug: @@ -2129,9 +2113,7 @@ def mediaFileMimeType(filename: str) -> str: def isRecentPost(postJsonObject: {}, maxDays=3) -> bool: """ Is the given post recent? """ - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if not postJsonObject['object'].get('published'): return False @@ -2206,9 +2188,7 @@ def isDM(postJsonObject: {}) -> bool: """ if postJsonObject['type'] != 'Create': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if postJsonObject['object']['type'] != 'Note' and \ postJsonObject['object']['type'] != 'Patch' and \ @@ -2234,9 +2214,7 @@ def isReply(postJsonObject: {}, actor: str) -> bool: """ if postJsonObject['type'] != 'Create': return False - if not postJsonObject.get('object'): - return False - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return False if postJsonObject['object'].get('moderationStatus'): return False @@ -2456,3 +2434,12 @@ def userAgentDomain(userAgent: str, debug: bool) -> str: if debug: print('User-Agent Domain: ' + agentDomain) return agentDomain + + +def hasObjectDict(postJsonObject: {}) -> bool: + """Returns true if the given post has an object dict + """ + if postJsonObject.get('object'): + if isinstance(postJsonObject['object'], dict): + return True + return False diff --git a/webapp_profile.py b/webapp_profile.py index d73a0601f..fcaff60c8 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -9,6 +9,7 @@ __module_group__ = "Web Interface" import os from pprint import pprint +from utils import hasObjectDict from utils import getOccupationName from utils import getLockedAccount from utils import getFullDomain @@ -230,7 +231,7 @@ def htmlProfileAfterSearch(cssCache: {}, continue if item['type'] != 'Create': continue - if not item.get('object'): + if not hasObjectDict(item): continue profileStr += \