Merge branch 'main' of gitlab.com:bashrc2/epicyon

merge-requests/30/head
Bob Mottram 2022-05-01 18:31:42 +01:00
commit 9cf69b1911
10 changed files with 252 additions and 105 deletions

View File

@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Core"
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer, HTTPServer
import copy
import sys
import json
import time
@ -2362,7 +2363,8 @@ class PubServer(BaseHTTPRequestHandler):
nickname, domain,
post_filename,
debug,
self.server.recent_posts_cache)
self.server.recent_posts_cache,
True)
if nickname != 'news':
# if this is a local blog post then also remove it
# from the news actor
@ -2378,7 +2380,8 @@ class PubServer(BaseHTTPRequestHandler):
'news', domain,
post_filename,
debug,
self.server.recent_posts_cache)
self.server.recent_posts_cache,
True)
self._redirect_headers(actor_str + '/moderation',
cookie, calling_domain)
@ -8654,7 +8657,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.server.iconsCache.get('repeat_inactive.png'):
del self.server.iconsCache['repeat_inactive.png']
# delete the announce post
# delete the announce post
if '?unannounce=' in path:
announce_url = path.split('?unannounce=')[1]
if '?' in announce_url:
@ -8668,7 +8671,7 @@ class PubServer(BaseHTTPRequestHandler):
if post_filename:
delete_post(base_dir, http_prefix,
nickname, domain, post_filename,
debug, recent_posts_cache)
debug, recent_posts_cache, True)
self._post_to_outbox(new_undo_announce,
self.server.project_version,
@ -19398,12 +19401,12 @@ class PubServer(BaseHTTPRequestHandler):
np_thread.kill()
# make a copy of self.headers
headers = {}
headers_without_cookie = {}
for dict_entry_name, header_line in self.headers.items():
headers[dict_entry_name] = header_line
if dict_entry_name.lower() != 'cookie':
headers_without_cookie[dict_entry_name] = header_line
headers = copy.deepcopy(self.headers)
headers_without_cookie = copy.deepcopy(headers)
if 'cookie' in headers_without_cookie:
del headers_without_cookie['cookie']
if 'Cookie' in headers_without_cookie:
del headers_without_cookie['Cookie']
print('New post headers: ' + str(headers_without_cookie))
length = int(headers['Content-Length'])

View File

@ -170,7 +170,7 @@ def outbox_delete(base_dir: str, http_prefix: str,
print(message_id)
return True
delete_post(base_dir, http_prefix, delete_nickname, delete_domain,
post_filename, debug, recent_posts_cache)
post_filename, debug, recent_posts_cache, True)
if debug:
print('DEBUG: post deleted via c2s - ' + post_filename)

View File

@ -581,13 +581,16 @@ input[type=submit]:hover {
}
.timeline-avatar {
margin: 10px auto;
margin: 0px 0px;
padding: 0px 0px;
width: 8%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 80%;
width: 6%;
float: left;
}
.search-result-text {
@ -795,11 +798,15 @@ div.gallery img {
}
.timeline-avatar img {
opacity: 1.0;
width: 8%;
height: 8%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.cwButton {
@ -1149,13 +1156,28 @@ div.gallery img {
margin: 1% 3%;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.cwButton {
@ -1496,13 +1518,28 @@ div.gallery img {
margin: 1% 3%;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.cwButton {

View File

@ -582,8 +582,10 @@ input[type=submit]:hover {
}
.timeline-avatar {
margin: 10px auto;
margin: 0px 0px;
padding: 0px 0px;
width: 8%;
float: left;
}
.timeline-avatar:hover {
@ -592,7 +594,8 @@ input[type=submit]:hover {
.timeline-avatar-reply {
padding: 0px 0px;
width: 80%;
width: 6%;
float: left;
}
.search-result-text {
@ -1054,11 +1057,15 @@ aside .toggle-inside li {
}
.timeline-avatar img {
opacity: 1.0;
width: 8%;
height: 8%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {
@ -1523,13 +1530,28 @@ aside .toggle-inside li {
margin: 1% 3%;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {
@ -1986,13 +2008,28 @@ aside .toggle-inside li {
margin: 1% 3%;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {

View File

@ -885,8 +885,10 @@ input[type=submit]:hover {
}
.timeline-avatar {
margin: 10px auto;
margin: 0px 0px;
padding: 0px 0px;
width: 8%;
float: left;
}
.timeline-avatar:hover {
@ -895,7 +897,8 @@ input[type=submit]:hover {
.timeline-avatar-reply {
padding: 0px 0px;
width: 80%;
width: 6%;
float: left;
}
.search-result-text {
@ -1473,11 +1476,15 @@ h3 {
}
.timeline-avatar img {
opacity: 1.0;
width: 8%;
height: 8%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {
@ -2228,13 +2235,28 @@ h3 {
margin-right: 0px;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {
@ -2988,13 +3010,28 @@ h3 {
margin-right: 0px;
border-radius: 0%;
}
.timeline-avatar {
margin: 0px 0px;
padding: 0px 0px;
width: 15%;
float: left;
}
.timeline-avatar-reply {
padding: 0px 0px;
width: 12%;
float: left;
}
.timeline-avatar img {
opacity: 1.0;
width: 15%;
height: 15%;
width: 100%;
padding: 0px 0px;
-ms-transform: translateY(-10%);
transform: translateY(-10%);
border-radius: var(--avatar-rounding);
}
.timeline-avatar-reply img {
opacity: 1.0;
width: 80%;
padding: 0px 0px;
margin: 0px 10%;
border-radius: var(--avatar-rounding);
}
.buttonevent {

View File

@ -1276,7 +1276,7 @@ def dav_delete_response(base_dir: str, nickname: str, domain: str,
token_post_id)
delete_post(base_dir, http_prefix,
nickname, domain, post_filename,
debug, recent_posts_cache)
debug, recent_posts_cache, True)
return 'Ok'

View File

@ -2058,7 +2058,7 @@ def _receive_delete(session, handle: str, is_group: bool, base_dir: str,
return True
delete_post(base_dir, http_prefix, handle_nickname,
handle_domain, post_filename, debug,
recent_posts_cache)
recent_posts_cache, True)
if debug:
print('DEBUG: post deleted - ' + post_filename)
@ -2069,7 +2069,7 @@ def _receive_delete(session, handle: str, is_group: bool, base_dir: str,
if post_filename:
delete_post(base_dir, http_prefix, 'news',
handle_domain, post_filename, debug,
recent_posts_cache)
recent_posts_cache, True)
if debug:
print('DEBUG: blog post deleted - ' + post_filename)
return True
@ -4094,7 +4094,7 @@ def _inbox_after_initial(server, inbox_start_time,
if edited_filename != destination_filename:
delete_post(base_dir, http_prefix,
nickname, domain, edited_filename,
debug, recent_posts_cache)
debug, recent_posts_cache, True)
# update the indexes for different timelines
for boxname in update_index_list:

View File

@ -4294,7 +4294,7 @@ def archive_posts_for_person(http_prefix: str, nickname: str, domain: str,
'.json.' + ext))
else:
delete_post(base_dir, http_prefix, nickname, domain,
file_path, False, recent_posts_cache)
file_path, False, recent_posts_cache, False)
# remove cached html posts
post_cache_filename = \

View File

@ -3393,11 +3393,14 @@ def test_client_to_server(base_dir: str):
inbox_path = bob_dir + '/accounts/bob@' + bob_domain + '/inbox'
outbox_path = alice_dir + '/accounts/alice@' + alice_domain + '/outbox'
posts_before = \
bob_posts_before = \
len([name for name in os.listdir(inbox_path)
if os.path.isfile(os.path.join(inbox_path, name))])
alice_posts_before = \
len([name for name in os.listdir(outbox_path)
if os.path.isfile(os.path.join(outbox_path, name))])
print('\n\nEVENT: Alice deletes her post: ' + outbox_post_id + ' ' +
str(posts_before))
str(alice_posts_before))
password = 'alicepass'
send_delete_via_server(alice_dir, session_alice, 'alice', password,
alice_domain, alice_port,
@ -3408,14 +3411,20 @@ def test_client_to_server(base_dir: str):
if os.path.isdir(inbox_path):
test = len([name for name in os.listdir(inbox_path)
if os.path.isfile(os.path.join(inbox_path, name))])
if test == posts_before-1:
if test == bob_posts_before-1:
break
time.sleep(1)
test = len([name for name in os.listdir(inbox_path)
if os.path.isfile(os.path.join(inbox_path, name))])
assert test == posts_before - 1
print(">>> post deleted from Alice's outbox and Bob's inbox")
assert test == bob_posts_before - 1
print(">>> post was deleted from Bob's inbox")
test = len([name for name in os.listdir(outbox_path)
if os.path.isfile(os.path.join(outbox_path, name))])
# this should be unchanged because a delete post was added
# at the outbox and one was removed
assert test == alice_posts_before
print(">>> post deleted from Alice's outbox")
assert valid_inbox(bob_dir, 'bob', bob_domain)
assert valid_inbox_filenames(bob_dir, 'bob', bob_domain,
alice_domain, alice_port)

108
utils.py
View File

@ -91,6 +91,15 @@ def get_actor_languages_list(actor_json: {}) -> []:
return []
def has_object_dict(post_json_object: {}) -> bool:
"""Returns true if the given post has an object dict
"""
if post_json_object.get('object'):
if isinstance(post_json_object['object'], dict):
return True
return False
def get_content_from_post(post_json_object: {}, system_language: str,
languages_understood: [],
contentType: str = "content") -> str:
@ -1610,7 +1619,8 @@ def _is_reply_to_blog_post(base_dir: str, nickname: str, domain: str,
def _delete_post_remove_replies(base_dir: str, nickname: str, domain: str,
http_prefix: str, post_filename: str,
recent_posts_cache: {}, debug: bool) -> None:
recent_posts_cache: {}, debug: bool,
manual: bool) -> None:
"""Removes replies when deleting a post
"""
replies_filename = post_filename.replace('.json', '.replies')
@ -1626,7 +1636,7 @@ def _delete_post_remove_replies(base_dir: str, nickname: str, domain: str,
if os.path.isfile(reply_file):
delete_post(base_dir, http_prefix,
nickname, domain, reply_file, debug,
recent_posts_cache)
recent_posts_cache, manual)
# remove the replies file
try:
os.remove(replies_filename)
@ -1794,9 +1804,53 @@ def _delete_conversation_post(base_dir: str, nickname: str, domain: str,
str(conversation_filename))
def is_dm(post_json_object: {}) -> bool:
"""Returns true if the given post is a DM
"""
if post_json_object['type'] != 'Create':
return False
if not has_object_dict(post_json_object):
return False
if post_json_object['object']['type'] != 'ChatMessage':
if post_json_object['object']['type'] != 'Note' and \
post_json_object['object']['type'] != 'Page' and \
post_json_object['object']['type'] != 'Patch' and \
post_json_object['object']['type'] != 'EncryptedMessage' and \
post_json_object['object']['type'] != 'Article':
return False
if post_json_object['object'].get('moderationStatus'):
return False
fields = ('to', 'cc')
for field_name in fields:
if not post_json_object['object'].get(field_name):
continue
for to_address in post_json_object['object'][field_name]:
if to_address.endswith('#Public'):
return False
if to_address.endswith('followers'):
return False
return True
def _is_remote_dm(domain_full: str, post_json_object: {}) -> bool:
"""Is the given post a DM from a different domain?
"""
if not is_dm(post_json_object):
return False
this_post_json = post_json_object
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']:
return True
return False
def delete_post(base_dir: str, http_prefix: str,
nickname: str, domain: str, post_filename: str,
debug: bool, recent_posts_cache: {}) -> None:
debug: bool, recent_posts_cache: {},
manual: bool) -> None:
"""Recursively deletes a post and its replies and attachments
"""
post_json_object = load_json(post_filename, 1)
@ -1804,7 +1858,7 @@ def delete_post(base_dir: str, http_prefix: str,
# remove any replies
_delete_post_remove_replies(base_dir, nickname, domain,
http_prefix, post_filename,
recent_posts_cache, debug)
recent_posts_cache, debug, manual)
# finally, remove the post itself
try:
os.remove(post_filename)
@ -1814,6 +1868,13 @@ def delete_post(base_dir: str, http_prefix: str,
str(post_filename))
return
# don't allow DMs to be deleted if they came from a different instance
# otherwise this breaks expectations about how DMs should operate
# i.e. DMs should only be removed if they are manually deleted
if not manual:
if _is_remote_dm(domain, post_json_object):
return
# don't allow deletion of bookmarked posts
if _is_bookmarked(base_dir, nickname, domain, post_filename):
return
@ -1874,7 +1935,7 @@ def delete_post(base_dir: str, http_prefix: str,
# remove any replies
_delete_post_remove_replies(base_dir, nickname, domain,
http_prefix, post_filename,
recent_posts_cache, debug)
recent_posts_cache, debug, manual)
# finally, remove the post itself
try:
os.remove(post_filename)
@ -2769,34 +2830,6 @@ def is_chat_message(post_json_object: {}) -> bool:
return True
def is_dm(post_json_object: {}) -> bool:
"""Returns true if the given post is a DM
"""
if post_json_object['type'] != 'Create':
return False
if not has_object_dict(post_json_object):
return False
if post_json_object['object']['type'] != 'ChatMessage':
if post_json_object['object']['type'] != 'Note' and \
post_json_object['object']['type'] != 'Page' and \
post_json_object['object']['type'] != 'Patch' and \
post_json_object['object']['type'] != 'EncryptedMessage' and \
post_json_object['object']['type'] != 'Article':
return False
if post_json_object['object'].get('moderationStatus'):
return False
fields = ('to', 'cc')
for field_name in fields:
if not post_json_object['object'].get(field_name):
continue
for to_address in post_json_object['object'][field_name]:
if to_address.endswith('#Public'):
return False
if to_address.endswith('followers'):
return False
return True
def is_reply(post_json_object: {}, actor: str) -> bool:
"""Returns true if the given post is a reply to the given actor
"""
@ -3037,15 +3070,6 @@ def user_agent_domain(user_agent: str, debug: bool) -> str:
return agent_domain
def has_object_dict(post_json_object: {}) -> bool:
"""Returns true if the given post has an object dict
"""
if post_json_object.get('object'):
if isinstance(post_json_object['object'], dict):
return True
return False
def get_alt_path(actor: str, domain_full: str, calling_domain: str) -> str:
"""Returns alternate path from the actor
eg. https://clearnetdomain/path becomes http://oniondomain/path