epicyon/daemon_get.py

5694 lines
254 KiB
Python

__filename__ = "daemon_get.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.5.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Core"
import os
import time
import json
import urllib.parse
from siteactive import referer_is_active
from maps import map_format_from_tagmaps_path
from blog import html_edit_blog
from blog import html_blog_post
from blog import path_contains_blog_link
from blog import html_blog_view
from speaker import get_ssml_box
from follow import pending_followers_timeline_json
from blocking import broch_mode_is_active
from blocking import remove_global_block
from blocking import update_blocked_cache
from blocking import add_global_block
from blocking import blocked_timeline_json
from cache import get_person_from_cache
from webapp_moderation import html_account_info
from webapp_calendar import html_calendar_delete_confirm
from webapp_calendar import html_calendar
from webapp_hashtagswarm import html_search_hashtag_category
from webapp_minimalbutton import set_minimal
from webapp_minimalbutton import is_minimal
from webapp_search import html_search_emoji_text_entry
from webapp_search import html_search
from webapp_search import html_hashtag_search_remote
from webapp_column_left import html_links_mobile
from webapp_column_right import html_newswire_mobile
from webapp_theme_designer import html_theme_designer
from webapp_accesskeys import html_access_keys
from webapp_manual import html_manual
from webapp_specification import html_specification
from webapp_about import html_about
from webapp_tos import html_terms_of_service
from webapp_confirm import html_confirm_remove_shared_item
from webapp_welcome_profile import html_welcome_profile
from webapp_welcome_final import html_welcome_final
from webapp_welcome import html_welcome_screen
from webapp_welcome import is_welcome_screen_complete
from webapp_podcast import html_podcast_episode
from webapp_utils import get_default_path
from webapp_utils import csv_following_list
from webapp_utils import get_shares_collection
from webapp_utils import html_following_list
from webapp_utils import html_show_share
from webapp_login import html_login
from followerSync import update_followers_sync_cache
from securemode import secure_mode
from fitnessFunctions import sorted_watch_points
from fitnessFunctions import fitness_performance
from fitnessFunctions import html_watch_points_graph
from session import establish_session
from session import get_session_for_domains
from crawlers import blocked_user_agent
from daemon_utils import etag_exists
from daemon_utils import has_accept
from daemon_utils import show_person_options
from daemon_utils import is_authorized
from daemon_utils import get_user_agent
from httpheaders import set_headers_etag
from httpheaders import login_headers
from httpheaders import redirect_headers
from httprequests import request_icalendar
from httprequests import request_ssml
from httprequests import request_csv
from httprequests import request_http
from httpheaders import set_headers
from httpheaders import logout_headers
from httpheaders import logout_redirect
from httpcodes import http_200
from httpcodes import http_402
from httpcodes import http_403
from httpcodes import http_404
from httpcodes import http_304
from httpcodes import http_400
from httpcodes import http_503
from httpcodes import write2
from utils import user_agent_domain
from utils import local_network_host
from utils import permitted_dir
from utils import has_users_path
from utils import media_file_mime_type
from utils import is_image_file
from utils import is_artist
from utils import is_blog_post
from utils import replace_users_with_at
from utils import remove_id_ending
from utils import local_actor_url
from utils import load_json
from utils import acct_dir
from utils import get_instance_url
from utils import convert_domains
from utils import get_nickname_from_actor
from utils import get_json_content_from_accept
from utils import check_bad_path
from utils import corp_servers
from utils import decoded_host
from person import get_account_pub_key
from shares import actor_attached_shares
from shares import get_share_category
from shares import vf_proposal_from_id
from shares import authorize_shared_items
from shares import shares_catalog_endpoint
from shares import shares_catalog_account_endpoint
from shares import shares_catalog_csv_endpoint
from posts import is_moderator
from posts import get_pinned_post_as_json
from posts import outbox_message_create_wrap
from daemon_get_masto_api import masto_api
from daemon_get_favicon import show_cached_favicon
from daemon_get_favicon import get_favicon
from daemon_get_exports import get_exported_blocks
from daemon_get_exports import get_exported_theme
from daemon_get_pwa import progressive_web_app_manifest
from daemon_get_css import get_fonts
from daemon_get_css import get_style_sheet
from daemon_get_nodeinfo import get_nodeinfo
from daemon_get_hashtag import hashtag_search_rss2
from daemon_get_hashtag import hashtag_search_json2
from daemon_get_hashtag import hashtag_search2
from daemon_get_hashtag import get_hashtag_categories_feed2
from daemon_get_timeline import show_media_timeline
from daemon_get_timeline import show_blogs_timeline
from daemon_get_timeline import show_news_timeline
from daemon_get_timeline import show_features_timeline
from daemon_get_timeline import show_shares_timeline
from daemon_get_timeline import show_wanted_timeline
from daemon_get_timeline import show_bookmarks_timeline
from daemon_get_timeline import show_outbox_timeline
from daemon_get_timeline import show_mod_timeline
from daemon_get_timeline import show_dms
from daemon_get_timeline import show_replies
from daemon_get_timeline import show_inbox
from daemon_get_feeds import show_shares_feed
from daemon_get_feeds import show_following_feed
from daemon_get_feeds import show_moved_feed
from daemon_get_feeds import show_inactive_feed
from daemon_get_feeds import show_followers_feed
from daemon_get_buttons import announce_button
from daemon_get_buttons import announce_button_undo
from daemon_get_buttons import follow_approve_button
from daemon_get_buttons import follow_deny_button
from daemon_get_buttons import like_button
from daemon_get_buttons import like_button_undo
from daemon_get_buttons import reaction_button
from daemon_get_buttons import reaction_button_undo
from daemon_get_buttons import bookmark_button
from daemon_get_buttons import bookmark_button_undo
from daemon_get_buttons import delete_button
from daemon_get_buttons import mute_button
from daemon_get_buttons import mute_button_undo
from daemon_get_newswire import get_newswire_feed
from daemon_get_newswire import newswire_vote
from daemon_get_newswire import newswire_unvote
from daemon_get_newswire import edit_newswire2
from daemon_get_newswire import edit_news_post2
from daemon_get_rss import get_rss2feed
from daemon_get_rss import get_rss2site
from daemon_get_rss import get_rss3feed
from daemon_get_profile import show_person_profile
from daemon_get_profile import show_skills
from daemon_get_profile import show_roles
from daemon_get_profile import edit_profile2
from daemon_get_images import show_avatar_or_banner
from daemon_get_images import show_cached_avatar
from daemon_get_images import show_help_screen_image
from daemon_get_images import show_manual_image
from daemon_get_images import show_specification_image
from daemon_get_images import show_icon
from daemon_get_images import show_share_image
from daemon_get_images import show_media
from daemon_get_images import show_background_image
from daemon_get_images import show_default_profile_background
from daemon_get_images import column_image
from daemon_get_images import search_screen_banner
from daemon_get_images import show_qrcode
from daemon_get_images import show_emoji
from daemon_get_post import show_individual_post
from daemon_get_post import show_notify_post
from daemon_get_post import show_replies_to_post
from daemon_get_post import show_announcers_of_post
from daemon_get_post import show_likers_of_post
from daemon_get_post import show_individual_at_post
from daemon_get_post import show_new_post
from daemon_get_post import show_conversation_thread
from daemon_get_collections import get_featured_collection
from daemon_get_collections import get_featured_tags_collection
from daemon_get_collections import get_following_json
from daemon_get_webfinger import get_webfinger
from daemon_get_reactions import reaction_picker2
from daemon_get_instance_actor import show_instance_actor
from daemon_get_vcard import show_vcard
from daemon_get_blog import show_blog_page
from daemon_get_links import edit_links2
from daemon_get_login import redirect_to_login_screen
from daemon_get_login import show_login_screen
# Blogs can be longer, so don't show many per page
MAX_POSTS_IN_BLOGS_FEED = 4
# maximum number of posts to list in outbox feed
MAX_POSTS_IN_FEED = 12
# Maximum number of entries in returned rss.xml
MAX_POSTS_IN_RSS_FEED = 10
# reduced posts for media feed because it can take a while
MAX_POSTS_IN_MEDIA_FEED = 6
MAX_POSTS_IN_NEWS_FEED = 10
# number of item shares per page
SHARES_PER_PAGE = 12
# number of follows/followers per page
FOLLOWS_PER_PAGE = 6
# maximum number of posts in a hashtag feed
MAX_POSTS_IN_HASHTAG_FEED = 6
def daemon_http_get(self) -> None:
"""daemon handler for http GET
"""
if self.server.starting_daemon:
return
if check_bad_path(self.path):
http_400(self)
return
calling_domain = self.server.domain_full
if self.headers.get('Server'):
if self.headers['Server'] in corp_servers():
if self.server.debug:
print('Corporate leech bounced: ' + self.headers['Server'])
http_402(self)
return
if self.headers.get('Host'):
calling_domain = decoded_host(self.headers['Host'])
if self.server.onion_domain:
if calling_domain not in (self.server.domain,
self.server.domain_full,
self.server.onion_domain):
print('GET domain blocked: ' + calling_domain)
http_400(self)
return
elif self.server.i2p_domain:
if calling_domain not in (self.server.domain,
self.server.domain_full,
self.server.i2p_domain):
print('GET domain blocked: ' + calling_domain)
http_400(self)
return
else:
if calling_domain not in (self.server.domain,
self.server.domain_full):
print('GET domain blocked: ' + calling_domain)
http_400(self)
return
ua_str = get_user_agent(self)
if not _permitted_crawler_path(self.path):
block, self.server.blocked_cache_last_updated = \
blocked_user_agent(calling_domain, ua_str,
self.server.news_instance,
self.server.debug,
self.server.user_agents_blocked,
self.server.blocked_cache_last_updated,
self.server.base_dir,
self.server.blocked_cache,
self.server.block_federated,
self.server.blocked_cache_update_secs,
self.server.crawlers_allowed,
self.server.known_bots,
self.path, self.server.block_military)
if block:
http_400(self)
return
referer_domain = _get_referer_domain(self, ua_str)
curr_session, proxy_type = \
get_session_for_domains(self.server,
calling_domain, referer_domain)
getreq_start_time = time.time()
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'start', self.server.debug)
if show_vcard(self, self.server.base_dir,
self.path, calling_domain, referer_domain,
self.server.domain):
return
# getting the public key for an account
acct_pub_key_json = \
get_account_pub_key(self.path, self.server.person_cache,
self.server.base_dir,
self.server.domain, calling_domain,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain)
if acct_pub_key_json:
msg_str = json.dumps(acct_pub_key_json, ensure_ascii=False)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
# Since fediverse crawlers are quite active,
# make returning info to them high priority
# get nodeinfo endpoint
if get_nodeinfo(self, ua_str, calling_domain, referer_domain,
self.server.http_prefix, 5, self.server.debug,
self.server.base_dir,
self.server.unit_test,
self.server.domain_full,
self.path,
self.server.allow_local_network_access,
self.server.sites_unavailable,
self.server.known_crawlers,
self.server.onion_domain,
self.server.i2p_domain,
self.server.project_version,
self.server.show_node_info_version,
self.server.show_node_info_accounts,
self.server.registration,
self.server.domain):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_nodeinfo[calling_domain]',
self.server.debug)
if _security_txt(self, ua_str, calling_domain, referer_domain,
self.server.http_prefix, 5, self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_security_txt[calling_domain]',
self.server.debug)
# followers synchronization request
# See https://github.com/mastodon/mastodon/pull/14510
# https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-8fcf.md
if self.path.startswith('/users/') and \
self.path.endswith('/followers_synchronization'):
if self.server.followers_synchronization:
# only do one request at a time
http_503(self)
return
self.server.followers_synchronization = True
if self.server.debug:
print('DEBUG: followers synchronization request ' +
self.path + ' ' + calling_domain)
# check authorized fetch
if secure_mode(curr_session, proxy_type, False,
self.server, self.headers, self.path):
nickname = get_nickname_from_actor(self.path)
sync_cache = self.server.followers_sync_cache
sync_json, _ = \
update_followers_sync_cache(self.server.base_dir,
nickname,
self.server.domain,
self.server.http_prefix,
self.server.domain_full,
calling_domain,
sync_cache)
msg_str = json.dumps(sync_json, ensure_ascii=False)
msg_str = convert_domains(calling_domain, referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'application/json', msglen,
None, calling_domain, False)
write2(self, msg)
self.server.followers_synchronization = False
return
else:
# request was not signed
result_json = {
"error": "Request not signed"
}
msg_str = json.dumps(result_json, ensure_ascii=False)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
if 'json' in accept_str:
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
self.server.followers_synchronization = False
return
http_404(self, 110)
self.server.followers_synchronization = False
return
if self.path == '/logout':
if not self.server.news_instance:
msg = \
html_login(self.server.translate,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.system_language,
False, ua_str,
self.server.theme_name).encode('utf-8')
msglen = len(msg)
logout_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
else:
news_url = \
get_instance_url(calling_domain,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain) + \
'/users/news'
logout_redirect(self, news_url, calling_domain)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'logout',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show logout',
self.server.debug)
# replace https://domain/@nick with https://domain/users/nick
if self.path.startswith('/@'):
self.path = self.path.replace('/@', '/users/')
# replace https://domain/@nick/statusnumber
# with https://domain/users/nick/statuses/statusnumber
nickname = self.path.split('/users/')[1]
if '/' in nickname:
status_number_str = nickname.split('/')[1]
if status_number_str.isdigit():
nickname = nickname.split('/')[0]
self.path = \
self.path.replace('/users/' + nickname + '/',
'/users/' + nickname + '/statuses/')
# instance actor
if self.path in ('/actor', '/users/instance.actor', '/users/actor',
'/Actor', '/users/Actor'):
self.path = '/users/inbox'
if show_instance_actor(self, calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
None, self.server.debug,
self.server.enable_shared_inbox,
self.server.fitness):
return
http_404(self, 111)
return
# turn off dropdowns on new post screen
no_drop_down = False
if self.path.endswith('?nodropdown'):
no_drop_down = True
self.path = self.path.replace('?nodropdown', '')
# redirect music to #nowplaying list
if self.path == '/music' or self.path == '/NowPlaying':
self.path = '/tags/NowPlaying'
if self.server.debug:
print('DEBUG: GET from ' + self.server.base_dir +
' path: ' + self.path + ' busy: ' +
str(self.server.getreq_busy))
if self.server.debug:
print(str(self.headers))
cookie = None
if self.headers.get('Cookie'):
cookie = self.headers['Cookie']
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'get cookie',
self.server.debug)
if '/manifest.json' in self.path:
if has_accept(self, calling_domain):
if not request_http(self.headers, self.server.debug):
progressive_web_app_manifest(self, self.server.base_dir,
calling_domain,
referer_domain,
getreq_start_time,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain,
self.server.fitness,
self.server.debug)
return
else:
self.path = '/'
if '/browserconfig.xml' in self.path:
if has_accept(self, calling_domain):
_browser_config(self, calling_domain, referer_domain,
getreq_start_time)
return
# default newswire favicon, for links to sites which
# have no favicon
if not self.path.startswith('/favicons/'):
if 'newswire_favicon.ico' in self.path:
get_favicon(self, calling_domain, self.server.base_dir,
self.server.debug,
'newswire_favicon.ico',
self.server.iconsCache,
self.server.domain_full)
return
# favicon image
if 'favicon.ico' in self.path:
get_favicon(self, calling_domain, self.server.base_dir,
self.server.debug, 'favicon.ico',
self.server.iconsCache,
self.server.domain_full)
return
# check authorization
authorized = is_authorized(self)
if self.server.debug:
if authorized:
print('GET Authorization granted ' + self.path)
else:
print('GET Not authorized ' + self.path + ' ' +
str(self.headers))
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'isAuthorized',
self.server.debug)
if authorized and self.path.endswith('/bots.txt'):
known_bots_str = ''
for bot_name in self.server.known_bots:
known_bots_str += bot_name + '\n'
msg = known_bots_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/plain; charset=utf-8',
msglen, None, calling_domain, True)
write2(self, msg)
if self.server.debug:
print('Sent known bots: ' +
self.server.path + ' ' + calling_domain)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'get_known_bots',
self.server.debug)
return
if show_conversation_thread(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
self.server.debug,
self.server.session,
cookie, ua_str,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
self.server.account_timezone,
self.server.bold_reading,
self.server.translate,
self.server.project_version,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.access_keys,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.blocked_cache,
self.server.block_federated,
self.server.auto_cw_cache):
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_show_conversation_thread',
self.server.debug)
return
# show a shared item if it is listed within actor attachment
if self.path.startswith('/users/') and '/shareditems/' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
shared_item_display_name = self.path.split('/shareditems/')[1]
if not nickname or not shared_item_display_name:
http_404(self, 112)
return
if not has_accept(self, calling_domain):
print('DEBUG: shareditems 1')
http_404(self, 113)
return
# get the actor from the cache
actor = \
get_instance_url(calling_domain,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain) + \
'/users/' + nickname
actor_json = get_person_from_cache(self.server.base_dir, actor,
self.server.person_cache)
if not actor_json:
actor_filename = acct_dir(self.server.base_dir, nickname,
self.server.domain) + '.json'
if os.path.isfile(actor_filename):
actor_json = load_json(actor_filename, 1, 1)
if not actor_json:
print('DEBUG: shareditems 2 ' + actor)
http_404(self, 114)
return
attached_shares = actor_attached_shares(actor_json)
if not attached_shares:
print('DEBUG: shareditems 3 ' + str(actor_json['attachment']))
http_404(self, 115)
return
# is the given shared item in the list?
share_id = None
for share_href in attached_shares:
if not isinstance(share_href, str):
continue
if share_href.endswith(self.path):
share_id = share_href.replace('://', '___')
share_id = share_id.replace('/', '--')
break
if not share_id:
print('DEBUG: shareditems 4')
http_404(self, 116)
return
# show the shared item
print('DEBUG: shareditems 5 ' + share_id)
shares_file_type = 'shares'
if request_http(self.headers, self.server.debug):
# get the category for share_id
share_category = \
get_share_category(self.server.base_dir,
nickname, self.server.domain,
shares_file_type, share_id)
msg = \
html_show_share(self.server.base_dir,
self.server.domain, nickname,
self.server.http_prefix,
self.server.domain_full,
share_id, self.server.translate,
self.server.shared_items_federated_domains,
self.server.default_timeline,
self.server.theme_name, shares_file_type,
share_category, not authorized)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
None, calling_domain, True)
write2(self, msg)
return
print('DEBUG: shareditems 6 ' + share_id)
else:
# get json for the shared item in ValueFlows format
share_json = \
vf_proposal_from_id(self.server.base_dir,
nickname, self.server.domain,
shares_file_type, share_id,
actor)
if share_json:
msg_str = json.dumps(share_json)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'application/json', msglen,
None, calling_domain, True)
write2(self, msg)
return
print('DEBUG: shareditems 7 ' + share_id)
http_404(self, 117)
return
# shared items offers collection for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token
if self.path.startswith('/users/') and '/offers' in self.path:
offers_collection_authorized = authorized
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
page_number = 1
if '?page=' in self.path:
page_number_str = self.path.split('?page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
if not offers_collection_authorized:
if self.server.debug:
print('Offers collection access is not authorized. ' +
'Checking Authorization header')
# Check the authorization token
if self.headers.get('Origin') and \
self.headers.get('Authorization'):
permitted_domains = \
self.server.shared_items_federated_domains
shared_item_tokens = \
self.server.shared_item_federation_tokens
if authorize_shared_items(permitted_domains,
self.server.base_dir,
self.headers['Origin'],
calling_domain,
self.headers['Authorization'],
self.server.debug,
shared_item_tokens):
offers_collection_authorized = True
elif self.server.debug:
print('Authorization token refused for ' +
'offers collection federation')
# show offers collection for federation
offers_json = []
if has_accept(self, calling_domain) and \
offers_collection_authorized:
if self.server.debug:
print('Preparing offers collection')
domain_full = self.server.domain_full
http_prefix = self.server.http_prefix
if self.server.debug:
print('Offers collection for account: ' + nickname)
base_dir = self.server.base_dir
offers_items_per_page = 12
max_shares_per_account = offers_items_per_page
shared_items_federated_domains = \
self.server.shared_items_federated_domains
actor = \
local_actor_url(http_prefix, nickname, domain_full) + \
'/offers'
offers_json = \
get_shares_collection(actor, page_number,
offers_items_per_page, base_dir,
self.server.domain, nickname,
max_shares_per_account,
shared_items_federated_domains,
'shares')
msg_str = json.dumps(offers_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
if self.path.startswith('/users/') and '/blocked' in self.path:
blocked_collection_authorized = authorized
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
page_number = 1
if '?page=' in self.path:
page_number_str = self.path.split('?page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
# show blocked collection for the nickname
actor = \
local_actor_url(self.server.http_prefix,
nickname, self.server.domain_full)
actor += '/blocked'
blocked_json = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://purl.archive.org/socialweb/blocked"
],
"id": actor,
"type": "OrderedCollection",
"name": nickname + "'s Blocked Collection",
"orderedItems": []
}
if has_accept(self, calling_domain) and \
blocked_collection_authorized:
if self.server.debug:
print('Preparing blocked collection')
if self.server.debug:
print('Blocked collection for account: ' + nickname)
base_dir = self.server.base_dir
blocked_items_per_page = 12
blocked_json = \
blocked_timeline_json(actor, page_number,
blocked_items_per_page, base_dir,
nickname, self.server.domain)
msg_str = json.dumps(blocked_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
if self.path.startswith('/users/') and \
'/pendingFollowers' in self.path:
pending_collection_authorized = authorized
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
page_number = 1
if '?page=' in self.path:
page_number_str = self.path.split('?page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
# show pending followers collection for the nickname
actor = \
local_actor_url(self.server.http_prefix,
nickname, self.server.domain_full)
actor += '/pendingFollowers'
pending_json = {
"@context": [
"https://www.w3.org/ns/activitystreams"
],
"id": actor,
"type": "OrderedCollection",
"name": nickname + "'s Pending Followers",
"orderedItems": []
}
if has_accept(self, calling_domain) and \
pending_collection_authorized:
if self.server.debug:
print('Preparing pending followers collection')
if self.server.debug:
print('Pending followers collection for account: ' +
nickname)
base_dir = self.server.base_dir
pending_json = \
pending_followers_timeline_json(actor, base_dir, nickname,
self.server.domain)
msg_str = json.dumps(pending_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
# wanted items collection for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token
if self.path.startswith('/users/') and '/wanted' in self.path:
wanted_collection_authorized = authorized
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
page_number = 1
if '?page=' in self.path:
page_number_str = self.path.split('?page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
if not wanted_collection_authorized:
if self.server.debug:
print('Wanted collection access is not authorized. ' +
'Checking Authorization header')
# Check the authorization token
if self.headers.get('Origin') and \
self.headers.get('Authorization'):
permitted_domains = \
self.server.shared_items_federated_domains
shared_item_tokens = \
self.server.shared_item_federation_tokens
if authorize_shared_items(permitted_domains,
self.server.base_dir,
self.headers['Origin'],
calling_domain,
self.headers['Authorization'],
self.server.debug,
shared_item_tokens):
wanted_collection_authorized = True
elif self.server.debug:
print('Authorization token refused for ' +
'wanted collection federation')
# show wanted collection for federation
wanted_json = []
if has_accept(self, calling_domain) and \
wanted_collection_authorized:
if self.server.debug:
print('Preparing wanted collection')
domain_full = self.server.domain_full
http_prefix = self.server.http_prefix
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if self.server.debug:
print('Wanted collection for account: ' + nickname)
base_dir = self.server.base_dir
wanted_items_per_page = 12
max_shares_per_account = wanted_items_per_page
shared_items_federated_domains = \
self.server.shared_items_federated_domains
actor = \
local_actor_url(http_prefix, nickname, domain_full) + \
'/wanted'
wanted_json = \
get_shares_collection(actor, page_number,
wanted_items_per_page, base_dir,
self.server.domain, nickname,
max_shares_per_account,
shared_items_federated_domains,
'wanted')
msg_str = json.dumps(wanted_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
# shared items catalog for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token
if self.path.startswith('/catalog') or \
(self.path.startswith('/users/') and '/catalog' in self.path):
catalog_authorized = authorized
if not catalog_authorized:
if self.server.debug:
print('Catalog access is not authorized. ' +
'Checking Authorization header')
# Check the authorization token
if self.headers.get('Origin') and \
self.headers.get('Authorization'):
permitted_domains = \
self.server.shared_items_federated_domains
shared_item_tokens = \
self.server.shared_item_federation_tokens
if authorize_shared_items(permitted_domains,
self.server.base_dir,
self.headers['Origin'],
calling_domain,
self.headers['Authorization'],
self.server.debug,
shared_item_tokens):
catalog_authorized = True
elif self.server.debug:
print('Authorization token refused for ' +
'shared items federation')
elif self.server.debug:
print('No Authorization header is available for ' +
'shared items federation')
# show shared items catalog for federation
if has_accept(self, calling_domain) and catalog_authorized:
catalog_type = 'json'
headers = self.headers
debug = self.server.debug
if self.path.endswith('.csv') or request_csv(headers):
catalog_type = 'csv'
elif (self.path.endswith('.json') or
not request_http(headers, debug)):
catalog_type = 'json'
if self.server.debug:
print('Preparing DFC catalog in format ' + catalog_type)
if catalog_type == 'json':
# catalog as a json
if not self.path.startswith('/users/'):
if self.server.debug:
print('Catalog for the instance')
catalog_json = \
shares_catalog_endpoint(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.path, 'shares')
else:
domain_full = self.server.domain_full
http_prefix = self.server.http_prefix
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if self.server.debug:
print('Catalog for account: ' + nickname)
base_dir = self.server.base_dir
catalog_json = \
shares_catalog_account_endpoint(base_dir,
http_prefix,
nickname,
self.server.domain,
domain_full,
self.path,
self.server.debug,
'shares')
msg_str = json.dumps(catalog_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
if catalog_type == 'csv':
# catalog as a CSV file for import into a spreadsheet
msg = \
shares_catalog_csv_endpoint(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.path,
'shares').encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/csv',
msglen, None, calling_domain, False)
write2(self, msg)
return
http_404(self, 118)
return
http_400(self)
return
# wanted items catalog for this instance
# this is only accessible to instance members or to
# other instances which present an authorization token
if self.path.startswith('/wantedItems') or \
(self.path.startswith('/users/') and '/wantedItems' in self.path):
catalog_authorized = authorized
if not catalog_authorized:
if self.server.debug:
print('Wanted catalog access is not authorized. ' +
'Checking Authorization header')
# Check the authorization token
if self.headers.get('Origin') and \
self.headers.get('Authorization'):
permitted_domains = \
self.server.shared_items_federated_domains
shared_item_tokens = \
self.server.shared_item_federation_tokens
if authorize_shared_items(permitted_domains,
self.server.base_dir,
self.headers['Origin'],
calling_domain,
self.headers['Authorization'],
self.server.debug,
shared_item_tokens):
catalog_authorized = True
elif self.server.debug:
print('Authorization token refused for ' +
'wanted items federation')
elif self.server.debug:
print('No Authorization header is available for ' +
'wanted items federation')
# show wanted items catalog for federation
if has_accept(self, calling_domain) and catalog_authorized:
catalog_type = 'json'
headers = self.headers
debug = self.server.debug
if self.path.endswith('.csv') or request_csv(headers):
catalog_type = 'csv'
elif (self.path.endswith('.json') or
not request_http(headers, debug)):
catalog_type = 'json'
if self.server.debug:
print('Preparing DFC wanted catalog in format ' +
catalog_type)
if catalog_type == 'json':
# catalog as a json
if not self.path.startswith('/users/'):
if self.server.debug:
print('Wanted catalog for the instance')
catalog_json = \
shares_catalog_endpoint(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.path, 'wanted')
else:
domain_full = self.server.domain_full
http_prefix = self.server.http_prefix
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if self.server.debug:
print('Wanted catalog for account: ' + nickname)
base_dir = self.server.base_dir
catalog_json = \
shares_catalog_account_endpoint(base_dir,
http_prefix,
nickname,
self.server.domain,
domain_full,
self.path,
self.server.debug,
'wanted')
msg_str = json.dumps(catalog_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
if catalog_type == 'csv':
# catalog as a CSV file for import into a spreadsheet
msg = \
shares_catalog_csv_endpoint(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.path,
'wanted').encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/csv',
msglen, None, calling_domain, False)
write2(self, msg)
return
http_404(self, 119)
return
http_400(self)
return
# minimal mastodon api
if masto_api(self, self.path, calling_domain, ua_str,
authorized,
self.server.http_prefix,
self.server.base_dir,
self.authorized_nickname,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
self.server.translate,
self.server.registration,
self.server.system_language,
self.server.project_version,
self.server.custom_emoji,
self.server.show_node_info_accounts,
referer_domain,
self.server.debug,
self.server.known_crawlers,
self.server.sites_unavailable,
self.server.unit_test,
self.server.allow_local_network_access):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_masto_api[calling_domain]',
self.server.debug)
curr_session = \
establish_session("GET", curr_session,
proxy_type, self.server)
if not curr_session:
http_404(self, 120)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'session fail',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'create session',
self.server.debug)
# is this a html/ssml/icalendar request?
html_getreq = False
csv_getreq = False
ssml_getreq = False
icalendar_getreq = False
if has_accept(self, calling_domain):
if request_http(self.headers, self.server.debug):
html_getreq = True
elif request_csv(self.headers):
csv_getreq = True
elif request_ssml(self.headers):
ssml_getreq = True
elif request_icalendar(self.headers):
icalendar_getreq = True
else:
if self.headers.get('Connection'):
# https://developer.mozilla.org/en-US/
# docs/Web/HTTP/Protocol_upgrade_mechanism
if self.headers.get('Upgrade'):
print('HTTP Connection request: ' +
self.headers['Upgrade'])
else:
print('HTTP Connection request: ' +
self.headers['Connection'])
http_200(self)
else:
print('WARN: No Accept header ' + str(self.headers))
http_400(self)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'hasAccept',
self.server.debug)
# cached favicon images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/favicons/'):
if self.server.domain_full in self.path:
# favicon for this instance
get_favicon(self, calling_domain, self.server.base_dir,
self.server.debug, 'favicon.ico',
self.server.iconsCache,
self.server.domain_full)
return
show_cached_favicon(self, referer_domain, self.path,
self.server.base_dir,
getreq_start_time,
self.server.favicons_cache,
self.server.fitness,
self.server.debug)
return
# get css
# Note that this comes before the busy flag to avoid conflicts
if self.path.endswith('.css'):
if get_style_sheet(self, self.server.base_dir,
calling_domain, self.path,
getreq_start_time,
self.server.debug,
self.server.css_cache,
self.server.fitness):
return
if authorized and '/exports/' in self.path:
if 'blocks.csv' in self.path:
get_exported_blocks(self, self.path,
self.server.base_dir,
self.server.domain,
calling_domain)
else:
get_exported_theme(self, self.path,
self.server.base_dir,
self.server.domain_full)
return
# get fonts
if '/fonts/' in self.path:
get_fonts(self, calling_domain, self.path,
self.server.base_dir, self.server.debug,
getreq_start_time, self.server.fitness,
self.server.fontsCache,
self.server.domain_full)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'fonts',
self.server.debug)
if self.path in ('/sharedInbox', '/users/inbox', '/actor/inbox',
'/users/' + self.server.domain):
# if shared inbox is not enabled
if not self.server.enable_shared_inbox:
http_503(self)
return
self.path = '/inbox'
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'sharedInbox enabled',
self.server.debug)
if self.path == '/categories.xml':
get_hashtag_categories_feed2(self, calling_domain, self.path,
self.server.base_dir,
proxy_type,
getreq_start_time,
self.server.debug,
curr_session,
self.server.fitness)
return
if self.path == '/newswire.xml':
get_newswire_feed(self, calling_domain, self.path,
proxy_type,
getreq_start_time,
self.server.debug,
curr_session,
self.server.newswire,
self.server.http_prefix,
self.server.domain_full,
self.server.translate,
self.server.fitness)
return
# RSS 2.0
if self.path.startswith('/blog/') and \
self.path.endswith('/rss.xml'):
if not self.path == '/blog/rss.xml':
get_rss2feed(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
proxy_type,
getreq_start_time,
self.server.debug,
curr_session, MAX_POSTS_IN_RSS_FEED,
self.server.translate,
self.server.system_language,
self.server.fitness)
else:
get_rss2site(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.port,
proxy_type,
self.server.translate,
getreq_start_time,
self.server.debug,
curr_session, MAX_POSTS_IN_RSS_FEED,
self.server.system_language,
self.server.fitness)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'rss2 done',
self.server.debug)
# RSS 3.0
if self.path.startswith('/blog/') and \
self.path.endswith('/rss.txt'):
get_rss3feed(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
proxy_type,
getreq_start_time,
self.server.debug,
self.server.system_language,
curr_session, MAX_POSTS_IN_RSS_FEED,
self.server.fitness)
return
users_in_path = False
if '/users/' in self.path:
users_in_path = True
if authorized and not html_getreq and users_in_path:
if '/following?page=' in self.path:
get_following_json(self, self.server.base_dir,
self.path,
calling_domain, referer_domain,
self.server.http_prefix,
self.server.domain,
self.server.port,
self.server.followingItemsPerPage,
self.server.debug, 'following',
self.server.onion_domain,
self.server.i2p_domain)
return
if '/followers?page=' in self.path:
get_following_json(self, self.server.base_dir,
self.path,
calling_domain, referer_domain,
self.server.http_prefix,
self.server.domain,
self.server.port,
self.server.followingItemsPerPage,
self.server.debug, 'followers',
self.server.onion_domain,
self.server.i2p_domain)
return
if '/followrequests?page=' in self.path:
get_following_json(self, self.server.base_dir,
self.path,
calling_domain, referer_domain,
self.server.http_prefix,
self.server.domain,
self.server.port,
self.server.followingItemsPerPage,
self.server.debug,
'followrequests',
self.server.onion_domain,
self.server.i2p_domain)
return
# authorized endpoint used for TTS of posts
# arriving in your inbox
if authorized and users_in_path and \
self.path.endswith('/speaker'):
if 'application/ssml' not in self.headers['Accept']:
# json endpoint
_get_speaker(self, calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.domain)
else:
xml_str = \
get_ssml_box(self.server.base_dir,
self.path, self.server.domain,
self.server.system_language,
self.server.instanceTitle,
'inbox')
if xml_str:
msg = xml_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'application/xrd+xml', msglen,
None, calling_domain, False)
write2(self, msg)
return
# show a podcast episode
if authorized and users_in_path and html_getreq and \
'?podepisode=' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
episode_timestamp = self.path.split('?podepisode=')[1].strip()
episode_timestamp = episode_timestamp.replace('__', ' ')
episode_timestamp = episode_timestamp.replace('aa', ':')
if self.server.newswire.get(episode_timestamp):
pod_episode = self.server.newswire[episode_timestamp]
html_str = \
html_podcast_episode(self.server.translate,
self.server.base_dir,
nickname,
self.server.domain,
pod_episode,
self.server.text_mode_banner,
self.server.session,
self.server.session_onion,
self.server.session_i2p,
self.server.http_prefix,
self.server.debug)
if html_str:
msg = html_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
None, calling_domain, False)
write2(self, msg)
return
# redirect to the welcome screen
if html_getreq and authorized and users_in_path and \
'/welcome' not in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if '?' in nickname:
nickname = nickname.split('?')[0]
if nickname == self.authorized_nickname and \
self.path != '/users/' + nickname:
if not is_welcome_screen_complete(self.server.base_dir,
nickname,
self.server.domain):
redirect_headers(self, '/users/' + nickname + '/welcome',
cookie, calling_domain, 303)
return
if not html_getreq and \
users_in_path and self.path.endswith('/pinned'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
pinned_post_json = \
get_pinned_post_as_json(self.server.base_dir,
self.server.http_prefix,
nickname, self.server.domain,
self.server.domain_full,
self.server.system_language)
message_json = {}
if pinned_post_json:
post_id = remove_id_ending(pinned_post_json['id'])
message_json = \
outbox_message_create_wrap(self.server.http_prefix,
nickname,
self.server.domain,
self.server.port,
pinned_post_json)
message_json['id'] = post_id + '/activity'
message_json['object']['id'] = post_id
message_json['object']['url'] = replace_users_with_at(post_id)
message_json['object']['atomUri'] = post_id
msg_str = json.dumps(message_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
return
if not html_getreq and \
users_in_path and self.path.endswith('/collections/featured'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
# return the featured posts collection
get_featured_collection(self, calling_domain, referer_domain,
self.server.base_dir,
self.server.http_prefix,
nickname, self.server.domain,
self.server.domain_full,
self.server.system_language,
self.server.onion_domain,
self.server.i2p_domain)
return
if not html_getreq and \
users_in_path and self.path.endswith('/collections/featuredTags'):
get_featured_tags_collection(self, calling_domain, referer_domain,
self.path,
self.server.http_prefix,
self.server.domain_full,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'get_featured_tags_collection done',
self.server.debug)
# show a performance graph
if authorized and '/performance?graph=' in self.path:
graph = self.path.split('?graph=')[1]
if html_getreq and not graph.endswith('.json'):
if graph == 'post':
graph = '_POST'
elif graph == 'inbox':
graph = 'INBOX'
elif graph == 'get':
graph = '_GET'
msg = \
html_watch_points_graph(self.server.base_dir,
self.server.fitness,
graph, 16).encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'graph',
self.server.debug)
return
graph = graph.replace('.json', '')
if graph == 'post':
graph = '_POST'
elif graph == 'inbox':
graph = 'INBOX'
elif graph == 'get':
graph = '_GET'
watch_points_json = \
sorted_watch_points(self.server.fitness, graph)
msg_str = json.dumps(watch_points_json,
ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'graph json',
self.server.debug)
return
# show the main blog page
if html_getreq and \
self.path in ('/blog', '/blog/', '/blogs', '/blogs/'):
if '/rss.xml' not in self.path:
curr_session = \
establish_session("show the main blog page",
curr_session,
proxy_type, self.server)
if not curr_session:
http_404(self, 121)
return
msg = html_blog_view(authorized,
curr_session,
self.server.base_dir,
self.server.http_prefix,
self.server.translate,
self.server.domain,
self.server.port,
MAX_POSTS_IN_BLOGS_FEED,
self.server.peertube_instances,
self.server.system_language,
self.server.person_cache,
self.server.debug)
if msg is not None:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'blog view',
self.server.debug)
return
http_404(self, 122)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'blog view done',
self.server.debug)
# show a particular page of blog entries
# for a particular account
if html_getreq and self.path.startswith('/blog/'):
if '/rss.xml' not in self.path:
if show_blog_page(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.translate,
self.server.debug,
curr_session, MAX_POSTS_IN_BLOGS_FEED,
self.server.peertube_instances,
self.server.system_language,
self.server.person_cache,
self.server.fitness):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show_blog_page',
self.server.debug)
if html_getreq and users_in_path:
# show the person options screen with view/follow/block/report
if '?options=' in self.path:
show_person_options(self, calling_domain, self.path,
self.server.base_dir,
self.server.domain,
self.server.domain_full,
getreq_start_time,
cookie, self.server.debug,
authorized,
curr_session)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'person options done',
self.server.debug)
# show blog post
blog_filename, nickname = \
path_contains_blog_link(self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.path)
if blog_filename and nickname:
post_json_object = load_json(blog_filename)
if is_blog_post(post_json_object):
msg = html_blog_post(curr_session,
authorized,
self.server.base_dir,
self.server.http_prefix,
self.server.translate,
nickname, self.server.domain,
self.server.domain_full,
post_json_object,
self.server.peertube_instances,
self.server.system_language,
self.server.person_cache,
self.server.debug,
self.server.content_license_url)
if msg is not None:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time,
self.server.fitness,
'_GET', 'blog post 2',
self.server.debug)
return
http_404(self, 123)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'blog post 2 done',
self.server.debug)
# after selecting a shared item from the left column then show it
if html_getreq and \
'?showshare=' in self.path and '/users/' in self.path:
item_id = self.path.split('?showshare=')[1]
if '?' in item_id:
item_id = item_id.split('?')[0]
category = ''
if '?category=' in self.path:
category = self.path.split('?category=')[1]
if '?' in category:
category = category.split('?')[0]
users_path = self.path.split('?showshare=')[0]
nickname = users_path.replace('/users/', '')
item_id = urllib.parse.unquote_plus(item_id.strip())
msg = \
html_show_share(self.server.base_dir,
self.server.domain, nickname,
self.server.http_prefix,
self.server.domain_full,
item_id, self.server.translate,
self.server.shared_items_federated_domains,
self.server.default_timeline,
self.server.theme_name, 'shares', category,
False)
if not msg:
if calling_domain.endswith('.onion') and \
self.server.onion_domain:
actor = 'http://' + self.server.onion_domain + users_path
elif (calling_domain.endswith('.i2p') and
self.server.i2p_domain):
actor = 'http://' + self.server.i2p_domain + users_path
redirect_headers(self, actor + '/tlshares',
cookie, calling_domain, 303)
return
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'html_show_share',
self.server.debug)
return
# after selecting a wanted item from the left column then show it
if html_getreq and \
'?showwanted=' in self.path and '/users/' in self.path:
item_id = self.path.split('?showwanted=')[1]
if ';' in item_id:
item_id = item_id.split(';')[0]
category = self.path.split('?category=')[1]
if ';' in category:
category = category.split(';')[0]
users_path = self.path.split('?showwanted=')[0]
nickname = users_path.replace('/users/', '')
item_id = urllib.parse.unquote_plus(item_id.strip())
msg = \
html_show_share(self.server.base_dir,
self.server.domain, nickname,
self.server.http_prefix,
self.server.domain_full,
item_id, self.server.translate,
self.server.shared_items_federated_domains,
self.server.default_timeline,
self.server.theme_name, 'wanted', category,
False)
if not msg:
if calling_domain.endswith('.onion') and \
self.server.onion_domain:
actor = 'http://' + self.server.onion_domain + users_path
elif (calling_domain.endswith('.i2p') and
self.server.i2p_domain):
actor = 'http://' + self.server.i2p_domain + users_path
redirect_headers(self, actor + '/tlwanted',
cookie, calling_domain, 303)
return
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'htmlShowWanted',
self.server.debug)
return
# remove a shared item
if html_getreq and '?rmshare=' in self.path:
item_id = self.path.split('?rmshare=')[1]
item_id = urllib.parse.unquote_plus(item_id.strip())
users_path = self.path.split('?rmshare=')[0]
actor = \
self.server.http_prefix + '://' + \
self.server.domain_full + users_path
msg = html_confirm_remove_shared_item(self.server.translate,
self.server.base_dir,
actor, item_id,
calling_domain, 'shares')
if not msg:
if calling_domain.endswith('.onion') and \
self.server.onion_domain:
actor = 'http://' + self.server.onion_domain + users_path
elif (calling_domain.endswith('.i2p') and
self.server.i2p_domain):
actor = 'http://' + self.server.i2p_domain + users_path
redirect_headers(self, actor + '/tlshares',
cookie, calling_domain, 303)
return
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'remove shared item',
self.server.debug)
return
# remove a wanted item
if html_getreq and '?rmwanted=' in self.path:
item_id = self.path.split('?rmwanted=')[1]
item_id = urllib.parse.unquote_plus(item_id.strip())
users_path = self.path.split('?rmwanted=')[0]
actor = \
self.server.http_prefix + '://' + \
self.server.domain_full + users_path
msg = html_confirm_remove_shared_item(self.server.translate,
self.server.base_dir,
actor, item_id,
calling_domain, 'wanted')
if not msg:
if calling_domain.endswith('.onion') and \
self.server.onion_domain:
actor = 'http://' + self.server.onion_domain + users_path
elif (calling_domain.endswith('.i2p') and
self.server.i2p_domain):
actor = 'http://' + self.server.i2p_domain + users_path
redirect_headers(self, actor + '/tlwanted',
cookie, calling_domain, 303)
return
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'remove shared item',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'remove shared item done',
self.server.debug)
if self.path.startswith('/terms'):
if calling_domain.endswith('.onion') and \
self.server.onion_domain:
msg = html_terms_of_service(self.server.base_dir, 'http',
self.server.onion_domain)
elif (calling_domain.endswith('.i2p') and
self.server.i2p_domain):
msg = html_terms_of_service(self.server.base_dir, 'http',
self.server.i2p_domain)
else:
msg = html_terms_of_service(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'terms of service shown',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'terms of service done',
self.server.debug)
# show a list of who you are following
if (authorized and users_in_path and
(self.path.endswith('/followingaccounts') or
self.path.endswith('/followingaccounts.csv'))):
nickname = get_nickname_from_actor(self.path)
if not nickname:
http_404(self, 124)
return
following_filename = \
acct_dir(self.server.base_dir,
nickname, self.server.domain) + '/following.txt'
if not os.path.isfile(following_filename):
http_404(self, 125)
return
if self.path.endswith('/followingaccounts.csv'):
html_getreq = False
csv_getreq = True
if html_getreq:
msg = html_following_list(self.server.base_dir,
following_filename)
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg.encode('utf-8'))
elif csv_getreq:
msg = csv_following_list(following_filename,
self.server.base_dir,
nickname,
self.server.domain)
msglen = len(msg)
login_headers(self, 'text/csv', msglen, calling_domain)
write2(self, msg.encode('utf-8'))
else:
http_404(self, 126)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'following accounts shown',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'following accounts done',
self.server.debug)
# show a list of who are your followers
if authorized and users_in_path and \
self.path.endswith('/followersaccounts'):
nickname = get_nickname_from_actor(self.path)
if not nickname:
http_404(self, 127)
return
followers_filename = \
acct_dir(self.server.base_dir,
nickname, self.server.domain) + '/followers.txt'
if not os.path.isfile(followers_filename):
http_404(self, 128)
return
if html_getreq:
msg = html_following_list(self.server.base_dir,
followers_filename)
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg.encode('utf-8'))
elif csv_getreq:
msg = csv_following_list(followers_filename,
self.server.base_dir,
nickname,
self.server.domain)
msglen = len(msg)
login_headers(self, 'text/csv', msglen, calling_domain)
write2(self, msg.encode('utf-8'))
else:
http_404(self, 129)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'followers accounts shown',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'followers accounts done',
self.server.debug)
if self.path.endswith('/about'):
if calling_domain.endswith('.onion'):
msg = \
html_about(self.server.base_dir, 'http',
self.server.onion_domain,
None, self.server.translate,
self.server.system_language)
elif calling_domain.endswith('.i2p'):
msg = \
html_about(self.server.base_dir, 'http',
self.server.i2p_domain,
None, self.server.translate,
self.server.system_language)
else:
msg = \
html_about(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.translate,
self.server.system_language)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show about screen',
self.server.debug)
return
if self.path in ('/specification', '/protocol', '/activitypub'):
if calling_domain.endswith('.onion'):
msg = \
html_specification(self.server.base_dir, 'http',
self.server.onion_domain,
None, self.server.translate,
self.server.system_language)
elif calling_domain.endswith('.i2p'):
msg = \
html_specification(self.server.base_dir, 'http',
self.server.i2p_domain,
None, self.server.translate,
self.server.system_language)
else:
msg = \
html_specification(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.translate,
self.server.system_language)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show specification screen',
self.server.debug)
return
if self.path in ('/manual', '/usermanual', '/userguide'):
if calling_domain.endswith('.onion'):
msg = \
html_manual(self.server.base_dir, 'http',
self.server.onion_domain,
None, self.server.translate,
self.server.system_language)
elif calling_domain.endswith('.i2p'):
msg = \
html_manual(self.server.base_dir, 'http',
self.server.i2p_domain,
None, self.server.translate,
self.server.system_language)
else:
msg = \
html_manual(self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.translate,
self.server.system_language)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show user manual screen',
self.server.debug)
return
if html_getreq and users_in_path and authorized and \
self.path.endswith('/accesskeys'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = \
self.server.key_shortcuts[nickname]
msg = \
html_access_keys(self.server.base_dir,
nickname, self.server.domain,
self.server.translate,
access_keys,
self.server.access_keys,
self.server.default_timeline,
self.server.theme_name)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show accesskeys screen',
self.server.debug)
return
if html_getreq and users_in_path and authorized and \
self.path.endswith('/themedesigner'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_artist(self.server.base_dir, nickname):
http_403(self)
return
msg = \
html_theme_designer(self.server.base_dir,
nickname, self.server.domain,
self.server.translate,
self.server.default_timeline,
self.server.theme_name,
self.server.access_keys)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show theme designer screen',
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show about screen done',
self.server.debug)
# the initial welcome screen after first logging in
if html_getreq and authorized and \
'/users/' in self.path and self.path.endswith('/welcome'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_welcome_screen_complete(self.server.base_dir,
nickname,
self.server.domain):
msg = \
html_welcome_screen(self.server.base_dir, nickname,
self.server.system_language,
self.server.translate,
self.server.theme_name)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show welcome screen',
self.server.debug)
return
self.path = self.path.replace('/welcome', '')
# the welcome screen which allows you to set an avatar image
if html_getreq and authorized and \
'/users/' in self.path and self.path.endswith('/welcome_profile'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_welcome_screen_complete(self.server.base_dir,
nickname,
self.server.domain):
msg = \
html_welcome_profile(self.server.base_dir, nickname,
self.server.domain,
self.server.http_prefix,
self.server.domain_full,
self.server.system_language,
self.server.translate,
self.server.theme_name)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show welcome profile screen',
self.server.debug)
return
self.path = self.path.replace('/welcome_profile', '')
# the final welcome screen
if html_getreq and authorized and \
'/users/' in self.path and self.path.endswith('/welcome_final'):
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_welcome_screen_complete(self.server.base_dir,
nickname,
self.server.domain):
msg = \
html_welcome_final(self.server.base_dir, nickname,
self.server.system_language,
self.server.translate,
self.server.theme_name)
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html', msglen, calling_domain)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show welcome final screen',
self.server.debug)
return
self.path = self.path.replace('/welcome_final', '')
# if not authorized then show the login screen
if html_getreq and self.path != '/login' and \
not is_image_file(self.path) and \
self.path != '/' and \
not self.path.startswith('/.well-known/protocol-handler') and \
self.path != '/users/news/linksmobile' and \
self.path != '/users/news/newswiremobile':
if redirect_to_login_screen(self, calling_domain, self.path,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
authorized, self.server.debug,
self.server.news_instance,
self.server.fitness):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show login screen done',
self.server.debug)
# manifest images used to create a home screen icon
# when selecting "add to home screen" in browsers
# which support progressive web apps
if self.path in ('/logo72.png', '/logo96.png', '/logo128.png',
'/logo144.png', '/logo150.png', '/logo192.png',
'/logo256.png', '/logo512.png',
'/apple-touch-icon.png'):
media_filename = \
self.server.base_dir + '/img' + self.path
if os.path.isfile(media_filename):
if etag_exists(self, media_filename):
# The file has not changed
http_304(self)
return
tries = 0
media_binary = None
while tries < 5:
try:
with open(media_filename, 'rb') as av_file:
media_binary = av_file.read()
break
except OSError as ex:
print('EX: manifest logo ' +
str(tries) + ' ' + str(ex))
time.sleep(1)
tries += 1
if media_binary:
mime_type = media_file_mime_type(media_filename)
set_headers_etag(self, media_filename, mime_type,
media_binary, cookie,
self.server.domain_full,
False, None)
write2(self, media_binary)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'manifest logo shown',
self.server.debug)
return
http_404(self, 130)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'manifest logo done',
self.server.debug)
# manifest images used to show example screenshots
# for use by app stores
if self.path in ('/screenshot1.jpg', '/screenshot2.jpg'):
screen_filename = \
self.server.base_dir + '/img' + self.path
if os.path.isfile(screen_filename):
if etag_exists(self, screen_filename):
# The file has not changed
http_304(self)
return
tries = 0
media_binary = None
while tries < 5:
try:
with open(screen_filename, 'rb') as av_file:
media_binary = av_file.read()
break
except OSError as ex:
print('EX: manifest screenshot ' +
str(tries) + ' ' + str(ex))
time.sleep(1)
tries += 1
if media_binary:
mime_type = media_file_mime_type(screen_filename)
set_headers_etag(self, screen_filename, mime_type,
media_binary, cookie,
self.server.domain_full,
False, None)
write2(self, media_binary)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show screenshot',
self.server.debug)
return
http_404(self, 131)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show screenshot done',
self.server.debug)
# image on login screen or qrcode
if (is_image_file(self.path) and
(self.path.startswith('/login.') or
self.path.startswith('/qrcode.png'))):
icon_filename = \
self.server.base_dir + '/accounts' + self.path
if os.path.isfile(icon_filename):
if etag_exists(self, icon_filename):
# The file has not changed
http_304(self)
return
tries = 0
media_binary = None
while tries < 5:
try:
with open(icon_filename, 'rb') as av_file:
media_binary = av_file.read()
break
except OSError as ex:
print('EX: login screen image ' +
str(tries) + ' ' + str(ex))
time.sleep(1)
tries += 1
if media_binary:
mime_type_str = media_file_mime_type(icon_filename)
set_headers_etag(self, icon_filename,
mime_type_str,
media_binary, cookie,
self.server.domain_full,
False, None)
write2(self, media_binary)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'login screen logo',
self.server.debug)
return
http_404(self, 132)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'login screen logo done',
self.server.debug)
# QR code for account handle
if users_in_path and \
self.path.endswith('/qrcode.png'):
if show_qrcode(self, calling_domain, self.path,
self.server.base_dir,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
self.server.port,
getreq_start_time,
self.server.fitness,
self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'account qrcode done',
self.server.debug)
# search screen banner image
if users_in_path:
if self.path.endswith('/search_banner.png'):
if search_screen_banner(self, self.path,
self.server.base_dir,
self.server.domain,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug):
return
if self.path.endswith('/left_col_image.png'):
if column_image(self, 'left', self.path,
self.server.base_dir,
self.server.domain,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug):
return
if self.path.endswith('/right_col_image.png'):
if column_image(self, 'right', self.path,
self.server.base_dir,
self.server.domain,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'search screen banner done',
self.server.debug)
if self.path.startswith('/defaultprofilebackground'):
show_default_profile_background(self, self.server.base_dir,
self.server.theme_name,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug)
return
# show a background image on the login or person options page
if '-background.' in self.path:
if show_background_image(self, self.path,
self.server.base_dir,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'background shown done',
self.server.debug)
# emoji images
if '/emoji/' in self.path:
show_emoji(self, self.path, self.server.base_dir,
getreq_start_time, self.server.domain_full,
self.server.fitness, self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show emoji done',
self.server.debug)
# show media
# Note that this comes before the busy flag to avoid conflicts
# replace mastoson-style media path
if '/system/media_attachments/files/' in self.path:
self.path = self.path.replace('/system/media_attachments/files/',
'/media/')
if '/media/' in self.path:
show_media(self, self.path, self.server.base_dir,
getreq_start_time, self.server.fitness,
self.server.debug)
return
if '/ontologies/' in self.path or \
'/data/' in self.path:
if not has_users_path(self.path):
_get_ontology(self, calling_domain,
self.path, self.server.base_dir,
getreq_start_time)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show media done',
self.server.debug)
# show shared item images
# Note that this comes before the busy flag to avoid conflicts
if '/sharefiles/' in self.path:
if show_share_image(self, self.path, self.server.base_dir,
getreq_start_time,
self.server.domain_full,
self.server.fitness,
self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'share image done',
self.server.debug)
# icon images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/icons/'):
show_icon(self, self.path, self.server.base_dir,
getreq_start_time, self.server.theme_name,
self.server.iconsCache,
self.server.domain_full,
self.server.fitness, self.server.debug)
return
# show images within https://instancedomain/activitypub
if self.path.startswith('/activitypub-tutorial-'):
if self.path.endswith('.png'):
show_specification_image(self, self.path,
self.server.base_dir,
getreq_start_time,
self.server.iconsCache,
self.server.domain_full,
self.server.fitness,
self.server.debug)
return
# show images within https://instancedomain/manual
if self.path.startswith('/manual-'):
if is_image_file(self.path):
show_manual_image(self, self.path,
self.server.base_dir,
getreq_start_time,
self.server.iconsCache,
self.server.domain_full,
self.server.fitness,
self.server.debug)
return
# help screen images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/helpimages/'):
show_help_screen_image(self, self.path,
self.server.base_dir,
getreq_start_time,
self.server.theme_name,
self.server.domain_full,
self.server.fitness,
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'help screen image done',
self.server.debug)
# cached avatar images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/avatars/'):
show_cached_avatar(self, referer_domain, self.path,
self.server.base_dir,
getreq_start_time,
self.server.fitness,
self.server.debug)
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'cached avatar done',
self.server.debug)
# show avatar or background image
# Note that this comes before the busy flag to avoid conflicts
if show_avatar_or_banner(self, referer_domain, self.path,
self.server.base_dir,
self.server.domain,
getreq_start_time,
self.server.fitness,
self.server.debug):
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'avatar or banner shown done',
self.server.debug)
# This busy state helps to avoid flooding
# Resources which are expected to be called from a web page
# should be above this
curr_time_getreq = int(time.time() * 1000)
if self.server.getreq_busy:
if curr_time_getreq - self.server.last_getreq < 500:
if self.server.debug:
print('DEBUG: GET Busy')
self.send_response(429)
self.end_headers()
return
self.server.getreq_busy = True
self.server.last_getreq = curr_time_getreq
# returns after this point should set getreq_busy to False
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'GET busy time',
self.server.debug)
if not permitted_dir(self.path):
if self.server.debug:
print('DEBUG: GET Not permitted')
http_404(self, 133)
self.server.getreq_busy = False
return
# get webfinger endpoint for a person
if get_webfinger(self, calling_domain, referer_domain, cookie,
self.path, self.server.debug,
self.server.onion_domain,
self.server.i2p_domain,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.base_dir,
self.server.port):
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'webfinger called',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'permitted directory',
self.server.debug)
# show the login screen
if show_login_screen(self.path, authorized,
self.server.news_instance,
self.server.translate,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.system_language,
ua_str, self.server.theme_name,
calling_domain,
getreq_start_time,
self.server.fitness, self.server.debug,
self):
self.server.getreq_busy = False
return
# show the news front page
if self.path == '/' and \
not authorized and \
self.server.news_instance:
news_url = get_instance_url(calling_domain,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain) + \
'/users/news'
logout_redirect(self, news_url, calling_domain)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'news front page shown',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'login shown done',
self.server.debug)
# the newswire screen on mobile
if html_getreq and self.path.startswith('/users/') and \
self.path.endswith('/newswiremobile'):
if (authorized or
(not authorized and
self.path.startswith('/users/news/') and
self.server.news_instance)):
nickname = get_nickname_from_actor(self.path)
if not nickname:
http_404(self, 134)
self.server.getreq_busy = False
return
timeline_path = \
'/users/' + nickname + '/' + self.server.default_timeline
show_publish_as_icon = self.server.show_publish_as_icon
rss_icon_at_top = self.server.rss_icon_at_top
icons_as_buttons = self.server.icons_as_buttons
default_timeline = self.server.default_timeline
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = self.server.key_shortcuts[nickname]
msg = \
html_newswire_mobile(self.server.base_dir,
nickname,
self.server.domain,
self.server.domain_full,
self.server.translate,
self.server.newswire,
self.server.positive_voting,
timeline_path,
show_publish_as_icon,
authorized,
rss_icon_at_top,
icons_as_buttons,
default_timeline,
self.server.theme_name,
access_keys,
ua_str).encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
self.server.getreq_busy = False
return
if html_getreq and self.path.startswith('/users/') and \
self.path.endswith('/linksmobile'):
if (authorized or
(not authorized and
self.path.startswith('/users/news/') and
self.server.news_instance)):
nickname = get_nickname_from_actor(self.path)
if not nickname:
http_404(self, 135)
self.server.getreq_busy = False
return
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = self.server.key_shortcuts[nickname]
timeline_path = \
'/users/' + nickname + '/' + self.server.default_timeline
icons_as_buttons = self.server.icons_as_buttons
default_timeline = self.server.default_timeline
shared_items_domains = \
self.server.shared_items_federated_domains
msg = \
html_links_mobile(self.server.base_dir, nickname,
self.server.domain_full,
self.server.http_prefix,
self.server.translate,
timeline_path,
authorized,
self.server.rss_icon_at_top,
icons_as_buttons,
default_timeline,
self.server.theme_name,
access_keys,
shared_items_domains).encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen, cookie, calling_domain,
False)
write2(self, msg)
self.server.getreq_busy = False
return
if '?remotetag=' in self.path and \
'/users/' in self.path and authorized:
actor = self.path.split('?remotetag=')[0]
nickname = get_nickname_from_actor(actor)
hashtag_url = self.path.split('?remotetag=')[1]
if ';' in hashtag_url:
hashtag_url = hashtag_url.split(';')[0]
hashtag_url = hashtag_url.replace('--', '/')
page_number = 1
if ';page=' in self.path:
page_number_str = self.path.split(';page=')[1]
if ';' in page_number_str:
page_number_str = page_number_str.split(';')[0]
if page_number_str.isdigit():
page_number = int(page_number_str)
allow_local_network_access = self.server.allow_local_network_access
show_published_date_only = self.server.show_published_date_only
twitter_replacement_domain = self.server.twitter_replacement_domain
timezone = None
if self.server.account_timezone.get(nickname):
timezone = \
self.server.account_timezone.get(nickname)
msg = \
html_hashtag_search_remote(nickname,
self.server.domain,
self.server.port,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.base_dir,
hashtag_url,
page_number, MAX_POSTS_IN_FEED,
self.server.session,
self.server.cached_webfingers,
self.server.person_cache,
self.server.http_prefix,
self.server.project_version,
self.server.yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
self.server.peertube_instances,
allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
timezone,
self.server.bold_reading,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.debug,
self.server.buy_sites,
self.server.auto_cw_cache)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen, cookie, calling_domain,
False)
write2(self, msg)
self.server.getreq_busy = False
return
hashtag = urllib.parse.unquote(hashtag_url.split('/')[-1])
tags_filename = \
self.server.base_dir + '/tags/' + hashtag + '.txt'
if os.path.isfile(tags_filename):
# redirect to the local hashtag screen
self.server.getreq_busy = False
ht_url = \
get_instance_url(calling_domain,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain) + \
'/users/' + nickname + '/tags/' + hashtag
redirect_headers(self, ht_url, cookie, calling_domain, 303)
else:
# redirect to the upstream hashtag url
self.server.getreq_busy = False
redirect_headers(self, hashtag_url, None, calling_domain, 303)
return
# hashtag search
if self.path.startswith('/tags/') or \
(authorized and '/tags/' in self.path):
if self.path.startswith('/tags/rss2/'):
hashtag_search_rss2(self, calling_domain,
self.path, cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
self.server.system_language,
self.server.fitness,
self.server.debug)
self.server.getreq_busy = False
return
if not html_getreq:
hashtag_search_json2(self, calling_domain, referer_domain,
self.path, cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
MAX_POSTS_IN_FEED,
self.server.fitness,
self.server.debug)
self.server.getreq_busy = False
return
hashtag_search2(self, calling_domain,
self.path, cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
curr_session,
MAX_POSTS_IN_HASHTAG_FEED,
self.server.translate,
self.server.account_timezone,
self.server.bold_reading,
self.server.fitness,
self.server.debug,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.map_format,
self.server.access_keys,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache)
self.server.getreq_busy = False
return
# hashtag map kml
if self.path.startswith('/tagmaps/') or \
(authorized and '/tagmaps/' in self.path):
map_str = \
map_format_from_tagmaps_path(self.server.base_dir, self.path,
self.server.map_format,
self.server.domain)
if map_str:
msg = map_str.encode('utf-8')
msglen = len(msg)
if self.server.map_format == 'gpx':
header_type = \
'application/gpx+xml; charset=utf-8'
else:
header_type = \
'application/vnd.google-earth.kml+xml; charset=utf-8'
set_headers(self, header_type, msglen,
None, calling_domain, True)
write2(self, msg)
self.server.getreq_busy = False
return
http_404(self, 136)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'hashtag search done',
self.server.debug)
# show or hide buttons in the web interface
if html_getreq and users_in_path and \
self.path.endswith('/minimal') and \
authorized:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
not_min = not is_minimal(self.server.base_dir,
self.server.domain, nickname)
set_minimal(self.server.base_dir,
self.server.domain, nickname, not_min)
self.path = get_default_path(self.server.media_instance,
self.server.blogs_instance,
nickname)
# search for a fediverse address, shared item or emoji
# from the web interface by selecting search icon
if html_getreq and users_in_path:
if self.path.endswith('/search') or \
'/search?' in self.path:
if '?' in self.path:
self.path = self.path.split('?')[0]
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = self.server.key_shortcuts[nickname]
# show the search screen
msg = html_search(self.server.translate,
self.server.base_dir, self.path,
self.server.domain,
self.server.default_timeline,
self.server.theme_name,
self.server.text_mode_banner,
access_keys)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen, cookie,
calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'search screen shown',
self.server.debug)
self.server.getreq_busy = False
return
# show a hashtag category from the search screen
if html_getreq and '/category/' in self.path:
msg = html_search_hashtag_category(self.server.translate,
self.server.base_dir, self.path,
self.server.domain,
self.server.theme_name)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen, cookie, calling_domain,
False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'hashtag category screen shown',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'search screen shown done',
self.server.debug)
# Show the html calendar for a user
if html_getreq and users_in_path:
if '/calendar' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = self.server.key_shortcuts[nickname]
# show the calendar screen
msg = html_calendar(self.server.person_cache,
self.server.translate,
self.server.base_dir, self.path,
self.server.http_prefix,
self.server.domain_full,
self.server.text_mode_banner,
access_keys,
False, self.server.system_language,
self.server.default_timeline,
self.server.theme_name)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
if 'ical=true' in self.path:
set_headers(self, 'text/calendar',
msglen, cookie, calling_domain,
False)
else:
set_headers(self, 'text/html',
msglen, cookie, calling_domain,
False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'calendar shown',
self.server.debug)
else:
http_404(self, 137)
self.server.getreq_busy = False
return
# Show the icalendar for a user
if icalendar_getreq and users_in_path:
if '/calendar' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
access_keys = self.server.access_keys
if self.server.key_shortcuts.get(nickname):
access_keys = self.server.key_shortcuts[nickname]
# show the calendar screen
msg = html_calendar(self.server.person_cache,
self.server.translate,
self.server.base_dir, self.path,
self.server.http_prefix,
self.server.domain_full,
self.server.text_mode_banner,
access_keys,
True,
self.server.system_language,
self.server.default_timeline,
self.server.theme_name)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/calendar',
msglen, cookie, calling_domain,
False)
write2(self, msg)
else:
http_404(self, 138)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'icalendar shown',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'calendar shown done',
self.server.debug)
# Show confirmation for deleting a calendar event
if html_getreq and users_in_path:
if '/eventdelete' in self.path and \
'?time=' in self.path and \
'?eventid=' in self.path:
if _confirm_delete_event(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
cookie,
self.server.translate,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'calendar delete shown done',
self.server.debug)
# search for emoji by name
if html_getreq and users_in_path:
if self.path.endswith('/searchemoji'):
# show the search screen
msg = \
html_search_emoji_text_entry(self.server.translate,
self.server.base_dir,
self.path).encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'emoji search shown',
self.server.debug)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'emoji search shown done',
self.server.debug)
repeat_private = False
if html_getreq and '?repeatprivate=' in self.path:
repeat_private = True
self.path = self.path.replace('?repeatprivate=', '?repeat=')
# announce/repeat button was pressed
if authorized and html_getreq and '?repeat=' in self.path:
announce_button(self, calling_domain, self.path,
self.server.base_dir,
cookie, proxy_type,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
repeat_private,
self.server.debug,
curr_session,
self.server.sites_unavailable,
self.server.federation_list,
self.server.send_threads,
self.server.postLog,
self.server.person_cache,
self.server.cached_webfingers,
self.server.project_version,
self.server.signing_priv_key_pem,
self.server.system_language,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.allow_deletion,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.iconsCache,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show announce done',
self.server.debug)
if authorized and html_getreq and '?unrepeatprivate=' in self.path:
self.path = self.path.replace('?unrepeatprivate=', '?unrepeat=')
# undo an announce/repeat from the web interface
if authorized and html_getreq and '?unrepeat=' in self.path:
announce_button_undo(self, calling_domain, self.path,
self.server.base_dir,
cookie, proxy_type,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
self.server.debug,
self.server.recent_posts_cache,
curr_session,
self.server.iconsCache,
self.server.project_version,
self.server.fitness,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'unannounce done',
self.server.debug)
# send a newswire moderation vote from the web interface
if authorized and '/newswirevote=' in self.path and \
self.path.startswith('/users/'):
newswire_vote(self, calling_domain, self.path,
cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
self.server.newswire,
self.server.default_timeline,
self.server.fitness,
self.server.debug)
self.server.getreq_busy = False
return
# send a newswire moderation unvote from the web interface
if authorized and '/newswireunvote=' in self.path and \
self.path.startswith('/users/'):
newswire_unvote(self, calling_domain, self.path,
cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
self.server.debug,
self.server.newswire,
self.server.default_timeline,
self.server.fitness)
self.server.getreq_busy = False
return
# send a follow request approval from the web interface
if authorized and '/followapprove=' in self.path and \
self.path.startswith('/users/'):
follow_approve_button(self, calling_domain, self.path,
cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
self.server.debug,
curr_session,
self.server.federation_list,
self.server.send_threads,
self.server.postLog,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.sites_unavailable,
self.server.system_language,
self.server.fitness,
self.server.signing_priv_key_pem,
self.server.followers_sync_cache,
self.server.session_onion,
self.server.session_i2p,
self.server.session)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'follow approve done',
self.server.debug)
# deny a follow request from the web interface
if authorized and '/followdeny=' in self.path and \
self.path.startswith('/users/'):
follow_deny_button(self, calling_domain, self.path,
cookie,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
self.server.debug,
self.server.federation_list,
self.server.send_threads,
self.server.postLog,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.signing_priv_key_pem,
self.server.followers_sync_cache,
self.server.sites_unavailable,
self.server.system_language,
self.server.fitness,
self.server.session,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'follow deny done',
self.server.debug)
# like from the web interface icon
if authorized and html_getreq and '?like=' in self.path:
like_button(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie,
self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.port,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.iconsCache,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'like button done',
self.server.debug)
# undo a like from the web interface icon
if authorized and html_getreq and '?unlike=' in self.path:
like_button_undo(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.port,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.iconsCache,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'unlike button done',
self.server.debug)
# emoji reaction from the web interface icon
if authorized and html_getreq and \
'?react=' in self.path and \
'?actor=' in self.path:
reaction_button(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie,
self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.port,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'emoji reaction button done',
self.server.debug)
# undo an emoji reaction from the web interface icon
if authorized and html_getreq and \
'?unreact=' in self.path and \
'?actor=' in self.path:
reaction_button_undo(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.port,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'unreaction button done',
self.server.debug)
# bookmark from the web interface icon
if authorized and html_getreq and '?bookmark=' in self.path:
bookmark_button(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.federation_list,
self.server.iconsCache,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'bookmark shown done',
self.server.debug)
# emoji recation from the web interface bottom icon
if authorized and html_getreq and '?selreact=' in self.path:
reaction_picker2(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session,
self.server.onion_domain,
self.server.i2p_domain,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.account_timezone,
self.server.bold_reading)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'bookmark shown done',
self.server.debug)
# undo a bookmark from the web interface icon
if authorized and html_getreq and '?unbookmark=' in self.path:
bookmark_button_undo(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type, cookie,
self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.federation_list,
self.server.iconsCache,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.session_onion,
self.server.session_i2p)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'unbookmark shown done',
self.server.debug)
# delete button is pressed on a post
if authorized and html_getreq and '?delete=' in self.path:
delete_button(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type, cookie,
self.server.debug,
curr_session,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.allow_deletion,
self.server.session_onion,
self.server.session_i2p,
self.server.default_timeline)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'delete shown done',
self.server.debug)
# The mute button is pressed
if authorized and html_getreq and '?mute=' in self.path:
mute_button(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
cookie, self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.default_timeline)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'post muted done',
self.server.debug)
# unmute a post from the web interface icon
if authorized and html_getreq and '?unmute=' in self.path:
mute_button_undo(self, calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
cookie, self.server.debug,
curr_session,
self.server.signing_priv_key_pem,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.account_timezone,
self.server.bold_reading,
self.server.min_images_for_accounts,
self.server.default_timeline)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'unmute activated done',
self.server.debug)
# reply from the web interface icon
in_reply_to_url = None
reply_to_list = []
reply_page_number = 1
reply_category = ''
share_description = None
conversation_id = None
if html_getreq:
if '?conversationId=' in self.path:
conversation_id = self.path.split('?conversationId=')[1]
if '?' in conversation_id:
conversation_id = conversation_id.split('?')[0]
# public reply
if '?replyto=' in self.path:
in_reply_to_url = self.path.split('?replyto=')[1]
if '?' in in_reply_to_url:
mentions_list = in_reply_to_url.split('?')
for ment in mentions_list:
if ment.startswith('mention='):
reply_handle = ment.replace('mention=', '')
if reply_handle not in reply_to_list:
reply_to_list.append(reply_handle)
if ment.startswith('page='):
reply_page_str = ment.replace('page=', '')
if len(reply_page_str) > 5:
reply_page_str = "1"
if reply_page_str.isdigit():
reply_page_number = int(reply_page_str)
in_reply_to_url = mentions_list[0]
if not self.server.public_replies_unlisted:
self.path = self.path.split('?replyto=')[0] + '/newpost'
else:
self.path = \
self.path.split('?replyto=')[0] + '/newunlisted'
if self.server.debug:
print('DEBUG: replyto path ' + self.path)
# unlisted reply
if '?replyunlisted=' in self.path:
in_reply_to_url = self.path.split('?replyunlisted=')[1]
if '?' in in_reply_to_url:
mentions_list = in_reply_to_url.split('?')
for ment in mentions_list:
if ment.startswith('mention='):
reply_handle = ment.replace('mention=', '')
if reply_handle not in reply_to_list:
reply_to_list.append(reply_handle)
if ment.startswith('page='):
reply_page_str = ment.replace('page=', '')
if len(reply_page_str) > 5:
reply_page_str = "1"
if reply_page_str.isdigit():
reply_page_number = int(reply_page_str)
in_reply_to_url = mentions_list[0]
self.path = \
self.path.split('?replyunlisted=')[0] + '/newunlisted'
if self.server.debug:
print('DEBUG: replyunlisted path ' + self.path)
# reply to followers
if '?replyfollowers=' in self.path:
in_reply_to_url = self.path.split('?replyfollowers=')[1]
if '?' in in_reply_to_url:
mentions_list = in_reply_to_url.split('?')
for ment in mentions_list:
if ment.startswith('mention='):
reply_handle = ment.replace('mention=', '')
ment2 = ment.replace('mention=', '')
if ment2 not in reply_to_list:
reply_to_list.append(reply_handle)
if ment.startswith('page='):
reply_page_str = ment.replace('page=', '')
if len(reply_page_str) > 5:
reply_page_str = "1"
if reply_page_str.isdigit():
reply_page_number = int(reply_page_str)
in_reply_to_url = mentions_list[0]
self.path = self.path.split('?replyfollowers=')[0] + \
'/newfollowers'
if self.server.debug:
print('DEBUG: replyfollowers path ' + self.path)
# replying as a direct message,
# for moderation posts or the dm timeline
reply_is_chat = False
if '?replydm=' in self.path or '?replychat=' in self.path:
reply_type = 'replydm'
if '?replychat=' in self.path:
reply_type = 'replychat'
reply_is_chat = True
in_reply_to_url = self.path.split('?' + reply_type + '=')[1]
in_reply_to_url = urllib.parse.unquote_plus(in_reply_to_url)
if '?' in in_reply_to_url:
# multiple parameters
mentions_list = in_reply_to_url.split('?')
for ment in mentions_list:
if ment.startswith('mention='):
reply_handle = ment.replace('mention=', '')
in_reply_to_url = reply_handle
if reply_handle not in reply_to_list:
reply_to_list.append(reply_handle)
elif ment.startswith('page='):
reply_page_str = ment.replace('page=', '')
if len(reply_page_str) > 5:
reply_page_str = "1"
if reply_page_str.isdigit():
reply_page_number = int(reply_page_str)
elif ment.startswith('category='):
reply_category = ment.replace('category=', '')
elif ment.startswith('sharedesc:'):
# get the title for the shared item
share_description = \
ment.replace('sharedesc:', '').strip()
share_description = \
share_description.replace('_', ' ')
in_reply_to_url = mentions_list[0]
else:
# single parameter
if in_reply_to_url.startswith('mention='):
reply_handle = in_reply_to_url.replace('mention=', '')
in_reply_to_url = reply_handle
if reply_handle not in reply_to_list:
reply_to_list.append(reply_handle)
elif in_reply_to_url.startswith('sharedesc:'):
# get the title for the shared item
share_description = \
in_reply_to_url.replace('sharedesc:', '').strip()
share_description = \
share_description.replace('_', ' ')
self.path = \
self.path.split('?' + reply_type + '=')[0] + '/newdm'
if self.server.debug:
print('DEBUG: ' + reply_type + ' path ' + self.path)
# Edit a blog post
if authorized and \
'/users/' in self.path and \
'?editblogpost=' in self.path and \
';actor=' in self.path:
message_id = self.path.split('?editblogpost=')[1]
if ';' in message_id:
message_id = message_id.split(';')[0]
actor = self.path.split(';actor=')[1]
if ';' in actor:
actor = actor.split(';')[0]
nickname = get_nickname_from_actor(self.path.split('?')[0])
if not nickname:
http_404(self, 139)
self.server.getreq_busy = False
return
if nickname == actor:
post_url = \
local_actor_url(self.server.http_prefix, nickname,
self.server.domain_full) + \
'/statuses/' + message_id
msg = html_edit_blog(self.server.media_instance,
self.server.translate,
self.server.base_dir,
self.path, reply_page_number,
nickname, self.server.domain,
post_url,
self.server.system_language)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
self.server.getreq_busy = False
return
# Edit a post
edit_post_params = {}
if authorized and \
'/users/' in self.path and \
'?postedit=' in self.path and \
';scope=' in self.path and \
';actor=' in self.path:
post_scope = self.path.split(';scope=')[1]
if ';' in post_scope:
post_scope = post_scope.split(';')[0]
edit_post_params['scope'] = post_scope
message_id = self.path.split('?postedit=')[1]
if ';' in message_id:
message_id = message_id.split(';')[0]
if ';replyTo=' in self.path:
reply_to = self.path.split(';replyTo=')[1]
if ';' in reply_to:
reply_to = message_id.split(';')[0]
edit_post_params['replyTo'] = reply_to
actor = self.path.split(';actor=')[1]
if ';' in actor:
actor = actor.split(';')[0]
edit_post_params['actor'] = actor
nickname = get_nickname_from_actor(self.path.split('?')[0])
edit_post_params['nickname'] = nickname
if not nickname:
http_404(self, 140)
self.server.getreq_busy = False
return
if nickname != actor:
http_404(self, 141)
self.server.getreq_busy = False
return
post_url = \
local_actor_url(self.server.http_prefix, nickname,
self.server.domain_full) + \
'/statuses/' + message_id
edit_post_params['post_url'] = post_url
# use the new post functions, but using edit_post_params
new_post_scope = post_scope
if post_scope == 'public':
new_post_scope = 'post'
self.path = '/users/' + nickname + '/new' + new_post_scope
# list of known crawlers accessing nodeinfo or masto API
if _show_known_crawlers(self, calling_domain, self.path,
self.server.base_dir,
self.server.known_crawlers):
self.server.getreq_busy = False
return
# edit profile in web interface
if edit_profile2(self, calling_domain, self.path,
self.server.translate,
self.server.base_dir,
self.server.domain,
self.server.port,
cookie,
self.server.peertube_instances,
self.server.access_keys,
self.server.key_shortcuts,
self.server.default_reply_interval_hrs,
self.server.default_timeline,
self.server.theme_name,
self.server.text_mode_banner,
self.server.user_agents_blocked,
self.server.crawlers_allowed,
self.server.cw_lists,
self.server.lists_enabled,
self.server.system_language,
self.server.min_images_for_accounts,
self.server.max_recent_posts,
self.server.reverse_sequence,
self.server.buy_sites,
self.server.block_military,
self.server.block_federated_endpoints):
self.server.getreq_busy = False
return
# edit links from the left column of the timeline in web interface
if edit_links2(self, calling_domain, self.path,
self.server.translate,
self.server.base_dir,
self.server.domain,
cookie,
self.server.theme_name,
self.server.access_keys,
self.server.key_shortcuts,
self.server.default_timeline):
self.server.getreq_busy = False
return
# edit newswire from the right column of the timeline
if edit_newswire2(self, calling_domain, self.path,
self.server.translate,
self.server.base_dir,
self.server.domain, cookie,
self.server.access_keys,
self.server.key_shortcuts,
self.server.default_timeline,
self.server.theme_name,
self.server.dogwhistles):
self.server.getreq_busy = False
return
# edit news post
if edit_news_post2(self, calling_domain, self.path,
self.server.translate,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
cookie, self.server.system_language):
self.server.getreq_busy = False
return
if show_new_post(self, edit_post_params,
calling_domain, self.path,
self.server.media_instance,
self.server.translate,
self.server.base_dir,
self.server.http_prefix,
in_reply_to_url, reply_to_list,
reply_is_chat,
share_description, reply_page_number,
reply_category,
self.server.domain,
self.server.domain_full,
getreq_start_time,
cookie, no_drop_down, conversation_id,
curr_session,
self.server.default_reply_interval_hrs,
self.server.debug,
self.server.access_keys,
self.server.key_shortcuts,
self.server.system_language,
self.server.default_post_language,
self.server.bold_reading,
self.server.person_cache,
self.server.fitness,
self.server.default_timeline,
self.server.newswire,
self.server.theme_name,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.port,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'new post done',
self.server.debug)
# get an individual post from the path /@nickname/statusnumber
if show_individual_at_post(self, ssml_getreq, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.translate,
self.server.account_timezone,
self.server.fitness,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.onion_domain,
self.server.i2p_domain,
self.server.bold_reading):
self.server.getreq_busy = False
return
# show the likers of a post
if show_likers_of_post(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session,
self.server.bold_reading,
self.server.translate,
self.server.theme_name,
self.server.access_keys,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.default_timeline,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness):
self.server.getreq_busy = False
return
# show the announcers/repeaters of a post
if show_announcers_of_post(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session,
self.server.bold_reading,
self.server.translate,
self.server.theme_name,
self.server.access_keys,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.default_timeline,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'individual post done',
self.server.debug)
# get replies to a post /users/nickname/statuses/number/replies
if self.path.endswith('/replies') or '/replies?page=' in self.path:
if show_replies_to_post(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
getreq_start_time,
proxy_type, cookie,
self.server.debug,
curr_session,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.peertube_instances,
self.server.account_timezone,
self.server.bold_reading,
self.server.show_published_date_only,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'post replies done',
self.server.debug)
# roles on profile screen
if self.path.endswith('/roles') and users_in_path:
if show_roles(self, calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.cached_webfingers,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.icons_as_buttons,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.shared_items_federated_domains,
self.server.account_timezone,
self.server.bold_reading,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.person_cache,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
self.server.buy_sites,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show roles done',
self.server.debug)
# show skills on the profile page
if self.path.endswith('/skills') and users_in_path:
if show_skills(self, calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.cached_webfingers,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.icons_as_buttons,
self.server.allow_local_network_access,
self.server.access_keys,
self.server.key_shortcuts,
self.server.shared_items_federated_domains,
self.server.signing_priv_key_pem,
self.server.content_license_url,
self.server.peertube_instances,
self.server.city,
self.server.account_timezone,
self.server.bold_reading,
self.server.max_shares_on_profile,
self.server.rss_icon_at_top,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.person_cache,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.buy_sites,
self.server.sites_unavailable,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.domain_full,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show skills done',
self.server.debug)
if '?notifypost=' in self.path and users_in_path and authorized:
if show_notify_post(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.translate,
self.server.account_timezone,
self.server.fitness,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.onion_domain,
self.server.i2p_domain,
self.server.bold_reading):
self.server.getreq_busy = False
return
# get an individual post from the path
# /users/nickname/statuses/number
if '/statuses/' in self.path and users_in_path:
if show_individual_post(self, ssml_getreq, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.domain_full,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.translate,
self.server.account_timezone,
self.server.fitness,
self.server.bold_reading,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.cached_webfingers,
self.server.person_cache,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.theme_name,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show status done',
self.server.debug)
# get the inbox timeline for a given person
if self.path.endswith('/inbox') or '/inbox?page=' in self.path:
if show_inbox(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
self.server.recent_posts_cache,
curr_session,
self.server.default_timeline,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
ua_str, MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show inbox done',
self.server.debug)
# get the direct messages timeline for a given person
if self.path.endswith('/dm') or '/dm?page=' in self.path:
if show_dms(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str, MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show dms done',
self.server.debug)
# get the replies timeline for a given person
if self.path.endswith('/tlreplies') or '/tlreplies?page=' in self.path:
if show_replies(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str, MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show replies 2 done',
self.server.debug)
# get the media timeline for a given person
if self.path.endswith('/tlmedia') or '/tlmedia?page=' in self.path:
if show_media_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_MEDIA_FEED,
self.server.recent_posts_cache,
self.server.positive_voting,
self.server.voting_time_mins,
self.server.access_keys,
self.server.key_shortcuts,
self.server.shared_items_federated_domains,
self.server.allow_local_network_access,
self.server.twitter_replacement_domain,
self.server.account_timezone,
self.server.bold_reading,
self.server.reverse_sequence,
self.server.default_timeline,
self.server.max_recent_posts,
self.server.translate,
self.server.cached_webfingers,
self.server.person_cache,
self.server.allow_deletion,
self.server.project_version,
self.server.yt_replace_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.show_publish_as_icon,
self.server.icons_as_buttons,
self.server.rss_icon_at_top,
self.server.publish_button_at_top,
self.server.theme_name,
self.server.peertube_instances,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.signing_priv_key_pem,
self.server.cw_lists,
self.server.lists_enabled,
self.server.dogwhistles,
self.server.min_images_for_accounts,
self.server.buy_sites,
self.server.auto_cw_cache,
self.server.fitness):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show media 2 done',
self.server.debug)
# get the blogs for a given person
if self.path.endswith('/tlblogs') or '/tlblogs?page=' in self.path:
if show_blogs_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_BLOGS_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show blogs 2 done',
self.server.debug)
# get the news for a given person
if self.path.endswith('/tlnews') or '/tlnews?page=' in self.path:
if show_news_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_NEWS_FEED):
self.server.getreq_busy = False
return
# get features (local blogs) for a given person
if self.path.endswith('/tlfeatures') or \
'/tlfeatures?page=' in self.path:
if show_features_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_NEWS_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show news 2 done',
self.server.debug)
# get the shared items timeline for a given person
if self.path.endswith('/tlshares') or '/tlshares?page=' in self.path:
if show_shares_timeline(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
# get the wanted items timeline for a given person
if self.path.endswith('/tlwanted') or '/tlwanted?page=' in self.path:
if show_wanted_timeline(self, authorized,
calling_domain, self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show shares 2 done',
self.server.debug)
# block a domain from html_account_info
if authorized and users_in_path and \
'/accountinfo?blockdomain=' in self.path and \
'?handle=' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_moderator(self.server.base_dir, nickname):
http_400(self)
self.server.getreq_busy = False
return
block_domain = self.path.split('/accountinfo?blockdomain=')[1]
search_handle = block_domain.split('?handle=')[1]
search_handle = urllib.parse.unquote_plus(search_handle)
block_domain = block_domain.split('?handle=')[0]
block_domain = urllib.parse.unquote_plus(block_domain.strip())
if '?' in block_domain:
block_domain = block_domain.split('?')[0]
add_global_block(self.server.base_dir, '*', block_domain, None)
self.server.blocked_cache_last_updated = \
update_blocked_cache(self.server.base_dir,
self.server.blocked_cache,
self.server.blocked_cache_last_updated, 0)
msg = \
html_account_info(self.server.translate,
self.server.base_dir,
self.server.http_prefix,
nickname,
self.server.domain,
search_handle,
self.server.debug,
self.server.system_language,
self.server.signing_priv_key_pem,
None,
self.server.block_federated)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html',
msglen, calling_domain)
write2(self, msg)
self.server.getreq_busy = False
return
# unblock a domain from html_account_info
if authorized and users_in_path and \
'/accountinfo?unblockdomain=' in self.path and \
'?handle=' in self.path:
nickname = self.path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
if not is_moderator(self.server.base_dir, nickname):
http_400(self)
self.server.getreq_busy = False
return
block_domain = self.path.split('/accountinfo?unblockdomain=')[1]
search_handle = block_domain.split('?handle=')[1]
search_handle = urllib.parse.unquote_plus(search_handle)
block_domain = block_domain.split('?handle=')[0]
block_domain = urllib.parse.unquote_plus(block_domain.strip())
remove_global_block(self.server.base_dir, '*', block_domain)
self.server.blocked_cache_last_updated = \
update_blocked_cache(self.server.base_dir,
self.server.blocked_cache,
self.server.blocked_cache_last_updated, 0)
msg = \
html_account_info(self.server.translate,
self.server.base_dir,
self.server.http_prefix,
nickname,
self.server.domain,
search_handle,
self.server.debug,
self.server.system_language,
self.server.signing_priv_key_pem,
None,
self.server.block_federated)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
login_headers(self, 'text/html',
msglen, calling_domain)
write2(self, msg)
self.server.getreq_busy = False
return
# get the bookmarks timeline for a given person
if self.path.endswith('/tlbookmarks') or \
'/tlbookmarks?page=' in self.path or \
self.path.endswith('/bookmarks') or \
'/bookmarks?page=' in self.path:
if show_bookmarks_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show bookmarks 2 done',
self.server.debug)
# outbox timeline
if self.path.endswith('/outbox') or \
'/outbox?page=' in self.path:
if show_outbox_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
proxy_type, MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show outbox done',
self.server.debug)
# get the moderation feed for a moderator
if self.path.endswith('/moderation') or \
'/moderation?' in self.path:
if show_mod_timeline(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
cookie, self.server.debug,
curr_session, ua_str,
MAX_POSTS_IN_FEED):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show moderation done',
self.server.debug)
if show_shares_feed(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug, 'shares',
curr_session, SHARES_PER_PAGE,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.shared_items_federated_domains,
self.server.account_timezone,
self.server.bold_reading,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
self.server.buy_sites,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show profile 2 done',
self.server.debug)
if show_following_feed(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session, FOLLOWS_PER_PAGE,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.account_timezone,
self.server.content_license_url,
self.server.shared_items_federated_domains,
self.server.bold_reading,
self.server.hide_follows,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.buy_sites,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show profile 3 done',
self.server.debug)
if show_moved_feed(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session, FOLLOWS_PER_PAGE,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.account_timezone,
self.server.content_license_url,
self.server.shared_items_federated_domains,
self.server.bold_reading,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.buy_sites,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show moved 4 done',
self.server.debug)
if show_inactive_feed(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.dormant_months,
self.server.sites_unavailable,
FOLLOWS_PER_PAGE,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.account_timezone,
self.server.content_license_url,
self.server.shared_items_federated_domains,
self.server.bold_reading,
self.server.max_shares_on_profile,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.buy_sites,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show inactive 5 done',
self.server.debug)
if show_followers_feed(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.port,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session, FOLLOWS_PER_PAGE,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.account_timezone,
self.server.content_license_url,
self.server.shared_items_federated_domains,
self.server.bold_reading,
self.server.hide_follows,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.cw_lists,
self.server.lists_enabled,
self.server.buy_sites,
self.server.no_of_books,
self.server.auto_cw_cache,
self.server.fitness,
self.server.onion_domain,
self.server.i2p_domain):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show profile 5 done',
self.server.debug)
# look up a person
if show_person_profile(self, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain,
getreq_start_time,
proxy_type,
cookie, self.server.debug,
curr_session,
self.server.access_keys,
self.server.key_shortcuts,
self.server.city,
self.server.account_timezone,
self.server.bold_reading,
self.server.max_shares_on_profile,
self.server.sites_unavailable,
self.server.fitness,
self.server.signing_priv_key_pem,
self.server.rss_icon_at_top,
self.server.icons_as_buttons,
self.server.default_timeline,
self.server.recent_posts_cache,
self.server.max_recent_posts,
self.server.translate,
self.server.project_version,
self.server.cached_webfingers,
self.server.person_cache,
self.server.yt_replace_domain,
self.server.twitter_replacement_domain,
self.server.show_published_date_only,
self.server.newswire,
self.server.theme_name,
self.server.dormant_months,
self.server.peertube_instances,
self.server.allow_local_network_access,
self.server.text_mode_banner,
self.server.system_language,
self.server.max_like_count,
self.server.shared_items_federated_domains,
self.server.cw_lists,
self.server.lists_enabled,
self.server.content_license_url,
self.server.buy_sites,
self.server.no_of_books,
self.server.auto_cw_cache):
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show profile posts done',
self.server.debug)
# check that a json file was requested
if not self.path.endswith('.json'):
if self.server.debug:
print('DEBUG: GET Not json: ' + self.path +
' ' + self.server.base_dir)
http_404(self, 142)
self.server.getreq_busy = False
return
if not secure_mode(curr_session,
proxy_type, False,
self.server, self.headers,
self.path):
if self.server.debug:
print('WARN: Unauthorized GET')
http_404(self, 143)
self.server.getreq_busy = False
return
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'authorized fetch',
self.server.debug)
# check that the file exists
filename = self.server.base_dir + self.path
if os.path.isfile(filename):
content = None
try:
with open(filename, 'r', encoding='utf-8') as rfile:
content = rfile.read()
except OSError:
print('EX: unable to read file ' + filename)
if content:
try:
content_json = json.loads(content)
except json.decoder.JSONDecodeError as ex:
http_400(self)
print('EX: json decode error ' + str(ex) +
' from GET content_json ' +
str(content))
self.server.getreq_busy = False
return
msg_str = json.dumps(content_json, ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
accept_str = self.headers['Accept']
protocol_str = \
get_json_content_from_accept(accept_str)
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'arbitrary json',
self.server.debug)
else:
if self.server.debug:
print('DEBUG: GET Unknown file')
http_404(self, 144)
self.server.getreq_busy = False
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'end benchmarks',
self.server.debug)
def _permitted_crawler_path(path: str) -> bool:
"""Is the given path permitted to be crawled by a search engine?
this should only allow through basic information, such as nodeinfo
"""
if path == '/' or path == '/about' or path == '/login' or \
path.startswith('/blog/'):
return True
return False
def _get_referer_domain(self, ua_str: str) -> str:
"""Returns the referer domain
Which domain is the GET request coming from?
"""
referer_domain = None
if self.headers.get('referer'):
referer_domain = \
user_agent_domain(self.headers['referer'], self.server.debug)
elif self.headers.get('Referer'):
referer_domain = \
user_agent_domain(self.headers['Referer'], self.server.debug)
elif self.headers.get('Signature'):
if 'keyId="' in self.headers['Signature']:
referer_domain = self.headers['Signature'].split('keyId="')[1]
if '/' in referer_domain:
referer_domain = referer_domain.split('/')[0]
elif '#' in referer_domain:
referer_domain = referer_domain.split('#')[0]
elif '"' in referer_domain:
referer_domain = referer_domain.split('"')[0]
elif ua_str:
referer_domain = user_agent_domain(ua_str, self.server.debug)
return referer_domain
def _security_txt(self, ua_str: str, calling_domain: str,
referer_domain: str,
http_prefix: str, calling_site_timeout: int,
debug: bool) -> bool:
"""See https://www.rfc-editor.org/rfc/rfc9116
"""
if not self.path.startswith('/security.txt'):
return False
if referer_domain == self.server.domain_full:
print('security.txt request from self')
http_400(self)
return True
if self.server.security_txt_is_active:
if not referer_domain:
print('security.txt is busy ' +
'during request without referer domain')
else:
print('security.txt is busy during request from ' +
referer_domain)
http_503(self)
return True
self.server.security_txt_is_active = True
# is this a real website making the call ?
if not debug and not self.server.unit_test and referer_domain:
# Does calling_domain look like a domain?
if ' ' in referer_domain or \
';' in referer_domain or \
'.' not in referer_domain:
print('security.txt ' +
'referer domain does not look like a domain ' +
referer_domain)
http_400(self)
self.server.security_txt_is_active = False
return True
if not self.server.allow_local_network_access:
if local_network_host(referer_domain):
print('security.txt referer domain is from the ' +
'local network ' + referer_domain)
http_400(self)
self.server.security_txt_is_active = False
return True
if not referer_is_active(http_prefix,
referer_domain, ua_str,
calling_site_timeout,
self.server.sites_unavailable):
print('security.txt referer url is not active ' +
referer_domain)
http_400(self)
self.server.security_txt_is_active = False
return True
if debug:
print('DEBUG: security.txt ' + self.path)
# If we are in broch mode then don't reply
if not broch_mode_is_active(self.server.base_dir):
security_txt = \
'Contact: https://gitlab.com/bashrc2/epicyon/-/issues'
msg = security_txt.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/plain; charset=utf-8',
msglen, None, calling_domain, True)
write2(self, msg)
if referer_domain:
print('security.txt sent to ' + referer_domain)
else:
print('security.txt sent to unknown referer')
self.server.security_txt_is_active = False
return True
def _browser_config(self, calling_domain: str, referer_domain: str,
getreq_start_time) -> None:
"""Used by MS Windows to put an icon on the desktop if you
link to a website
"""
xml_str = \
'<?xml version="1.0" encoding="utf-8"?>\n' + \
'<browserconfig>\n' + \
' <msapplication>\n' + \
' <tile>\n' + \
' <square150x150logo src="/logo150.png"/>\n' + \
' <TileColor>#eeeeee</TileColor>\n' + \
' </tile>\n' + \
' </msapplication>\n' + \
'</browserconfig>'
msg_str = json.dumps(xml_str, ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
self.server.domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
set_headers(self, 'application/xrd+xml', msglen,
None, calling_domain, False)
write2(self, msg)
if self.server.debug:
print('Sent browserconfig: ' + calling_domain)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_browser_config',
self.server.debug)
def _get_speaker(self, calling_domain: str, referer_domain: str,
path: str, base_dir: str, domain: str) -> None:
"""Returns the speaker file used for TTS and
accessed via c2s
"""
nickname = path.split('/users/')[1]
if '/' in nickname:
nickname = nickname.split('/')[0]
speaker_filename = \
acct_dir(base_dir, nickname, domain) + '/speaker.json'
if not os.path.isfile(speaker_filename):
http_404(self, 18)
return
speaker_json = load_json(speaker_filename)
msg_str = json.dumps(speaker_json, ensure_ascii=False)
msg_str = convert_domains(calling_domain,
referer_domain,
msg_str,
self.server.http_prefix,
domain,
self.server.onion_domain,
self.server.i2p_domain)
msg = msg_str.encode('utf-8')
msglen = len(msg)
protocol_str = \
get_json_content_from_accept(self.headers['Accept'])
set_headers(self, protocol_str, msglen,
None, calling_domain, False)
write2(self, msg)
def _get_ontology(self, calling_domain: str,
path: str, base_dir: str,
getreq_start_time) -> None:
"""Returns an ontology file
"""
if '.owl' in path or '.rdf' in path or '.json' in path:
if '/ontologies/' in path:
ontology_str = path.split('/ontologies/')[1].replace('#', '')
else:
ontology_str = path.split('/data/')[1].replace('#', '')
ontology_filename = None
ontology_file_type = 'application/rdf+xml'
if ontology_str.startswith('DFC_'):
ontology_filename = base_dir + '/ontology/DFC/' + ontology_str
else:
ontology_str = ontology_str.replace('/data/', '')
ontology_filename = base_dir + '/ontology/' + ontology_str
if ontology_str.endswith('.json'):
ontology_file_type = 'application/ld+json'
if os.path.isfile(ontology_filename):
ontology_file = None
try:
with open(ontology_filename, 'r',
encoding='utf-8') as fp_ont:
ontology_file = fp_ont.read()
except OSError:
print('EX: unable to read ontology ' + ontology_filename)
if ontology_file:
ontology_file = \
ontology_file.replace('static.datafoodconsortium.org',
calling_domain)
if not calling_domain.endswith('.i2p') and \
not calling_domain.endswith('.onion'):
ontology_file = \
ontology_file.replace('http://' +
calling_domain,
'https://' +
calling_domain)
msg = ontology_file.encode('utf-8')
msglen = len(msg)
set_headers(self, ontology_file_type, msglen,
None, calling_domain, False)
write2(self, msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_get_ontology', self.server.debug)
return
http_404(self, 34)
def _confirm_delete_event(self, calling_domain: str, path: str,
base_dir: str, http_prefix: str, cookie: str,
translate: {}, domain_full: str,
onion_domain: str, i2p_domain: str,
getreq_start_time) -> bool:
"""Confirm whether to delete a calendar event
"""
post_id = path.split('?eventid=')[1]
if '?' in post_id:
post_id = post_id.split('?')[0]
post_time = path.split('?time=')[1]
if '?' in post_time:
post_time = post_time.split('?')[0]
post_year = path.split('?year=')[1]
if '?' in post_year:
post_year = post_year.split('?')[0]
post_month = path.split('?month=')[1]
if '?' in post_month:
post_month = post_month.split('?')[0]
post_day = path.split('?day=')[1]
if '?' in post_day:
post_day = post_day.split('?')[0]
# show the confirmation screen screen
msg = html_calendar_delete_confirm(translate,
base_dir, path,
http_prefix,
domain_full,
post_id, post_time,
post_year, post_month, post_day,
calling_domain)
if not msg:
actor = \
http_prefix + '://' + \
domain_full + \
path.split('/eventdelete')[0]
if calling_domain.endswith('.onion') and onion_domain:
actor = \
'http://' + onion_domain + \
path.split('/eventdelete')[0]
elif calling_domain.endswith('.i2p') and i2p_domain:
actor = \
'http://' + i2p_domain + \
path.split('/eventdelete')[0]
redirect_headers(self, actor + '/calendar',
cookie, calling_domain, 303)
fitness_performance(getreq_start_time,
self.server.fitness,
'_GET', '_confirm_delete_event',
self.server.debug)
return True
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/html', msglen,
cookie, calling_domain, False)
write2(self, msg)
return True
def _show_known_crawlers(self, calling_domain: str, path: str,
base_dir: str, known_crawlers: {}) -> bool:
"""Show a list of known web crawlers
"""
if '/users/' not in path:
return False
if not path.endswith('/crawlers'):
return False
nickname = get_nickname_from_actor(path)
if not nickname:
return False
if not is_moderator(base_dir, nickname):
return False
crawlers_list = []
curr_time = int(time.time())
recent_crawlers = 60 * 60 * 24 * 30
for ua_str, item in known_crawlers.items():
if item['lastseen'] - curr_time < recent_crawlers:
hits_str = str(item['hits']).zfill(8)
crawlers_list.append(hits_str + ' ' + ua_str)
crawlers_list.sort(reverse=True)
msg = ''
for line_str in crawlers_list:
msg += line_str + '\n'
msg = msg.encode('utf-8')
msglen = len(msg)
set_headers(self, 'text/plain; charset=utf-8', msglen,
None, calling_domain, True)
write2(self, msg)
return True