diff --git a/acceptreject.py b/acceptreject.py index 1ce9d256c..a21aa3866 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -20,7 +20,7 @@ from utils import acct_dir from utils import has_group_type from utils import local_actor_url from utils import has_actor -from utils import has_object_stringType +from utils import has_object_string_type def _create_accept_reject(base_dir: str, federation_list: [], @@ -81,7 +81,7 @@ def _accept_follow(base_dir: str, domain: str, message_json: {}, onion_domain: str, i2p_domain: str) -> None: """Receiving a follow Accept activity """ - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Follow': if not message_json['object']['type'] == 'Join': diff --git a/announce.py b/announce.py index 228295042..9fd69010c 100644 --- a/announce.py +++ b/announce.py @@ -26,7 +26,7 @@ from utils import update_announce_collection from utils import local_actor_url from utils import replace_users_with_at from utils import has_actor -from utils import has_object_stringType +from utils import has_object_string_type from posts import send_signed_json from posts import get_person_box from session import post_json @@ -100,7 +100,7 @@ def outbox_announce(recent_posts_cache: {}, nickname, domain, debug) return True elif message_json['type'] == 'Undo': - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if message_json['object']['type'] == 'Announce': if not isinstance(message_json['object']['object'], str): @@ -422,7 +422,7 @@ def outbox_undo_announce(recent_posts_cache: {}, return if not message_json['type'] == 'Undo': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Announce': if debug: diff --git a/blocking.py b/blocking.py index d2e5f7d64..d3eefc4e4 100644 --- a/blocking.py +++ b/blocking.py @@ -13,7 +13,7 @@ import time from datetime import datetime from utils import has_object_string from utils import has_object_string_object -from utils import has_object_stringType +from utils import has_object_string_type from utils import remove_domain_port from utils import has_object_dict from utils import is_account_dir @@ -483,7 +483,7 @@ def outbox_undo_block(base_dir: str, http_prefix: str, print('DEBUG: not an undo block') return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Block': if debug: @@ -841,7 +841,7 @@ def outbox_undo_mute(base_dir: str, http_prefix: str, return if not message_json['type'] == 'Undo': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if message_json['object']['type'] != 'Ignore': return diff --git a/bookmarks.py b/bookmarks.py index 5ee5478d9..6d8c78d1e 100644 --- a/bookmarks.py +++ b/bookmarks.py @@ -27,7 +27,7 @@ from utils import has_object_dict from utils import acct_dir from utils import local_actor_url from utils import has_actor -from utils import has_object_stringType +from utils import has_object_string_type from posts import get_person_box from session import post_json @@ -571,7 +571,7 @@ def outbox_bookmark(recent_posts_cache: {}, if debug: print('DEBUG: no target in bookmark Add') return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not isinstance(message_json['target'], str): if debug: @@ -627,7 +627,7 @@ def outbox_undo_bookmark(recent_posts_cache: {}, if debug: print('DEBUG: no target in unbookmark Remove') return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not isinstance(message_json['target'], str): if debug: diff --git a/follow.py b/follow.py index cfd234ddd..29b2e3ce0 100644 --- a/follow.py +++ b/follow.py @@ -10,7 +10,7 @@ __module_group__ = "ActivityPub" from pprint import pprint import os from utils import has_object_string_object -from utils import has_object_stringType +from utils import has_object_string_type from utils import remove_domain_port from utils import has_users_path from utils import get_full_domain @@ -1407,7 +1407,7 @@ def outbox_undo_follow(base_dir: str, message_json: {}, debug: bool) -> None: return if not message_json['type'] == 'Undo': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Follow': if not message_json['object']['type'] == 'Join': diff --git a/git.py b/git.py index 8c12d137c..f374fb3db 100644 --- a/git.py +++ b/git.py @@ -10,7 +10,7 @@ __module_group__ = "Profile Metadata" import os import html from utils import acct_dir -from utils import has_object_stringType +from utils import has_object_string_type def _git_format_content(content: str) -> str: @@ -114,7 +114,7 @@ def convert_post_to_patch(base_dir: 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 has_object_stringType(post_json_object, False): + if not has_object_string_type(post_json_object, False): return False if post_json_object['object']['type'] == 'Patch': return True diff --git a/inbox.py b/inbox.py index bb1fa70c2..e7a4f2500 100644 --- a/inbox.py +++ b/inbox.py @@ -17,6 +17,7 @@ from languages import understood_post_language from like import update_likes_collection from reaction import update_reaction_collection from reaction import valid_emoji_content +from utils import delete_cached_html from utils import get_account_timezone from utils import domain_permitted from utils import is_group_account @@ -61,7 +62,7 @@ from utils import undo_likes_collection_entry from utils import undo_reaction_collection_entry from utils import has_group_type from utils import local_actor_url -from utils import has_object_stringType +from utils import has_object_string_type from utils import valid_hash_tag from categories import get_hashtag_categories from categories import set_hashtag_category @@ -849,7 +850,7 @@ def _receive_undo(session, base_dir: str, http_prefix: str, if debug: print('DEBUG: "users" or "profile" missing from actor') return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if not has_object_string_object(message_json, debug): return False @@ -979,20 +980,150 @@ def _receive_update_to_question(recent_posts_cache: {}, message_json: {}, remove_post_from_cache(message_json, recent_posts_cache) +def _receive_edit_to_post(recent_posts_cache: {}, message_json: {}, + base_dir: str, + nickname: str, domain: str, + max_mentions: int, max_emoji: int, + allow_local_network_access: bool, + debug: bool, + system_language: str, http_prefix: str, + domain_full: str, person_cache: {}, + signing_priv_key_pem: str, + max_recent_posts: int, translate: {}, + session, cached_webfingers: {}, port: int, + allow_deletion: bool, + yt_replace_domain: str, + twitter_replacement_domain: str, + show_published_date_only: bool, + peertube_instances: [], + theme_name: str, max_like_count: int, + cw_lists: {}) -> bool: + """A post was edited + """ + if not has_object_dict(message_json): + return False + # message url of the question + if not message_json['object'].get('id'): + return False + if not message_json.get('actor'): + return False + if not has_actor(message_json, False): + return False + if not has_actor(message_json['object'], False): + return False + message_id = remove_id_ending(message_json['object']['id']) + if '#' in message_id: + message_id = message_id.split('#', 1)[0] + # find the post which was edited + post_filename = locate_post(base_dir, nickname, domain, message_id) + if not post_filename: + print('EDITPOST: ' + message_id + ' has already expired') + return False + if not _valid_post_content(base_dir, nickname, domain, + message_json, max_mentions, max_emoji, + allow_local_network_access, debug, + system_language, http_prefix, + domain_full, person_cache): + print('EDITPOST: contains invalid content' + str(message_json)) + return False + + # load the json for the post + post_json_object = load_json(post_filename, 1) + if not post_json_object: + return False + if not post_json_object.get('actor'): + return False + # does the actor match? + if post_json_object['actor'] != message_json['actor']: + print('EDITPOST: actors do not match ' + + post_json_object['actor'] + ' != ' + message_json['actor']) + return False + # Change Update to Create + message_json['type'] = 'Create' + save_json(message_json, post_filename) + # ensure that the cached post is removed if it exists, so + # that it then will be recreated + cached_post_filename = \ + get_cached_post_filename(base_dir, nickname, domain, message_json) + if cached_post_filename: + if os.path.isfile(cached_post_filename): + try: + os.remove(cached_post_filename) + except OSError: + print('EX: _receive_edit_to_post unable to delete ' + + cached_post_filename) + # remove any cached html for the post which was edited + delete_cached_html(base_dir, nickname, domain, post_json_object) + # remove from memory cache + remove_post_from_cache(message_json, recent_posts_cache) + # regenerate html for the post + page_number = 1 + show_published_date_only = False + show_individual_post_icons = True + manually_approve_followers = \ + follower_approval_active(base_dir, nickname, domain) + not_dm = not is_dm(message_json) + timezone = get_account_timezone(base_dir, nickname, domain) + mitm = False + if os.path.isfile(post_filename.replace('.json', '') + '.mitm'): + mitm = True + bold_reading = False + bold_reading_filename = \ + base_dir + '/accounts/' + nickname + '@' + domain + '/.boldReading' + if os.path.isfile(bold_reading_filename): + bold_reading = True + timezone = get_account_timezone(base_dir, nickname, domain) + lists_enabled = get_config_param(base_dir, "listsEnabled") + individual_post_as_html(signing_priv_key_pem, False, + recent_posts_cache, max_recent_posts, + translate, page_number, base_dir, + session, cached_webfingers, person_cache, + nickname, domain, port, message_json, + None, True, allow_deletion, + http_prefix, __version__, + 'inbox', + yt_replace_domain, + twitter_replacement_domain, + show_published_date_only, + peertube_instances, + allow_local_network_access, + theme_name, system_language, + max_like_count, not_dm, + show_individual_post_icons, + manually_approve_followers, + False, True, False, cw_lists, + lists_enabled, timezone, mitm, + bold_reading) + return True + + def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str, http_prefix: str, domain: str, port: int, send_threads: [], post_log: [], cached_webfingers: {}, person_cache: {}, message_json: {}, federation_list: [], - nickname: str, debug: bool) -> bool: + nickname: str, debug: bool, + max_mentions: int, max_emoji: int, + allow_local_network_access: bool, + system_language: str, + signing_priv_key_pem: str, + max_recent_posts: int, translate: {}, + allow_deletion: bool, + yt_replace_domain: str, + twitter_replacement_domain: str, + show_published_date_only: bool, + peertube_instances: [], + theme_name: str, max_like_count: int, + cw_lists: {}) -> bool: + """Receives an Update activity within the POST section of HTTPServer """ if message_json['type'] != 'Update': return False if not has_actor(message_json, debug): return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if not has_users_path(message_json['actor']): if debug: @@ -1006,6 +1137,30 @@ def _receive_update_activity(recent_posts_cache: {}, session, base_dir: str, if debug: print('DEBUG: Question update was received') return True + elif message_json['object']['type'] == 'Note': + if message_json['object'].get('id'): + domain_full = get_full_domain(domain, port) + if _receive_edit_to_post(recent_posts_cache, message_json, + base_dir, nickname, domain, + max_mentions, max_emoji, + allow_local_network_access, + debug, system_language, http_prefix, + domain_full, person_cache, + signing_priv_key_pem, + max_recent_posts, translate, + session, cached_webfingers, port, + allow_deletion, + yt_replace_domain, + twitter_replacement_domain, + show_published_date_only, + peertube_instances, + theme_name, max_like_count, + cw_lists): + print('EDITPOST: received ' + message_json['object']['id']) + return True + else: + print('EDITPOST: rejected ' + str(message_json)) + return False if message_json['object']['type'] == 'Person' or \ message_json['object']['type'] == 'Application' or \ @@ -1186,7 +1341,7 @@ def _receive_undo_like(recent_posts_cache: {}, return False if not has_actor(message_json, debug): return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if message_json['object']['type'] != 'Like': return False @@ -1458,7 +1613,7 @@ def _receive_undo_reaction(recent_posts_cache: {}, return False if not has_actor(message_json, debug): return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if message_json['object']['type'] != 'EmojiReact': return False @@ -1599,7 +1754,7 @@ def _receive_bookmark(recent_posts_cache: {}, if debug: print('DEBUG: no target in inbox bookmark Add') return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if not isinstance(message_json['target'], str): if debug: @@ -1716,7 +1871,7 @@ def _receive_undo_bookmark(recent_posts_cache: {}, if debug: print('DEBUG: no target in inbox undo bookmark Remove') return False - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return False if not isinstance(message_json['target'], str): if debug: @@ -2318,6 +2473,20 @@ def _valid_post_content(base_dir: str, nickname: str, domain: str, if not valid_post_date(published, 90, debug): return False + # if the post has been edited then check its edit date + if message_json['object'].get('updated'): + published_update = message_json['object']['updated'] + if 'T' not in published_update: + return False + if 'Z' not in published_update: + return False + if '.' in published_update: + # converts 2022-03-30T17:37:58.734Z into 2022-03-30T17:37:58Z + published_update = published_update.split('.')[0] + 'Z' + message_json['object']['updated'] = published_update + if not valid_post_date(published_update, 90, debug): + return False + summary = None if message_json['object'].get('summary'): summary = message_json['object']['summary'] @@ -3706,10 +3875,6 @@ def _inbox_after_initial(server, person_cache, post_json_object, debug, signing_priv_key_pem) - if json_obj.get('updated') and json_obj.get('id'): - updated_post_id = remove_id_ending(json_obj['id']) - print('Receiving edit to post ' + updated_post_id) - # save the post to file if save_json(post_json_object, destination_filename): if mitm: @@ -4748,7 +4913,19 @@ def run_inbox_queue(server, queue_json['post'], federation_list, queue_json['postNickname'], - debug): + debug, + max_mentions, max_emoji, + allow_local_network_access, + system_language, + signing_priv_key_pem, + max_recent_posts, translate, + allow_deletion, + yt_replace_domain, + twitter_replacement_domain, + show_published_date_only, + peertube_instances, + theme_name, max_like_count, + cw_lists): if debug: print('Queue: Update accepted from ' + key_id) if os.path.isfile(queue_filename): diff --git a/like.py b/like.py index 24aebf0b7..425d20c6c 100644 --- a/like.py +++ b/like.py @@ -11,7 +11,7 @@ import os from pprint import pprint from utils import has_object_string from utils import has_object_string_object -from utils import has_object_stringType +from utils import has_object_string_type from utils import remove_domain_port from utils import has_object_dict from utils import has_users_path @@ -395,7 +395,7 @@ def outbox_undo_like(recent_posts_cache: {}, return if not message_json['type'] == 'Undo': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Like': if debug: diff --git a/outbox.py b/outbox.py index db2f8877e..4e751734d 100644 --- a/outbox.py +++ b/outbox.py @@ -16,7 +16,7 @@ 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_account_timezone -from utils import has_object_stringType +from utils import has_object_string_type from utils import get_base_content_from_post from utils import has_object_dict from utils import get_local_network_addresses @@ -76,7 +76,7 @@ def _person_receive_update_outbox(recent_posts_cache: {}, return if message_json['type'] != 'Update': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not isinstance(message_json['object']['type'], str): if debug: diff --git a/posts.py b/posts.py index 09019cbbe..45f2a12f8 100644 --- a/posts.py +++ b/posts.py @@ -35,7 +35,7 @@ from languages import understood_post_language from utils import get_summary_from_post from utils import get_user_paths from utils import invalid_ciphertext -from utils import has_object_stringType +from utils import has_object_string_type from utils import remove_id_ending from utils import replace_users_with_at from utils import has_group_type @@ -2995,7 +2995,7 @@ def _is_profile_update(post_json_object: {}) -> bool: for actor updates there is no 'to' within the object """ if post_json_object.get('type'): - if has_object_stringType(post_json_object, False): + if has_object_string_type(post_json_object, False): if (post_json_object['type'] == 'Update' and (post_json_object['object']['type'] == 'Person' or post_json_object['object']['type'] == 'Application' or @@ -3038,7 +3038,7 @@ def _send_to_named_addresses(server, session, session_onion, session_i2p, pprint(post_json_object) print('DEBUG: ' + 'no "to" field when sending to named addresses') - if has_object_stringType(post_json_object, debug): + if has_object_string_type(post_json_object, debug): if post_json_object['object']['type'] == 'Follow' or \ post_json_object['object']['type'] == 'Join': post_json_obj2 = post_json_object['object']['object'] @@ -3257,7 +3257,7 @@ def _sending_profile_update(post_json_object: {}) -> bool: """ if post_json_object['type'] != 'Update': return False - if not has_object_stringType(post_json_object, False): + if not has_object_string_type(post_json_object, False): return False activity_type = post_json_object['object']['type'] if activity_type in ('Person', 'Application', 'Group', 'Service'): diff --git a/reaction.py b/reaction.py index 7dc2439b1..b8f530caa 100644 --- a/reaction.py +++ b/reaction.py @@ -13,7 +13,7 @@ import urllib.parse from pprint import pprint from utils import has_object_string from utils import has_object_string_object -from utils import has_object_stringType +from utils import has_object_string_type from utils import remove_domain_port from utils import has_object_dict from utils import has_users_path @@ -415,7 +415,7 @@ def outbox_undo_reaction(recent_posts_cache: {}, return if not message_json['type'] == 'Undo': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'EmojiReact': if debug: diff --git a/shares.py b/shares.py index af73be24d..d25373ed6 100644 --- a/shares.py +++ b/shares.py @@ -22,7 +22,7 @@ from posts import get_person_box from session import post_json from session import post_image from session import create_session -from utils import has_object_stringType +from utils import has_object_string_type from utils import date_string_to_seconds from utils import date_seconds_to_string from utils import get_config_param @@ -1040,7 +1040,7 @@ def outbox_share_upload(base_dir: str, http_prefix: str, return if not message_json['type'] == 'Add': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Offer': if debug: @@ -1109,7 +1109,7 @@ def outbox_undo_share_upload(base_dir: str, http_prefix: str, return if not message_json['type'] == 'Remove': return - if not has_object_stringType(message_json, debug): + if not has_object_string_type(message_json, debug): return if not message_json['object']['type'] == 'Offer': if debug: diff --git a/utils.py b/utils.py index 7a93dd934..ffc1d721c 100644 --- a/utils.py +++ b/utils.py @@ -1680,8 +1680,8 @@ def remove_post_from_cache(post_json_object: {}, del recent_posts_cache['html'][post_id] -def _delete_cached_html(base_dir: str, nickname: str, domain: str, - post_json_object: {}): +def delete_cached_html(base_dir: str, nickname: str, domain: str, + post_json_object: {}): """Removes cached html file for the given post """ cached_post_filename = \ @@ -1691,7 +1691,7 @@ def _delete_cached_html(base_dir: str, nickname: str, domain: str, try: os.remove(cached_post_filename) except OSError: - print('EX: _delete_cached_html ' + + print('EX: delete_cached_html ' + 'unable to delete cached post file ' + str(cached_post_filename)) @@ -1842,7 +1842,7 @@ def delete_post(base_dir: str, http_prefix: str, str(ext_filename)) # remove cached html version of the post - _delete_cached_html(base_dir, nickname, domain, post_json_object) + delete_cached_html(base_dir, nickname, domain, post_json_object) has_object = False if post_json_object.get('object'): @@ -2206,7 +2206,7 @@ def is_blog_post(post_json_object: {}) -> bool: return False if not has_object_dict(post_json_object): return False - if not has_object_stringType(post_json_object, False): + if not has_object_string_type(post_json_object, False): return False if not post_json_object['object'].get('content'): return False @@ -3353,12 +3353,12 @@ def has_actor(post_json_object: {}, debug: bool) -> bool: return False -def has_object_stringType(post_json_object: {}, debug: bool) -> bool: +def has_object_string_type(post_json_object: {}, debug: bool) -> bool: """Does the given post have a type field within an object dict? """ if not has_object_dict(post_json_object): if debug: - print('has_object_stringType no object found') + print('has_object_string_type no object found') return False if post_json_object['object'].get('type'): if isinstance(post_json_object['object']['type'], str): @@ -3377,7 +3377,7 @@ def has_object_string_object(post_json_object: {}, debug: bool) -> bool: """ if not has_object_dict(post_json_object): if debug: - print('has_object_stringType no object found') + print('has_object_string_type no object found') return False if post_json_object['object'].get('object'): if isinstance(post_json_object['object']['object'], str):