2024-03-02 12:58:13 +00:00
|
|
|
__filename__ = "daemon_get_masto_api.py"
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
__license__ = "AGPL3+"
|
|
|
|
__version__ = "1.5.0"
|
|
|
|
__maintainer__ = "Bob Mottram"
|
|
|
|
__email__ = "bob@libreserver.org"
|
|
|
|
__status__ = "Production"
|
2024-03-23 15:11:05 +00:00
|
|
|
__module_group__ = "Core GET"
|
2024-03-02 12:58:13 +00:00
|
|
|
|
|
|
|
import json
|
|
|
|
from httpheaders import set_headers
|
|
|
|
from httpcodes import write2
|
|
|
|
from mastoapiv1 import masto_api_v1_response
|
|
|
|
from mastoapiv2 import masto_api_v2_response
|
|
|
|
from siteactive import referer_is_active
|
|
|
|
from httpcodes import http_400
|
|
|
|
from httpcodes import http_404
|
|
|
|
from httpcodes import http_503
|
|
|
|
from utils import get_json_content_from_accept
|
|
|
|
from utils import convert_domains
|
|
|
|
from utils import local_network_host
|
|
|
|
from crawlers import update_known_crawlers
|
|
|
|
from blocking import broch_mode_is_active
|
|
|
|
from daemon_utils import has_accept
|
|
|
|
|
|
|
|
|
|
|
|
def masto_api(self, path: str, calling_domain: str,
|
|
|
|
ua_str: str,
|
|
|
|
authorized: bool, http_prefix: str,
|
|
|
|
base_dir: str, nickname: str, domain: str,
|
|
|
|
domain_full: str,
|
|
|
|
onion_domain: str, i2p_domain: str,
|
|
|
|
translate: {},
|
|
|
|
registration: bool,
|
|
|
|
system_language: str,
|
|
|
|
project_version: str,
|
|
|
|
custom_emoji: [],
|
|
|
|
show_node_info_accounts: bool,
|
|
|
|
referer_domain: str, debug: bool,
|
|
|
|
known_crawlers: {},
|
2024-04-23 12:24:13 +00:00
|
|
|
sites_unavailable: [],
|
|
|
|
unit_test: bool,
|
|
|
|
allow_local_network_access: bool) -> bool:
|
2024-03-02 12:58:13 +00:00
|
|
|
if _masto_api_v2(self, path, calling_domain, ua_str, authorized,
|
|
|
|
http_prefix, base_dir, nickname, domain,
|
|
|
|
domain_full, onion_domain, i2p_domain,
|
|
|
|
translate, registration, system_language,
|
|
|
|
project_version,
|
|
|
|
show_node_info_accounts,
|
|
|
|
referer_domain, debug, 5,
|
2024-04-23 12:24:13 +00:00
|
|
|
known_crawlers, sites_unavailable, unit_test,
|
|
|
|
allow_local_network_access):
|
2024-03-02 12:58:13 +00:00
|
|
|
return True
|
|
|
|
return _masto_api_v1(self, path, calling_domain, ua_str, authorized,
|
|
|
|
http_prefix, base_dir, nickname, domain,
|
|
|
|
domain_full, onion_domain, i2p_domain,
|
|
|
|
translate, registration, system_language,
|
|
|
|
project_version, custom_emoji,
|
|
|
|
show_node_info_accounts,
|
|
|
|
referer_domain, debug, 5,
|
2024-04-23 12:24:13 +00:00
|
|
|
known_crawlers, sites_unavailable,
|
|
|
|
unit_test,
|
|
|
|
allow_local_network_access)
|
2024-03-02 12:58:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _masto_api_v1(self, path: str, calling_domain: str,
|
|
|
|
ua_str: str,
|
|
|
|
authorized: bool,
|
|
|
|
http_prefix: str,
|
|
|
|
base_dir: str, nickname: str, domain: str,
|
|
|
|
domain_full: str,
|
|
|
|
onion_domain: str, i2p_domain: str,
|
|
|
|
translate: {},
|
|
|
|
registration: bool,
|
|
|
|
system_language: str,
|
|
|
|
project_version: str,
|
|
|
|
custom_emoji: [],
|
|
|
|
show_node_info_accounts: bool,
|
|
|
|
referer_domain: str,
|
|
|
|
debug: bool,
|
|
|
|
calling_site_timeout: int,
|
|
|
|
known_crawlers: {},
|
2024-04-23 12:24:13 +00:00
|
|
|
sites_unavailable: [],
|
|
|
|
unit_test: bool,
|
|
|
|
allow_local_network_access: bool) -> bool:
|
2024-03-02 12:58:13 +00:00
|
|
|
"""This is a vestigil mastodon API for the purpose
|
|
|
|
of returning an empty result to sites like
|
|
|
|
https://mastopeek.app-dist.eu
|
|
|
|
"""
|
|
|
|
if not path.startswith('/api/v1/'):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if not referer_domain:
|
2024-04-23 12:24:13 +00:00
|
|
|
if not (debug and unit_test):
|
2024-03-02 12:58:13 +00:00
|
|
|
print('mastodon api request has no referer domain ' +
|
|
|
|
str(ua_str))
|
|
|
|
http_400(self)
|
|
|
|
return True
|
|
|
|
if referer_domain == domain_full:
|
|
|
|
print('mastodon api request from self')
|
|
|
|
http_400(self)
|
|
|
|
return True
|
|
|
|
if self.server.masto_api_is_active:
|
|
|
|
print('mastodon api is busy during request from ' +
|
|
|
|
referer_domain)
|
|
|
|
http_503(self)
|
|
|
|
return True
|
|
|
|
self.server.masto_api_is_active = True
|
|
|
|
# is this a real website making the call ?
|
2024-04-23 12:24:13 +00:00
|
|
|
if not debug and not unit_test and referer_domain:
|
2024-03-02 12:58:13 +00:00
|
|
|
# Does calling_domain look like a domain?
|
|
|
|
if ' ' in referer_domain or \
|
|
|
|
';' in referer_domain or \
|
|
|
|
'.' not in referer_domain:
|
|
|
|
print('mastodon api ' +
|
|
|
|
'referer does not look like a domain ' +
|
|
|
|
referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
2024-04-23 12:24:13 +00:00
|
|
|
if not allow_local_network_access:
|
2024-03-02 12:58:13 +00:00
|
|
|
if local_network_host(referer_domain):
|
|
|
|
print('mastodon api referer domain is from the ' +
|
|
|
|
'local network ' + referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
if not referer_is_active(http_prefix,
|
|
|
|
referer_domain, ua_str,
|
|
|
|
calling_site_timeout,
|
|
|
|
sites_unavailable):
|
|
|
|
print('mastodon api referer url is not active ' +
|
|
|
|
referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
|
|
|
|
print('mastodon api v1: ' + path)
|
|
|
|
print('mastodon api v1: authorized ' + str(authorized))
|
|
|
|
print('mastodon api v1: nickname ' + str(nickname))
|
|
|
|
print('mastodon api v1: referer ' + str(referer_domain))
|
|
|
|
crawl_time = \
|
|
|
|
update_known_crawlers(ua_str, base_dir,
|
|
|
|
known_crawlers,
|
|
|
|
self.server.last_known_crawler)
|
|
|
|
if crawl_time is not None:
|
|
|
|
self.server.last_known_crawler = crawl_time
|
|
|
|
|
|
|
|
broch_mode = broch_mode_is_active(base_dir)
|
|
|
|
send_json, send_json_str = \
|
|
|
|
masto_api_v1_response(path,
|
|
|
|
calling_domain,
|
|
|
|
ua_str,
|
|
|
|
authorized,
|
|
|
|
http_prefix,
|
|
|
|
base_dir,
|
|
|
|
nickname, domain,
|
|
|
|
domain_full,
|
|
|
|
onion_domain,
|
|
|
|
i2p_domain,
|
|
|
|
translate,
|
|
|
|
registration,
|
|
|
|
system_language,
|
|
|
|
project_version,
|
|
|
|
custom_emoji,
|
|
|
|
show_node_info_accounts,
|
|
|
|
broch_mode)
|
|
|
|
|
|
|
|
if send_json is not None:
|
|
|
|
msg_str = json.dumps(send_json)
|
|
|
|
msg_str = convert_domains(calling_domain, referer_domain,
|
|
|
|
msg_str, http_prefix, domain,
|
|
|
|
onion_domain, i2p_domain)
|
|
|
|
msg = msg_str.encode('utf-8')
|
|
|
|
msglen = len(msg)
|
|
|
|
if has_accept(self, calling_domain):
|
|
|
|
protocol_str = \
|
|
|
|
get_json_content_from_accept(self.headers.get('Accept'))
|
|
|
|
set_headers(self, protocol_str, msglen,
|
|
|
|
None, calling_domain, True)
|
|
|
|
else:
|
|
|
|
set_headers(self, 'application/ld+json', msglen,
|
|
|
|
None, calling_domain, True)
|
|
|
|
write2(self, msg)
|
|
|
|
if send_json_str:
|
|
|
|
print(send_json_str)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
|
|
|
|
# no api endpoints were matched
|
|
|
|
http_404(self, 1)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def _masto_api_v2(self, path: str, calling_domain: str,
|
|
|
|
ua_str: str,
|
|
|
|
authorized: bool,
|
|
|
|
http_prefix: str,
|
|
|
|
base_dir: str, nickname: str, domain: str,
|
|
|
|
domain_full: str,
|
|
|
|
onion_domain: str, i2p_domain: str,
|
|
|
|
translate: {},
|
|
|
|
registration: bool,
|
|
|
|
system_language: str,
|
|
|
|
project_version: str,
|
|
|
|
show_node_info_accounts: bool,
|
|
|
|
referer_domain: str,
|
|
|
|
debug: bool,
|
|
|
|
calling_site_timeout: int,
|
|
|
|
known_crawlers: {},
|
2024-04-23 12:24:13 +00:00
|
|
|
sites_unavailable: [],
|
|
|
|
unit_test: bool,
|
|
|
|
allow_local_network_access: bool) -> bool:
|
2024-03-02 12:58:13 +00:00
|
|
|
"""This is a vestigil mastodon v2 API for the purpose
|
|
|
|
of returning an empty result to sites like
|
|
|
|
https://mastopeek.app-dist.eu
|
|
|
|
"""
|
|
|
|
if not path.startswith('/api/v2/'):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if not referer_domain:
|
2024-04-23 12:24:13 +00:00
|
|
|
if not (debug and unit_test):
|
2024-03-02 12:58:13 +00:00
|
|
|
print('mastodon api v2 request has no referer domain ' +
|
|
|
|
str(ua_str))
|
|
|
|
http_400(self)
|
|
|
|
return True
|
|
|
|
if referer_domain == domain_full:
|
|
|
|
print('mastodon api v2 request from self')
|
|
|
|
http_400(self)
|
|
|
|
return True
|
|
|
|
if self.server.masto_api_is_active:
|
|
|
|
print('mastodon api v2 is busy during request from ' +
|
|
|
|
referer_domain)
|
|
|
|
http_503(self)
|
|
|
|
return True
|
|
|
|
self.server.masto_api_is_active = True
|
|
|
|
# is this a real website making the call ?
|
2024-04-23 12:24:13 +00:00
|
|
|
if not debug and not unit_test and referer_domain:
|
2024-03-02 12:58:13 +00:00
|
|
|
# Does calling_domain look like a domain?
|
|
|
|
if ' ' in referer_domain or \
|
|
|
|
';' in referer_domain or \
|
|
|
|
'.' not in referer_domain:
|
|
|
|
print('mastodon api v2 ' +
|
|
|
|
'referer does not look like a domain ' +
|
|
|
|
referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
2024-04-23 12:24:13 +00:00
|
|
|
if not allow_local_network_access:
|
2024-03-02 12:58:13 +00:00
|
|
|
if local_network_host(referer_domain):
|
|
|
|
print('mastodon api v2 referer domain is from the ' +
|
|
|
|
'local network ' + referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
if not referer_is_active(http_prefix,
|
|
|
|
referer_domain, ua_str,
|
|
|
|
calling_site_timeout,
|
|
|
|
sites_unavailable):
|
|
|
|
print('mastodon api v2 referer url is not active ' +
|
|
|
|
referer_domain)
|
|
|
|
http_400(self)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
|
|
|
|
print('mastodon api v2: ' + path)
|
|
|
|
print('mastodon api v2: authorized ' + str(authorized))
|
|
|
|
print('mastodon api v2: nickname ' + str(nickname))
|
|
|
|
print('mastodon api v2: referer ' + str(referer_domain))
|
|
|
|
crawl_time = \
|
|
|
|
update_known_crawlers(ua_str, base_dir,
|
|
|
|
known_crawlers,
|
|
|
|
self.server.last_known_crawler)
|
|
|
|
if crawl_time is not None:
|
|
|
|
self.server.last_known_crawler = crawl_time
|
|
|
|
|
|
|
|
broch_mode = broch_mode_is_active(base_dir)
|
|
|
|
send_json, send_json_str = \
|
|
|
|
masto_api_v2_response(path,
|
|
|
|
calling_domain,
|
|
|
|
ua_str,
|
|
|
|
http_prefix,
|
|
|
|
base_dir,
|
|
|
|
domain,
|
|
|
|
domain_full,
|
|
|
|
onion_domain,
|
|
|
|
i2p_domain,
|
|
|
|
translate,
|
|
|
|
registration,
|
|
|
|
system_language,
|
|
|
|
project_version,
|
|
|
|
show_node_info_accounts,
|
|
|
|
broch_mode)
|
|
|
|
|
|
|
|
if send_json is not None:
|
|
|
|
msg_str = json.dumps(send_json)
|
|
|
|
msg_str = convert_domains(calling_domain, referer_domain,
|
|
|
|
msg_str, http_prefix, domain,
|
|
|
|
onion_domain, i2p_domain)
|
|
|
|
msg = msg_str.encode('utf-8')
|
|
|
|
msglen = len(msg)
|
|
|
|
if has_accept(self, calling_domain):
|
|
|
|
protocol_str = \
|
|
|
|
get_json_content_from_accept(self.headers.get('Accept'))
|
|
|
|
set_headers(self, protocol_str, msglen,
|
|
|
|
None, calling_domain, True)
|
|
|
|
else:
|
|
|
|
set_headers(self, 'application/ld+json', msglen,
|
|
|
|
None, calling_domain, True)
|
|
|
|
write2(self, msg)
|
|
|
|
if send_json_str:
|
|
|
|
print(send_json_str)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|
|
|
|
|
|
|
|
# no api v2 endpoints were matched
|
|
|
|
http_404(self, 2)
|
|
|
|
self.server.masto_api_is_active = False
|
|
|
|
return True
|