epicyon/daemon_post_moderator.py

337 lines
16 KiB
Python

__filename__ = "daemon_post_moderator.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.5.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Core POST"
import os
import errno
import urllib.parse
from socket import error as SocketError
from utils import data_dir
from utils import delete_post
from utils import locate_post
from utils import get_full_domain
from utils import get_domain_from_actor
from utils import get_nickname_from_actor
from utils import get_instance_url
from posts import is_moderator
from httpcodes import write2
from httpheaders import redirect_headers
from httpheaders import login_headers
from webapp_moderation import html_account_info
from webapp_moderation import html_moderation_info
from person import can_remove_post
from person import remove_account
from person import suspend_account
from person import reenable_account
from filters import add_global_filter
from filters import remove_global_filter
from cache import clear_actor_cache
from blocking import add_global_block
from blocking import update_blocked_cache
from blocking import remove_global_block
def moderator_actions(self, path: str, calling_domain: str, cookie: str,
base_dir: str, http_prefix: str,
domain: str, port: int, debug: bool,
domain_full: str,
onion_domain: str, i2p_domain: str,
translate: {},
system_language: str,
signing_priv_key_pem: str,
block_federated: [],
theme_name: str,
access_keys: {}, person_cache: {},
recent_posts_cache: {},
blocked_cache: {},
mitm_servers: []) -> None:
"""Actions on the moderator screen
"""
users_path = path.replace('/moderationaction', '')
nickname = users_path.replace('/users/', '')
actor_str = \
get_instance_url(calling_domain,
http_prefix, domain_full,
onion_domain, i2p_domain) + \
users_path
if not is_moderator(base_dir, nickname):
redirect_headers(self, actor_str + '/moderation',
cookie, calling_domain, 303)
self.server.postreq_busy = False
return
length = int(self.headers['Content-length'])
try:
moderation_params = self.rfile.read(length).decode('utf-8')
except SocketError as ex:
if ex.errno == errno.ECONNRESET:
print('EX: POST moderation_params connection was reset')
else:
print('EX: POST moderation_params ' +
'rfile.read socket error')
self.send_response(400)
self.end_headers()
self.server.postreq_busy = False
return
except ValueError as ex:
print('EX: POST moderation_params rfile.read failed, ' +
str(ex))
self.send_response(400)
self.end_headers()
self.server.postreq_busy = False
return
if '&' in moderation_params:
moderation_text = None
moderation_button = None
# get the moderation text first
act_str = 'moderationAction='
for moderation_str in moderation_params.split('&'):
if moderation_str.startswith(act_str):
if act_str in moderation_str:
moderation_text = \
moderation_str.split(act_str)[1].strip()
mod_text = moderation_text.replace('+', ' ')
moderation_text = \
urllib.parse.unquote_plus(mod_text.strip())
# which button was pressed?
for moderation_str in moderation_params.split('&'):
if moderation_str.startswith('submitInfo='):
if not moderation_text and \
'submitInfo=' in moderation_str:
moderation_text = \
moderation_str.split('submitInfo=')[1].strip()
mod_text = moderation_text.replace('+', ' ')
moderation_text = \
urllib.parse.unquote_plus(mod_text.strip())
search_handle = moderation_text
if search_handle:
if '/@' in search_handle and \
'/@/' not in search_handle:
search_nickname = \
get_nickname_from_actor(search_handle)
if search_nickname:
search_domain, _ = \
get_domain_from_actor(search_handle)
if search_domain:
search_handle = \
search_nickname + '@' + search_domain
else:
search_handle = ''
else:
search_handle = ''
if '@' not in search_handle or \
'/@/' in search_handle:
if search_handle.startswith('http') or \
search_handle.startswith('ipfs') or \
search_handle.startswith('ipns'):
search_nickname = \
get_nickname_from_actor(search_handle)
if search_nickname:
search_domain, _ = \
get_domain_from_actor(search_handle)
if search_domain:
search_handle = \
search_nickname + '@' + \
search_domain
else:
search_handle = ''
else:
search_handle = ''
if '@' not in search_handle:
# is this a local nickname on this instance?
local_handle = \
search_handle + '@' + domain
dir_str = data_dir(base_dir)
if os.path.isdir(dir_str + '/' + local_handle):
search_handle = local_handle
else:
search_handle = ''
if search_handle is None:
search_handle = ''
if '@' in search_handle:
msg = \
html_account_info(translate,
base_dir, http_prefix,
nickname,
domain,
search_handle,
debug,
system_language,
signing_priv_key_pem,
None,
block_federated,
mitm_servers)
else:
msg = \
html_moderation_info(translate,
base_dir, nickname,
domain,
theme_name,
access_keys)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html',
msglen, calling_domain)
write2(self, msg)
self.server.postreq_busy = False
return
if moderation_str.startswith('submitBlock'):
moderation_button = 'block'
elif moderation_str.startswith('submitUnblock'):
moderation_button = 'unblock'
elif moderation_str.startswith('submitFilter'):
moderation_button = 'filter'
elif moderation_str.startswith('submitUnfilter'):
moderation_button = 'unfilter'
elif moderation_str.startswith('submitClearCache'):
moderation_button = 'clearcache'
elif moderation_str.startswith('submitSuspend'):
moderation_button = 'suspend'
elif moderation_str.startswith('submitUnsuspend'):
moderation_button = 'unsuspend'
elif moderation_str.startswith('submitRemove'):
moderation_button = 'remove'
if moderation_button and moderation_text:
if debug:
print('moderation_button: ' + moderation_button)
print('moderation_text: ' + moderation_text)
nickname = moderation_text
if nickname.startswith('http') or \
nickname.startswith('ipfs') or \
nickname.startswith('ipns') or \
nickname.startswith('hyper'):
nickname = get_nickname_from_actor(nickname)
if '@' in nickname:
nickname = nickname.split('@')[0]
if moderation_button == 'suspend':
suspend_account(base_dir, nickname, domain)
if moderation_button == 'unsuspend':
reenable_account(base_dir, nickname)
if moderation_button == 'filter':
add_global_filter(base_dir, moderation_text)
if moderation_button == 'unfilter':
remove_global_filter(base_dir, moderation_text)
if moderation_button == 'clearcache':
clear_actor_cache(base_dir, person_cache,
moderation_text)
if moderation_button == 'block':
full_block_domain = None
moderation_text = moderation_text.strip()
moderation_reason = None
if ' ' in moderation_text:
moderation_domain = moderation_text.split(' ', 1)[0]
moderation_reason = moderation_text.split(' ', 1)[1]
else:
moderation_domain = moderation_text
if moderation_domain.startswith('http') or \
moderation_domain.startswith('ipfs') or \
moderation_domain.startswith('ipns') or \
moderation_domain.startswith('hyper'):
# https://domain
block_domain, block_port = \
get_domain_from_actor(moderation_domain)
if block_domain:
full_block_domain = \
get_full_domain(block_domain, block_port)
if '@' in moderation_domain:
# nick@domain or *@domain
full_block_domain = \
moderation_domain.split('@')[1]
else:
# assume the text is a domain name
if not full_block_domain and '.' in moderation_domain:
nickname = '*'
full_block_domain = \
moderation_domain.strip()
if full_block_domain or nickname.startswith('#'):
if nickname.startswith('#') and ' ' in nickname:
nickname = nickname.split(' ')[0]
add_global_block(base_dir, nickname,
full_block_domain, moderation_reason)
blocked_cache_last_updated = \
self.server.blocked_cache_last_updated
self.server.blocked_cache_last_updated = \
update_blocked_cache(base_dir, blocked_cache,
blocked_cache_last_updated, 0)
if moderation_button == 'unblock':
full_block_domain = None
if ' ' in moderation_text:
moderation_domain = moderation_text.split(' ', 1)[0]
else:
moderation_domain = moderation_text
if moderation_domain.startswith('http') or \
moderation_domain.startswith('ipfs') or \
moderation_domain.startswith('ipns') or \
moderation_domain.startswith('hyper'):
# https://domain
block_domain, block_port = \
get_domain_from_actor(moderation_domain)
if block_domain:
full_block_domain = \
get_full_domain(block_domain, block_port)
if '@' in moderation_domain:
# nick@domain or *@domain
full_block_domain = moderation_domain.split('@')[1]
else:
# assume the text is a domain name
if not full_block_domain and '.' in moderation_domain:
nickname = '*'
full_block_domain = moderation_domain.strip()
if full_block_domain or nickname.startswith('#'):
if nickname.startswith('#') and ' ' in nickname:
nickname = nickname.split(' ')[0]
remove_global_block(base_dir, nickname,
full_block_domain)
blocked_cache_last_updated = \
self.server.blocked_cache_last_updated
self.server.blocked_cache_last_updated = \
update_blocked_cache(base_dir, blocked_cache,
blocked_cache_last_updated, 0)
if moderation_button == 'remove':
if '/statuses/' not in moderation_text:
remove_account(base_dir, nickname, domain, port)
else:
# remove a post or thread
post_filename = \
locate_post(base_dir, nickname, domain,
moderation_text)
if post_filename:
if can_remove_post(base_dir, domain, port,
moderation_text):
delete_post(base_dir,
http_prefix,
nickname, domain,
post_filename,
debug,
recent_posts_cache,
True)
if nickname != 'news':
# if this is a local blog post then also remove it
# from the news actor
post_filename = \
locate_post(base_dir, 'news', domain,
moderation_text)
if post_filename:
if can_remove_post(base_dir, domain, port,
moderation_text):
delete_post(base_dir,
http_prefix,
'news', domain,
post_filename,
debug,
recent_posts_cache,
True)
redirect_headers(self, actor_str + '/moderation',
cookie, calling_domain, 303)
self.server.postreq_busy = False
return