diff --git a/blog.py b/blog.py index f9a7fdfa7..f6e5aaa41 100644 --- a/blog.py +++ b/blog.py @@ -17,6 +17,7 @@ from webapp_utils import html_footer from webapp_utils import get_post_attachments_as_html from webapp_utils import edit_text_area from webapp_media import add_embedded_elements +from utils import get_attributed_to from utils import remove_eol from utils import text_in_file from utils import local_actor_url @@ -219,8 +220,10 @@ def _html_blog_post_content(debug: bool, session, authorized: bool, # get the handle of the author if post_json_object['object'].get('attributedTo'): author_nickname = None - if isinstance(post_json_object['object']['attributedTo'], str): - actor = post_json_object['object']['attributedTo'] + actor_str = \ + get_attributed_to(post_json_object['object']['attributedTo']) + if actor_str: + actor = actor_str author_nickname = get_nickname_from_actor(actor) if author_nickname: author_domain, _ = get_domain_from_actor(actor) diff --git a/conversation.py b/conversation.py index 4214f33b2..698f523d1 100644 --- a/conversation.py +++ b/conversation.py @@ -15,6 +15,7 @@ from utils import text_in_file from utils import locate_post from utils import load_json from utils import harmless_markup +from utils import get_attributed_to from keys import get_instance_actor_key from session import get_json from session import get_json_valid @@ -149,7 +150,8 @@ def download_conversation_posts(authorized: bool, session, if debug: print(post_id + ' has no attributedTo') break - if not isinstance(post_json_object['attributedTo'], str): + attrib_str = get_attributed_to(post_json_object['attributedTo']) + if not attrib_str: break if not post_json_object.get('published'): if debug: @@ -171,7 +173,7 @@ def download_conversation_posts(authorized: bool, session, "@context": "https://www.w3.org/ns/activitystreams", 'id': post_id + '/activity', 'type': 'Create', - 'actor': post_json_object['attributedTo'], + 'actor': attrib_str, 'published': post_json_object['published'], 'to': post_json_object['to'], 'cc': post_json_object['cc'], diff --git a/daemon.py b/daemon.py index 6723d77d9..f6747cd0f 100644 --- a/daemon.py +++ b/daemon.py @@ -299,6 +299,7 @@ from languages import set_actor_languages from languages import get_understood_languages from like import update_likes_collection from reaction import update_reaction_collection +from utils import get_attributed_to from utils import get_memorials from utils import set_memorials from utils import is_memorial_account @@ -2055,7 +2056,7 @@ class PubServer(BaseHTTPRequestHandler): print('INBOX: checking object fields') string_fields = ( 'id', 'actor', 'type', 'content', 'published', - 'summary', 'url', 'attributedTo' + 'summary', 'url' ) for check_field in string_fields: if not message_json['object'].get(check_field): @@ -2067,6 +2068,16 @@ class PubServer(BaseHTTPRequestHandler): self._400() self.server.postreq_busy = False return 3 + if message_json['object'].get('attributedTo'): + attrib_field = message_json['object']['attributedTo'] + if not isinstance(attrib_field, str) and \ + not isinstance(attrib_field, list): + print('INBOX: ' + + 'attributedTo should be a string or list ' + + str(attrib_field)) + self._400() + self.server.postreq_busy = False + return 3 # check that some fields are lists if debug: print('INBOX: checking object to and cc fields') @@ -2140,7 +2151,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.postreq_busy = False return 3 # check that the sender is local - local_actor = message_json['object']['attributedTo'] + attrib_field = message_json['object']['attributedTo'] + local_actor = get_attributed_to(attrib_field) local_domain, local_port = \ get_domain_from_actor(local_actor) if local_domain: diff --git a/desktop_client.py b/desktop_client.py index eb2708bb1..a7b90b254 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 get_attributed_to from utils import remove_html from utils import safe_system_string from utils import text_in_file @@ -805,11 +806,12 @@ def _read_local_box_post(session, nickname: str, domain: str, if has_object_dict(post_json_object2): if post_json_object2['object'].get('attributedTo') and \ post_json_object2['object'].get('content'): - attributed_to = post_json_object2['object']['attributedTo'] + attrib_field = post_json_object2['object']['attributedTo'] + attributed_to = get_attributed_to(attrib_field) content = \ get_base_content_from_post(post_json_object2, system_language) - if isinstance(attributed_to, str) and content: + if attributed_to and content: actor = attributed_to name_str += ' ' + translate['announces'] + ' ' + \ get_nickname_from_actor(actor) @@ -832,7 +834,8 @@ def _read_local_box_post(session, nickname: str, domain: str, return post_json_object2 return {} - attributed_to = post_json_object['object']['attributedTo'] + attributed_to = \ + get_attributed_to(post_json_object['object']['attributedTo']) if not attributed_to: return {} content = get_base_content_from_post(post_json_object, system_language) @@ -958,7 +961,7 @@ def _desktop_show_profile(session, nickname: str, post_json_object['object'].split(nick_str)[0] + \ '/' + nickname else: - actor = post_json_object['object']['attributedTo'] + actor = get_attributed_to(post_json_object['object']['attributedTo']) if not actor: return {} @@ -1140,7 +1143,8 @@ def _desktop_show_box(indent: str, ctr_str = str(ctr) pos_str = _pad_to_width(ctr_str, number_width) - author_actor = post_json_object['object']['attributedTo'] + author_actor = \ + get_attributed_to(post_json_object['object']['attributedTo']) content_warning = None if post_json_object['object'].get('summary'): content_warning = '⚡' + \ @@ -1945,7 +1949,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - like_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + like_actor = get_attributed_to(attrib_field) say_str = 'Liking post by ' + \ get_nickname_from_actor(like_actor) _say_command(say_str, say_str, @@ -1986,7 +1992,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - mute_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + mute_actor = get_attributed_to(attrib_field) say_str = 'Unmuting post by ' + \ get_nickname_from_actor(mute_actor) _say_command(say_str, say_str, @@ -2020,7 +2028,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - mute_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + mute_actor = get_attributed_to(attrib_field) say_str = 'Muting post by ' + \ get_nickname_from_actor(mute_actor) _say_command(say_str, say_str, @@ -2064,7 +2074,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - bm_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + bm_actor = get_attributed_to(attrib_field) say_str = 'Unbookmarking post by ' + \ get_nickname_from_actor(bm_actor) _say_command(say_str, say_str, @@ -2098,7 +2110,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - bm_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + bm_actor = get_attributed_to(attrib_field) say_str = 'Bookmarking post by ' + \ get_nickname_from_actor(bm_actor) _say_command(say_str, say_str, @@ -2134,8 +2148,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, post_json_object.get('object'): if has_object_dict(post_json_object): if post_json_object['object'].get('attributedTo'): - block_actor = \ + attrib_field = \ post_json_object['object']['attributedTo'] + block_actor = get_attributed_to(attrib_field) say_str = 'Unblocking ' + \ get_nickname_from_actor(block_actor) _say_command(say_str, say_str, @@ -2184,8 +2199,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, post_json_object.get('object'): if has_object_dict(post_json_object): if post_json_object['object'].get('attributedTo'): - block_actor = \ + attrib_field = \ post_json_object['object']['attributedTo'] + block_actor = get_attributed_to(attrib_field) if block_actor: say_str = 'Blocking ' + \ get_nickname_from_actor(block_actor) @@ -2217,8 +2233,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - unlike_actor = \ + attrib_field = \ post_json_object['object']['attributedTo'] + unlike_actor = get_attributed_to(attrib_field) say_str = \ 'Undoing like of post by ' + \ get_nickname_from_actor(unlike_actor) @@ -2268,8 +2285,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, capabilities): if post_json_object.get('id'): post_id = post_json_object['id'] - announce_actor = \ + attrib_field = \ post_json_object['object']['attributedTo'] + announce_actor = get_attributed_to(attrib_field) say_str = 'Announcing post by ' + \ get_nickname_from_actor(announce_actor) _say_command(say_str, say_str, @@ -2305,8 +2323,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, if post_json_object: if post_json_object.get('id'): post_id = post_json_object['id'] - announce_actor = \ + attrib_field = \ post_json_object['object']['attributedTo'] + announce_actor = get_attributed_to(attrib_field) say_str = 'Undoing announce post by ' + \ get_nickname_from_actor(announce_actor) _say_command(say_str, say_str, @@ -2670,7 +2689,9 @@ def run_desktop_client(base_dir: str, proxy_type: str, http_prefix: str, _desktop_get_box_post_object(box_json, curr_index) if post_json_object: if post_json_object.get('id'): - rm_actor = post_json_object['object']['attributedTo'] + attrib_field = \ + post_json_object['object']['attributedTo'] + rm_actor = get_attributed_to(attrib_field) if rm_actor != your_actor: say_str = 'You can only delete your own posts' _say_command(say_str, say_str, diff --git a/git.py b/git.py index cc26fde57..15b99f4a0 100644 --- a/git.py +++ b/git.py @@ -14,6 +14,7 @@ from utils import has_object_string_type from utils import text_in_file from utils import get_attachment_property_value from utils import remove_html +from utils import get_attributed_to def _git_format_content(content: str) -> str: @@ -127,7 +128,7 @@ def convert_post_to_patch(base_dir: str, nickname: str, domain: str, return False if not post_json_object['object'].get('attributedTo'): return False - if not isinstance(post_json_object['object']['attributedTo'], str): + if get_attributed_to(post_json_object['object']['attributedTo']) is None: return False if not is_git_patch(base_dir, nickname, domain, post_json_object['object']['type'], @@ -143,7 +144,7 @@ def convert_post_to_patch(base_dir: str, nickname: str, domain: str, # add a commitedBy parameter if not post_json_object['object'].get('committedBy'): post_json_object['object']['committedBy'] = \ - post_json_object['object']['attributedTo'] + get_attributed_to(post_json_object['object']['attributedTo']) post_json_object['object']['hash'] = commit_hash post_json_object['object']['description'] = { "mediaType": "text/plain", diff --git a/inbox.py b/inbox.py index 70956bef6..906b7b04b 100644 --- a/inbox.py +++ b/inbox.py @@ -75,6 +75,7 @@ from utils import has_group_type from utils import local_actor_url from utils import has_object_string_type from utils import valid_hash_tag +from utils import get_attributed_to from categories import get_hashtag_categories from categories import set_hashtag_category from httpsig import get_digest_algorithm_from_headers @@ -180,7 +181,7 @@ def cache_svg_images(session, base_dir: str, http_prefix: str, post_id = remove_id_ending(obj['id']).replace('/', '--') actor = 'unknown' if obj.get('attributedTo'): - actor = obj['attributedTo'] + actor = get_attributed_to(obj['attributedTo']) log_filename = base_dir + '/accounts/svg_scripts_log.txt' for index in range(len(obj['attachment'])): attach = obj['attachment'][index] @@ -258,8 +259,10 @@ def _store_last_post_id(base_dir: str, nickname: str, domain: str, actor = post_id = None if has_object_dict(post_json_object): if post_json_object['object'].get('attributedTo'): - if isinstance(post_json_object['object']['attributedTo'], str): - actor = post_json_object['object']['attributedTo'] + actor_str = \ + get_attributed_to(post_json_object['object']['attributedTo']) + if actor_str: + actor = actor_str post_id = remove_id_ending(post_json_object['object']['id']) if not actor: actor = post_json_object['actor'] @@ -688,7 +691,8 @@ def save_post_to_inbox_queue(base_dir: str, http_prefix: str, if has_object_dict(post_json_object): obj_dict_exists = True if post_json_object['object'].get('attributedTo'): - sending_actor = post_json_object['object']['attributedTo'] + sending_actor = \ + get_attributed_to(post_json_object['object']['attributedTo']) if not sending_actor: if post_json_object.get('actor'): sending_actor = post_json_object['actor'] @@ -3198,15 +3202,16 @@ def _receive_announce(recent_posts_cache: {}, # so that their avatar can be shown lookup_actor = None if post_json_object.get('attributedTo'): - attrib = post_json_object['attributedTo'] - if isinstance(attrib, str): + attrib = get_attributed_to(post_json_object['attributedTo']) + if attrib: if not contains_invalid_actor_url_chars(attrib): lookup_actor = attrib else: if has_object_dict(post_json_object): if post_json_object['object'].get('attributedTo'): - attrib = post_json_object['object']['attributedTo'] - if isinstance(attrib, str): + attrib_field = post_json_object['object']['attributedTo'] + attrib = get_attributed_to(attrib_field) + if attrib: if not contains_invalid_actor_url_chars(attrib): lookup_actor = attrib if lookup_actor: @@ -4400,8 +4405,8 @@ def _low_frequency_post_notification(base_dir: str, http_prefix: str, return if not json_obj.get('id'): return - attributed_to = json_obj['attributedTo'] - if not isinstance(attributed_to, str): + attributed_to = get_attributed_to(json_obj['attributedTo']) + if not attributed_to: return from_nickname = get_nickname_from_actor(attributed_to) if not from_nickname: @@ -4432,8 +4437,8 @@ def _check_for_git_patches(base_dir: str, nickname: str, domain: str, return 0 if not json_obj.get('attributedTo'): return 0 - attributed_to = json_obj['attributedTo'] - if not isinstance(attributed_to, str): + attributed_to = get_attributed_to(json_obj['attributedTo']) + if not attributed_to: return 0 from_nickname = get_nickname_from_actor(attributed_to) if not from_nickname: diff --git a/outbox.py b/outbox.py index 2fe6fcfde..f963692b1 100644 --- a/outbox.py +++ b/outbox.py @@ -15,6 +15,7 @@ from posts import outbox_message_create_wrap from posts import save_post_to_box from posts import send_to_followers_thread from posts import send_to_named_addresses_thread +from utils import get_attributed_to from utils import contains_invalid_actor_url_chars from utils import get_attachment_property_value from utils import get_account_timezone @@ -284,7 +285,8 @@ def post_message_to_outbox(session, translate: {}, str(message_json)) return False # check that the sender is local - local_actor = message_json['object']['attributedTo'] + local_actor = \ + get_attributed_to(message_json['object']['attributedTo']) local_domain, local_port = get_domain_from_actor(local_actor) if local_domain: local_domain_full = \ diff --git a/posts.py b/posts.py index a85892423..d40bf558c 100644 --- a/posts.py +++ b/posts.py @@ -34,6 +34,7 @@ from webfinger import webfinger_handle from httpsig import create_signed_header from siteactive import site_is_active from languages import understood_post_language +from utils import get_attributed_to from utils import contains_statuses from utils import contains_invalid_actor_url_chars from utils import acct_handle_dir @@ -5617,7 +5618,7 @@ def download_announce(session, base_dir: str, http_prefix: str, return None announced_actor = announced_json['id'] if announced_json.get('attributedTo'): - announced_actor = announced_json['attributedTo'] + announced_actor = get_attributed_to(announced_json['attributedTo']) if not announced_json.get('type'): print('WARN: announced post does not have a type ' + str(announced_json)) @@ -6289,9 +6290,9 @@ def edited_post_filename(base_dir: str, nickname: str, domain: str, return '', None if not post_json_object['object'].get('attributedTo'): return '', None - if not isinstance(post_json_object['object']['attributedTo'], str): + if not get_attributed_to(post_json_object['object']['attributedTo']): return '', None - actor = post_json_object['object']['attributedTo'] + actor = get_attributed_to(post_json_object['object']['attributedTo']) actor_filename = \ acct_dir(base_dir, nickname, domain) + '/lastpost/' + \ actor.replace('/', '#') @@ -6337,7 +6338,7 @@ def edited_post_filename(base_dir: str, nickname: str, domain: str, return '', None if not lastpost_json['object'].get('attributedTo'): return '', None - if not isinstance(lastpost_json['object']['attributedTo'], str): + if not get_attributed_to(lastpost_json['object']['attributedTo']): return '', None time_diff_seconds = \ seconds_between_published(lastpost_json['object']['published'], @@ -6392,9 +6393,11 @@ def get_original_post_from_announce_url(announce_url: str, base_dir: str, if orig_post_json: if has_object_dict(orig_post_json): if orig_post_json['object'].get('attributedTo'): - attrib = orig_post_json['object']['attributedTo'] - if isinstance(attrib, str): - actor = orig_post_json['object']['attributedTo'] + attrib_field = \ + orig_post_json['object']['attributedTo'] + attrib = get_attributed_to(attrib_field) + if attrib: + actor = attrib url = orig_post_id elif orig_post_json['object'].get('actor'): actor = orig_post_json['actor'] diff --git a/utils.py b/utils.py index ff8615fec..23afcefbc 100644 --- a/utils.py +++ b/utils.py @@ -46,6 +46,17 @@ INVALID_ACTOR_URL_CHARACTERS = ( ) +def get_attributed_to(field) -> str: + """Returns the actor + """ + if isinstance(field, str): + return field + elif isinstance(field, list): + if isinstance(field[0], str): + return field[0] + return None + + def _standardize_text_range(text: str, range_start: int, range_end: int, offset: str) -> str: @@ -2307,7 +2318,7 @@ def is_reminder(post_json_object: {}) -> bool: if len(post_json_object['object']['to']) != 1: return False if post_json_object['object']['to'][0] != \ - post_json_object['object']['attributedTo']: + get_attributed_to(post_json_object['object']['attributedTo']): return False for tag in post_json_object['object']['tag']: if tag['type'] == 'Event': @@ -2324,8 +2335,9 @@ def _is_remote_dm(domain_full: str, post_json_object: {}) -> bool: if has_object_dict(post_json_object): this_post_json = post_json_object['object'] if this_post_json.get('attributedTo'): - if isinstance(this_post_json['attributedTo'], str): - if '://' + domain_full not in this_post_json['attributedTo']: + attrib = get_attributed_to(this_post_json['attributedTo']) + if attrib: + if '://' + domain_full not in attrib: return True return False diff --git a/video.py b/video.py index 9e06ddb68..1deb07896 100644 --- a/video.py +++ b/video.py @@ -12,6 +12,7 @@ from utils import get_full_domain from utils import get_nickname_from_actor from utils import get_domain_from_actor from utils import remove_id_ending +from utils import get_attributed_to from blocking import is_blocked from filters import is_filtered @@ -38,7 +39,7 @@ def convert_video_to_note(base_dir: str, nickname: str, domain: str, # who is this attributed to ? attributed_to = None if isinstance(post_json_object['attributedTo'], str): - attributed_to = post_json_object['attributedTo'] + attributed_to = get_attributed_to(post_json_object['attributedTo']) elif isinstance(post_json_object['attributedTo'], list): for entity in post_json_object['attributedTo']: if not isinstance(entity, dict): diff --git a/webapp_conversation.py b/webapp_conversation.py index 571ef0db8..5df0454e3 100644 --- a/webapp_conversation.py +++ b/webapp_conversation.py @@ -14,6 +14,7 @@ from utils import get_config_param from utils import get_nickname_from_actor from utils import get_domain_from_actor from utils import is_public_post +from utils import get_attributed_to from blocking import is_blocked from webapp_utils import html_header_with_external_style from webapp_utils import html_post_separator @@ -81,7 +82,8 @@ def html_conversation_view(authorized: bool, post_id: str, show_individual_post_icons = False if not is_public_post(post_json_object): continue - from_actor = post_json_object['object']['attributedTo'] + from_actor = \ + get_attributed_to(post_json_object['object']['attributedTo']) from_nickname = get_nickname_from_actor(from_actor) from_domain, _ = get_domain_from_actor(from_actor) # don't show icons on posts from blocked accounts/instances diff --git a/webapp_post.py b/webapp_post.py index 87ed82543..e532cdfc9 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -71,6 +71,7 @@ from utils import acct_dir from utils import local_actor_url from utils import is_unlisted_post from utils import language_right_to_left +from utils import get_attributed_to from content import format_mixed_right_to_left from content import replace_remote_hashtags from content import detect_dogwhistles @@ -139,8 +140,9 @@ def _html_post_metadata_open_graph(domain: str, post_json_object: {}, metadata += " \n" if obj_json.get('attributedTo'): - if isinstance(obj_json['attributedTo'], str): - attrib = obj_json['attributedTo'] + attrib_str = get_attributed_to(obj_json['attributedTo']) + if attrib_str: + attrib = attrib_str actor_nick = get_nickname_from_actor(attrib) actor_domain, _ = get_domain_from_actor(attrib) if actor_nick and actor_domain: @@ -527,9 +529,9 @@ def _get_reply_icon_html(base_dir: str, nickname: str, domain: str, reply_to_link = post_json_object['object']['replyTo'] if post_json_object['object'].get('attributedTo'): - if isinstance(post_json_object['object']['attributedTo'], str): - reply_to_link += \ - '?mention=' + post_json_object['object']['attributedTo'] + attrib = get_attributed_to(post_json_object['object']['attributedTo']) + if attrib: + reply_to_link += '?mention=' + attrib content = get_base_content_from_post(post_json_object, system_language) if content: mentioned_actors = \ @@ -1299,9 +1301,9 @@ def _get_post_title_announce_html(base_dir: str, return (title_str, reply_avatar_image_in_post, container_class_icons, container_class) - attributed_to = '' - if isinstance(obj_json['attributedTo'], str): - attributed_to = obj_json['attributedTo'] + attributed_to = get_attributed_to(obj_json['attributedTo']) + if attributed_to is None: + attributed_to = '' # boosting your own post if attributed_to.startswith(post_actor): @@ -1552,14 +1554,16 @@ def _get_post_title_reply_html(base_dir: str, if has_object_dict(reply_post_json): obj_json = reply_post_json['object'] if obj_json.get('attributedTo'): - if isinstance(obj_json['attributedTo'], str): - reply_actor = obj_json['attributedTo'] + attrib = get_attributed_to(obj_json['attributedTo']) + if attrib: + reply_actor = attrib in_reply_to = reply_actor elif obj_json != reply_post_json: obj_json = reply_post_json if obj_json.get('attributedTo'): - if isinstance(obj_json['attributedTo'], str): - reply_actor = obj_json['attributedTo'] + attrib = get_attributed_to(obj_json['attributedTo']) + if attrib: + reply_actor = attrib in_reply_to = reply_actor if post_domain and not reply_actor: @@ -1676,7 +1680,9 @@ def _get_post_title_html(base_dir: str, if not is_announced and box_name == 'search' and \ post_json_object.get('object'): if post_json_object['object'].get('attributedTo'): - if post_json_object['object']['attributedTo'] != post_actor: + attrib = \ + get_attributed_to(post_json_object['object']['attributedTo']) + if attrib != post_actor: is_announced = True if is_announced: @@ -2802,7 +2808,8 @@ def individual_post_as_html(signing_priv_key_pem: str, map_str = '