__filename__ = "daemon_post_profile.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 errno
from webfinger import webfinger_update
from socket import error as SocketError
from blocking import save_blocked_military
from httpheaders import redirect_headers
from httpheaders import clear_login_details
from utils import remove_avatar_from_cache
from utils import is_memorial_account
from utils import save_json
from utils import save_reverse_timeline
from utils import set_minimize_all_images
from utils import set_account_timezone
from utils import get_account_timezone
from utils import set_memorials
from utils import get_memorials
from utils import license_link_from_name
from utils import resembles_url
from utils import set_config_param
from utils import set_reply_interval_hours
from utils import valid_password
from utils import remove_eol
from utils import remove_html
from utils import get_url_from_post
from utils import load_json
from utils import is_artist
from utils import acct_dir
from utils import get_config_param
from utils import get_instance_url
from utils import get_nickname_from_actor
from utils import get_occupation_name
from auth import store_basic_credentials
from filters import is_filtered
from content import add_name_emojis_to_tags
from content import add_html_tags
from content import extract_text_fields_in_post
from content import extract_media_in_form_post
from content import save_media_in_form_post
from theme import enable_grayscale
from theme import disable_grayscale
from theme import get_theme
from theme import is_news_theme_name
from theme import set_news_avatar
from theme import get_text_mode_banner
from theme import set_theme
from theme import export_theme
from theme import import_theme
from city import get_spoofed_city
from media import convert_image_to_low_bandwidth
from media import process_meta_data
from webapp_welcome import welcome_screen_is_complete
from skills import no_of_actor_skills
from skills import actor_has_skill
from skills import actor_skill_value
from skills import set_actor_skill_level
from categories import set_hashtag_category
from person import deactivate_account
from person import get_actor_move_json
from person import get_actor_update_json
from person import add_actor_update_timestamp
from person import randomize_actor_images
from person import get_default_person_context
from person import update_memorial_flags
from pgp import set_pgp_pub_key
from pgp import get_pgp_pub_key
from pgp import get_email_address
from pgp import set_email_address
from pgp import set_pgp_fingerprint
from pgp import get_pgp_fingerprint
from xmpp import get_xmpp_address
from xmpp import set_xmpp_address
from matrix import get_matrix_address
from matrix import set_matrix_address
from ssb import get_ssb_address
from ssb import set_ssb_address
from utils import set_occupation_name
from blog import get_blog_address
from webapp_utils import set_blog_address
from session import site_is_verified
from languages import set_actor_languages
from languages import get_actor_languages
from posts import is_moderator
from posts import set_post_expiry_keep_dms
from posts import get_post_expiry_keep_dms
from posts import set_post_expiry_days
from posts import get_post_expiry_days
from posts import set_max_profile_posts
from posts import get_max_profile_posts
from tox import get_tox_address
from tox import set_tox_address
from briar import get_briar_address
from briar import set_briar_address
from cwtch import get_cwtch_address
from cwtch import set_cwtch_address
from enigma import get_enigma_pub_key
from enigma import set_enigma_pub_key
from donate import get_donation_url
from donate import set_donation_url
from donate import get_website
from donate import set_website
from donate import get_gemini_link
from donate import set_gemini_link
from person import get_featured_hashtags
from person import set_featured_hashtags
from blocking import save_block_federated_endpoints
from blocking import import_blocking_file
from blocking import add_account_blocks
from blocking import set_broch_mode
from shares import merge_shared_item_tokens
from roles import set_roles_from_list
from schedule import remove_scheduled_posts
from cwlists import get_cw_list_variable
from cache import store_person_in_cache
from daemon_utils import post_to_outbox


def _profile_post_mutuals_replies(account_dir: str, fields: {}) -> None:
    """ HTTP POST show replies only from mutuals checkbox
    """
    show_replies_mutuals = False
    if fields.get('repliesFromMutualsOnly'):
        if fields['repliesFromMutualsOnly'] == 'on':
            show_replies_mutuals = True
    show_replies_mutuals_file = \
        account_dir + '/.repliesFromMutualsOnly'
    if os.path.isfile(show_replies_mutuals_file):
        if not show_replies_mutuals:
            try:
                os.remove(show_replies_mutuals_file)
            except OSError:
                print('EX: unable to remove repliesFromMutualsOnly file ' +
                      show_replies_mutuals_file)
    else:
        if show_replies_mutuals:
            try:
                with open(show_replies_mutuals_file, 'w+',
                          encoding='utf-8') as fp_replies:
                    fp_replies.write('\n')
            except OSError:
                print('EX: unable to write repliesFromMutualsOnly file ' +
                      show_replies_mutuals_file)


def _profile_post_only_follower_replies(fields: {},
                                        account_dir: str) -> None:
    """ HTTP POST show replies only from followers checkbox
    """
    show_replies_followers = False
    if fields.get('repliesFromFollowersOnly'):
        if fields['repliesFromFollowersOnly'] == 'on':
            show_replies_followers = True
    show_replies_followers_file = \
        account_dir + '/.repliesFromFollowersOnly'
    if os.path.isfile(show_replies_followers_file):
        if not show_replies_followers:
            try:
                os.remove(show_replies_followers_file)
            except OSError:
                print('EX: unable to remove ' +
                      'repliesFromFollowersOnly file ' +
                      show_replies_followers_file)
    else:
        if show_replies_followers:
            try:
                with open(show_replies_followers_file, 'w+',
                          encoding='utf-8') as fp_replies:
                    fp_replies.write('\n')
            except OSError:
                print('EX: unable to write ' +
                      'repliesFromFollowersOnly file ' +
                      show_replies_followers_file)


def _profile_post_show_questions(base_dir: str,
                                 nickname: str, domain: str,
                                 fields: {},
                                 account_dir: str) -> None:
    """ HTTP POST show poll/vote/question posts checkbox
    """
    show_vote_posts = False
    if fields.get('showVotes'):
        if fields['showVotes'] == 'on':
            show_vote_posts = True
    show_vote_file = account_dir + '/.noVotes'
    if os.path.isfile(show_vote_file):
        if show_vote_posts:
            try:
                os.remove(show_vote_file)
            except OSError:
                print('EX: unable to remove noVotes file ' +
                      show_vote_file)
    else:
        if not show_vote_posts:
            try:
                with open(show_vote_file, 'w+',
                          encoding='utf-8') as fp_votes:
                    fp_votes.write('\n')
            except OSError:
                print('EX: unable to write noVotes file ' +
                      show_vote_file)


def _profile_post_reverse_timelines(base_dir: str,
                                    nickname: str, domain: str,
                                    fields: {}, self) -> None:
    """ HTTP POST reverse timelines checkbox
    """
    reverse = False
    if fields.get('reverseTimelines'):
        if fields['reverseTimelines'] == 'on':
            reverse = True
            if nickname not in self.server.reverse_sequence:
                self.server.reverse_sequence.append(nickname)
            save_reverse_timeline(base_dir,
                                  self.server.reverse_sequence)
    if not reverse:
        if nickname in self.server.reverse_sequence:
            self.server.reverse_sequence.remove(nickname)
            save_reverse_timeline(base_dir,
                                  self.server.reverse_sequence)


def _profile_post_bold_reading(base_dir: str,
                               nickname: str, domain: str,
                               fields: {}, self) -> None:
    """ HTTP POST bold reading checkbox
    """
    bold_reading_filename = \
        acct_dir(base_dir, nickname, domain) + '/.boldReading'
    bold_reading = False
    if fields.get('boldReading'):
        if fields['boldReading'] == 'on':
            bold_reading = True
            self.server.bold_reading[nickname] = True
            try:
                with open(bold_reading_filename, 'w+',
                          encoding='utf-8') as rfile:
                    rfile.write('\n')
            except OSError:
                print('EX: unable to write bold reading ' +
                      bold_reading_filename)
    if not bold_reading:
        if self.server.bold_reading.get(nickname):
            del self.server.bold_reading[nickname]
        if os.path.isfile(bold_reading_filename):
            try:
                os.remove(bold_reading_filename)
            except OSError:
                print('EX: _profile_edit unable to delete ' +
                      bold_reading_filename)


def _profile_post_hide_reaction_button(base_dir: str,
                                       nickname: str, domain: str,
                                       fields: {}) -> None:
    """ HTTP POST hide Reaction button
    """
    hide_reaction_button_file = \
        acct_dir(base_dir, nickname, domain) + '/.hideReactionButton'
    notify_reactions_filename = \
        acct_dir(base_dir, nickname, domain) + '/.notifyReactions'
    hide_reaction_button_active = False
    if fields.get('hideReactionButton'):
        if fields['hideReactionButton'] == 'on':
            hide_reaction_button_active = True
            try:
                with open(hide_reaction_button_file, 'w+',
                          encoding='utf-8') as rfile:
                    rfile.write('\n')
            except OSError:
                print('EX: unable to write hide reaction ' +
                      hide_reaction_button_file)
            # remove notify Reaction selection
            if os.path.isfile(notify_reactions_filename):
                try:
                    os.remove(notify_reactions_filename)
                except OSError:
                    print('EX: _profile_edit unable to delete ' +
                          notify_reactions_filename)
    if not hide_reaction_button_active:
        if os.path.isfile(hide_reaction_button_file):
            try:
                os.remove(hide_reaction_button_file)
            except OSError:
                print('EX: _profile_edit unable to delete ' +
                      hide_reaction_button_file)


def _profile_post_minimize_images(base_dir: str, nickname: str, domain: str,
                                  fields: {},
                                  min_images_for_accounts: []) -> None:
    """ HTTP POST Minimize all images from edit profile screen
    """
    minimize_all_images = False
    if fields.get('minimizeAllImages'):
        if fields['minimizeAllImages'] == 'on':
            minimize_all_images = True
            min_img_acct = min_images_for_accounts
            set_minimize_all_images(base_dir,
                                    nickname, domain,
                                    True, min_img_acct)
            print('min_images_for_accounts: ' +
                  str(min_img_acct))
    if not minimize_all_images:
        min_img_acct = min_images_for_accounts
        set_minimize_all_images(base_dir,
                                nickname, domain,
                                False, min_img_acct)
        print('min_images_for_accounts: ' +
              str(min_img_acct))


def _profile_post_hide_like_button(base_dir: str, nickname: str, domain: str,
                                   fields: {}) -> None:
    """ HTTP POST hide Like button
    """
    hide_like_button_file = \
        acct_dir(base_dir, nickname, domain) + '/.hideLikeButton'
    notify_likes_filename = \
        acct_dir(base_dir, nickname, domain) + '/.notifyLikes'
    hide_like_button_active = False
    if fields.get('hideLikeButton'):
        if fields['hideLikeButton'] == 'on':
            hide_like_button_active = True
            try:
                with open(hide_like_button_file, 'w+',
                          encoding='utf-8') as rfil:
                    rfil.write('\n')
            except OSError:
                print('EX: unable to write hide like ' +
                      hide_like_button_file)
            # remove notify likes selection
            if os.path.isfile(notify_likes_filename):
                try:
                    os.remove(notify_likes_filename)
                except OSError:
                    print('EX: _profile_edit unable to delete ' +
                          notify_likes_filename)
    if not hide_like_button_active:
        if os.path.isfile(hide_like_button_file):
            try:
                os.remove(hide_like_button_file)
            except OSError:
                print('EX: _profile_edit unable to delete ' +
                      hide_like_button_file)


def _profile_post_remove_retweets(base_dir: str, nickname: str, domain: str,
                                  fields: {}) -> None:
    """ HTTP POST remove Twitter retweets
    """
    remove_twitter_filename = \
        acct_dir(base_dir, nickname, domain) + '/.removeTwitter'
    remove_twitter_active = False
    if fields.get('removeTwitter'):
        if fields['removeTwitter'] == 'on':
            remove_twitter_active = True
            try:
                with open(remove_twitter_filename, 'w+',
                          encoding='utf-8') as rfile:
                    rfile.write('\n')
            except OSError:
                print('EX: unable to write remove twitter ' +
                      remove_twitter_filename)
    if not remove_twitter_active:
        if os.path.isfile(remove_twitter_filename):
            try:
                os.remove(remove_twitter_filename)
            except OSError:
                print('EX: _profile_edit unable to delete ' +
                      remove_twitter_filename)


def _profile_post_dms_from_followers(base_dir: str, nickname: str, domain: str,
                                     on_final_welcome_screen: str, fields: {},
                                     actor_changed: bool) -> bool:
    """ HTTP POST only receive DMs from accounts you follow
    """
    follow_dms_filename = \
        acct_dir(base_dir, nickname, domain) + '/.followDMs'
    if on_final_welcome_screen:
        # initial default setting created via
        # the welcome screen
        try:
            with open(follow_dms_filename, 'w+',
                      encoding='utf-8') as ffile:
                ffile.write('\n')
        except OSError:
            print('EX: unable to write follow DMs ' +
                  follow_dms_filename)
        actor_changed = True
    else:
        follow_dms_active = False
        if fields.get('followDMs'):
            if fields['followDMs'] == 'on':
                follow_dms_active = True
                try:
                    with open(follow_dms_filename, 'w+',
                              encoding='utf-8') as ffile:
                        ffile.write('\n')
                except OSError:
                    print('EX: unable to write follow DMs 2 ' +
                          follow_dms_filename)
        if not follow_dms_active:
            if os.path.isfile(follow_dms_filename):
                try:
                    os.remove(follow_dms_filename)
                except OSError:
                    print('EX: _profile_edit unable to delete ' +
                          follow_dms_filename)
    return actor_changed


def _profile_post_remove_custom_font(base_dir: str, nickname: str, domain: str,
                                     system_language: str, admin_nickname: str,
                                     dyslexic_font: bool,
                                     path: str, fields: {}, self) -> None:
    """ HTTP POST remove a custom font
    """
    if not fields.get('removeCustomFont'):
        return
    if (fields['removeCustomFont'] == 'on' and
        (is_artist(base_dir, nickname) or
         path.startswith('/users/' + admin_nickname + '/'))):
        font_ext = ('woff', 'woff2', 'otf', 'ttf')
        for ext in font_ext:
            if os.path.isfile(base_dir + '/fonts/custom.' + ext):
                try:
                    os.remove(base_dir + '/fonts/custom.' + ext)
                except OSError:
                    print('EX: _profile_edit unable to delete ' +
                          base_dir + '/fonts/custom.' + ext)
            if os.path.isfile(base_dir +
                              '/fonts/custom.' + ext + '.etag'):
                try:
                    os.remove(base_dir +
                              '/fonts/custom.' + ext + '.etag')
                except OSError:
                    print('EX: _profile_edit ' +
                          'unable to delete ' +
                          base_dir + '/fonts/custom.' +
                          ext + '.etag')
        curr_theme = get_theme(base_dir)
        if curr_theme:
            self.server.theme_name = curr_theme
            allow_local_network_access = \
                self.server.allow_local_network_access
            set_theme(base_dir, curr_theme, domain,
                      allow_local_network_access,
                      system_language,
                      dyslexic_font, False)
            self.server.text_mode_banner = \
                get_text_mode_banner(base_dir)
            self.server.iconsCache = {}
            self.server.fontsCache = {}
            self.server.show_publish_as_icon = \
                get_config_param(base_dir, 'showPublishAsIcon')
            self.server.full_width_tl_button_header = \
                get_config_param(base_dir, 'fullWidthTimelineButtonHeader')
            self.server.icons_as_buttons = \
                get_config_param(base_dir, 'iconsAsButtons')
            self.server.rss_icon_at_top = \
                get_config_param(base_dir, 'rssIconAtTop')
            self.server.publish_button_at_top = \
                get_config_param(base_dir, 'publishButtonAtTop')


def _profile_post_keep_dms(base_dir: str,
                           nickname: str, domain: str,
                           fields: {},
                           actor_changed: bool) -> bool:
    """ HTTP POST keep DMs during post expiry
    """
    expire_keep_dms = False
    if fields.get('expiryKeepDMs'):
        if fields['expiryKeepDMs'] == 'on':
            expire_keep_dms = True
    curr_keep_dms = \
        get_post_expiry_keep_dms(base_dir, nickname, domain)
    if curr_keep_dms != expire_keep_dms:
        set_post_expiry_keep_dms(base_dir, nickname, domain,
                                 expire_keep_dms)
        actor_changed = True
    return actor_changed


def _profile_post_reject_spam_actors(base_dir: str,
                                     nickname: str, domain: str,
                                     fields: {}) -> None:
    """ HTTP POST reject spam actors
    """
    reject_spam_actors = False
    if fields.get('rejectSpamActors'):
        if fields['rejectSpamActors'] == 'on':
            reject_spam_actors = True
    curr_reject_spam_actors = False
    actor_spam_filter_filename = \
        acct_dir(base_dir, nickname, domain) + '/.reject_spam_actors'
    if os.path.isfile(actor_spam_filter_filename):
        curr_reject_spam_actors = True
    if reject_spam_actors != curr_reject_spam_actors:
        if reject_spam_actors:
            try:
                with open(actor_spam_filter_filename, 'w+',
                          encoding='utf-8') as fp_spam:
                    fp_spam.write('\n')
            except OSError:
                print('EX: unable to write reject spam actors')
        else:
            try:
                os.remove(actor_spam_filter_filename)
            except OSError:
                print('EX: unable to remove reject spam actors')


def _profile_post_approve_followers(on_final_welcome_screen: bool,
                                    actor_json: {}, fields: {},
                                    actor_changed: bool) -> bool:
    """ HTTP POST approve followers
    """
    if on_final_welcome_screen:
        # Default setting created via the welcome screen
        actor_json['manuallyApprovesFollowers'] = True
        actor_changed = True
    else:
        approve_followers = False
        if fields.get('approveFollowers'):
            if fields['approveFollowers'] == 'on':
                approve_followers = True
        if approve_followers != \
           actor_json['manuallyApprovesFollowers']:
            actor_json['manuallyApprovesFollowers'] = approve_followers
            actor_changed = True
    return actor_changed


def _profile_post_shared_item_federation_domains(base_dir: str, fields: {},
                                                 self) -> None:
    """ HTTP POST shared item federation domains
    """
    # shared item federation domains
    si_domain_updated = False
    fed_domains_variable = "sharedItemsFederatedDomains"
    fed_domains_str = \
        get_config_param(base_dir, fed_domains_variable)
    if not fed_domains_str:
        fed_domains_str = ''
    shared_items_form_str = ''
    if fields.get('shareDomainList'):
        shared_it_list = fed_domains_str.split(',')
        for shared_federated_domain in shared_it_list:
            shared_items_form_str += \
                shared_federated_domain.strip() + '\n'

        share_domain_list = fields['shareDomainList']
        if share_domain_list != shared_items_form_str:
            shared_items_form_str2 = \
                share_domain_list.replace('\n', ',')
            shared_items_field = "sharedItemsFederatedDomains"
            set_config_param(base_dir,
                             shared_items_field,
                             shared_items_form_str2)
            si_domain_updated = True
    else:
        if fed_domains_str:
            shared_items_field = \
                "sharedItemsFederatedDomains"
            set_config_param(base_dir,
                             shared_items_field, '')
            si_domain_updated = True
    if si_domain_updated:
        si_domains = shared_items_form_str.split('\n')
        si_tokens = self.server.shared_item_federation_tokens
        self.server.shared_items_federated_domains = si_domains
        domain_full = self.server.domain_full
        base_dir = self.server.base_dir
        self.server.shared_item_federation_tokens = \
            merge_shared_item_tokens(base_dir,
                                     domain_full,
                                     si_domains,
                                     si_tokens)


def _profile_post_broch_mode(base_dir: str, domain_full: str,
                             fields: {}) -> None:
    """ HTTP POST broch mode
    """
    broch_mode = False
    if fields.get('brochMode'):
        if fields['brochMode'] == 'on':
            broch_mode = True
    curr_broch_mode = get_config_param(base_dir, "brochMode")
    if broch_mode != curr_broch_mode:
        set_broch_mode(base_dir, domain_full, broch_mode)
        set_config_param(base_dir, 'brochMode',
                         broch_mode)


def _profile_post_verify_all_signatures(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST verify all signatures
    """
    verify_all_signatures = False
    if fields.get('verifyallsignatures'):
        if fields['verifyallsignatures'] == 'on':
            verify_all_signatures = True
    self.server.verify_all_signatures = verify_all_signatures
    set_config_param(base_dir, "verifyAllSignatures",
                     verify_all_signatures)


def _profile_post_show_nodeinfo_version(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST show nodeinfo version
    """
    show_node_info_version = False
    if fields.get('showNodeInfoVersion'):
        if fields['showNodeInfoVersion'] == 'on':
            show_node_info_version = True
    self.server.show_node_info_version = \
        show_node_info_version
    set_config_param(base_dir,
                     "showNodeInfoVersion",
                     show_node_info_version)


def _profile_post_show_nodeinfo(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST Show number of accounts within nodeinfo
    """
    show_node_info_accounts = False
    if fields.get('showNodeInfoAccounts'):
        if fields['showNodeInfoAccounts'] == 'on':
            show_node_info_accounts = True
    self.server.show_node_info_accounts = \
        show_node_info_accounts
    set_config_param(base_dir,
                     "showNodeInfoAccounts",
                     show_node_info_accounts)


def _profile_post_bio(actor_json: {}, fields: {},
                      base_dir: str, http_prefix: str,
                      nickname: str, domain: str, domain_full: str,
                      system_language: str, translate: {},
                      actor_changed: bool,
                      redirect_path: str,
                      check_name_and_bio: bool) -> bool:
    """ HTTP POST change user bio
    """
    featured_tags = get_featured_hashtags(actor_json) + ' '
    actor_json['tag'] = []
    if fields.get('bio'):
        if fields['bio'] != actor_json['summary']:
            bio_str = remove_html(fields['bio'])
            if not is_filtered(base_dir,
                               nickname, domain, bio_str,
                               system_language):
                actor_tags = {}
                actor_json['summary'] = \
                    add_html_tags(base_dir,
                                  http_prefix,
                                  nickname,
                                  domain_full,
                                  bio_str, [], actor_tags,
                                  translate)
                if actor_tags:
                    for _, tag in actor_tags.items():
                        if tag['name'] + ' ' in featured_tags:
                            continue
                        actor_json['tag'].append(tag)
                actor_changed = True
            else:
                if check_name_and_bio:
                    redirect_path = '/welcome_profile'
    else:
        if check_name_and_bio:
            redirect_path = '/welcome_profile'
    set_featured_hashtags(actor_json, featured_tags, True)
    return actor_changed, redirect_path


def _profile_post_alsoknownas(actor_json: {}, fields: {},
                              actor_changed: bool) -> bool:
    """ HTTP POST Other accounts (alsoKnownAs)
    """
    also_known_as = []
    if actor_json.get('alsoKnownAs'):
        also_known_as = actor_json['alsoKnownAs']
    if fields.get('alsoKnownAs'):
        also_known_as_str = ''
        also_known_as_ctr = 0
        for alt_actor in also_known_as:
            if also_known_as_ctr > 0:
                also_known_as_str += ', '
            also_known_as_str += alt_actor
            also_known_as_ctr += 1
        if fields['alsoKnownAs'] != also_known_as_str and \
           '://' in fields['alsoKnownAs'] and \
           '@' not in fields['alsoKnownAs'] and \
           '.' in fields['alsoKnownAs']:
            if ';' in fields['alsoKnownAs']:
                fields['alsoKnownAs'] = \
                    fields['alsoKnownAs'].replace(';', ',')
            new_also_known_as = \
                fields['alsoKnownAs'].split(',')
            also_known_as = []
            for alt_actor in new_also_known_as:
                alt_actor = alt_actor.strip()
                if resembles_url(alt_actor):
                    if alt_actor not in also_known_as:
                        also_known_as.append(alt_actor)
            actor_json['alsoKnownAs'] = also_known_as
            actor_changed = True
    else:
        if also_known_as:
            del actor_json['alsoKnownAs']
            actor_changed = True
    return actor_changed


def _profile_post_featured_hashtags(actor_json: {}, fields: {},
                                    actor_changed: bool) -> bool:
    """ HTTP POST featured hashtags on edit profile screen
    """
    featured_hashtags = get_featured_hashtags(actor_json)
    if fields.get('featuredHashtags'):
        fields['featuredHashtags'] = \
            remove_html(fields['featuredHashtags'])
        if featured_hashtags != \
           fields['featuredHashtags']:
            set_featured_hashtags(actor_json,
                                  fields['featuredHashtags'])
            actor_changed = True
    else:
        if featured_hashtags:
            set_featured_hashtags(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_occupation(actor_json: {}, fields: {},
                             actor_changed: bool) -> bool:
    """ HTTP POST occupation on edit profile screen
    """
    occupation_name = get_occupation_name(actor_json)
    if fields.get('occupationName'):
        fields['occupationName'] = \
            remove_html(fields['occupationName'])
        if occupation_name != \
           fields['occupationName']:
            set_occupation_name(actor_json,
                                fields['occupationName'])
            actor_changed = True
    else:
        if occupation_name:
            set_occupation_name(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_moved(actor_json: {}, fields: {},
                        actor_changed: bool,
                        send_move_activity: bool) -> bool:
    """ HTTP POST account moved to new address
    """
    moved_to = ''
    if actor_json.get('movedTo'):
        moved_to = actor_json['movedTo']
    if fields.get('movedTo'):
        if fields['movedTo'] != moved_to and \
           resembles_url(fields['movedTo']):
            actor_json['movedTo'] = fields['movedTo']
            send_move_activity = True
            actor_changed = True
    else:
        if moved_to:
            del actor_json['movedTo']
            actor_changed = True
    return actor_changed, send_move_activity


def _profile_post_gemini_link(actor_json: {}, fields: {},
                              actor_changed: bool) -> bool:
    """ HTTP POST change gemini link
    """
    current_gemini_link = get_gemini_link(actor_json)
    if fields.get('geminiLink'):
        if fields['geminiLink'] != current_gemini_link:
            set_gemini_link(actor_json,
                            fields['geminiLink'])
            actor_changed = True
    else:
        if current_gemini_link:
            set_gemini_link(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_website(curr_session, base_dir: str, http_prefix: str,
                          nickname: str, domain: str,
                          actor_json: {}, fields: {},
                          actor_changed: bool,
                          translate: {}, debug: bool) -> bool:
    """ HTTP POST change website
    """
    current_website = get_website(actor_json, translate)
    if fields.get('websiteUrl'):
        if fields['websiteUrl'] != current_website:
            set_website(actor_json,
                        fields['websiteUrl'],
                        translate)
            actor_changed = True
        site_is_verified(curr_session,
                         base_dir,
                         http_prefix,
                         nickname, domain,
                         fields['websiteUrl'],
                         True, debug)
    else:
        if current_website:
            set_website(actor_json, '', translate)
            actor_changed = True
    return actor_changed


def _profile_post_donation_link(actor_json: {}, fields: {},
                                actor_changed: bool) -> bool:
    """ HTTP POST change donation link
    """
    current_donate_url = get_donation_url(actor_json)
    if fields.get('donateUrl'):
        if fields['donateUrl'] != current_donate_url:
            set_donation_url(actor_json,
                             fields['donateUrl'])
            actor_changed = True
    else:
        if current_donate_url:
            set_donation_url(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_pgp_fingerprint(actor_json: {}, fields: {},
                                  actor_changed: bool) -> bool:
    """ HTTP POST change PGP fingerprint
    """
    currentpgp_fingerprint = get_pgp_fingerprint(actor_json)
    if fields.get('openpgp'):
        if fields['openpgp'] != currentpgp_fingerprint:
            set_pgp_fingerprint(actor_json, fields['openpgp'])
            actor_changed = True
    else:
        if currentpgp_fingerprint:
            set_pgp_fingerprint(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_pgp_pubkey(actor_json: {}, fields: {},
                             actor_changed: bool) -> bool:
    """ HTTP POST change PGP public key
    """
    currentpgp_pub_key = get_pgp_pub_key(actor_json)
    if fields.get('pgp'):
        if fields['pgp'] != currentpgp_pub_key:
            set_pgp_pub_key(actor_json, fields['pgp'])
            actor_changed = True
    else:
        if currentpgp_pub_key:
            set_pgp_pub_key(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_enigma_pubkey(actor_json: {}, fields: {},
                                actor_changed: bool) -> bool:
    """ HTTP POST change Enigma public key
    """
    currentenigma_pub_key = get_enigma_pub_key(actor_json)
    if fields.get('enigmapubkey'):
        if fields['enigmapubkey'] != currentenigma_pub_key:
            set_enigma_pub_key(actor_json,
                               fields['enigmapubkey'])
            actor_changed = True
    else:
        if currentenigma_pub_key:
            set_enigma_pub_key(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_ntfy_topic(base_dir: str, nickname: str, domain: str,
                             fields: {}) -> None:
    """ HTTP POST change ntfy topic
    """
    if fields.get('ntfyTopic'):
        ntfy_topic_file = \
            base_dir + '/accounts/' + \
            nickname + '@' + domain + '/.ntfy_topic'
        try:
            with open(ntfy_topic_file, 'w+',
                      encoding='utf-8') as fp_ntfy:
                fp_ntfy.write(fields['ntfyTopic'])
        except OSError:
            print('EX: unable to save ntfy topic ' +
                  ntfy_topic_file)


def _profile_post_ntfy_url(base_dir: str, nickname: str, domain: str,
                           fields: {}) -> None:
    """ HTTP POST change ntfy url
    """
    if fields.get('ntfyUrl'):
        ntfy_url_file = \
            base_dir + '/accounts/' + \
            nickname + '@' + domain + '/.ntfy_url'
        try:
            with open(ntfy_url_file, 'w+',
                      encoding='utf-8') as fp_ntfy:
                fp_ntfy.write(fields['ntfyUrl'])
        except OSError:
            print('EX: unable to save ntfy url ' +
                  ntfy_url_file)


def _profile_post_cwtch_address(fields: {}, actor_json: {},
                                actor_changed: bool) -> bool:
    """ HTTP POST change cwtch address
    """
    current_cwtch_address = get_cwtch_address(actor_json)
    if fields.get('cwtchAddress'):
        if fields['cwtchAddress'] != current_cwtch_address:
            set_cwtch_address(actor_json,
                              fields['cwtchAddress'])
            actor_changed = True
    else:
        if current_cwtch_address:
            set_cwtch_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_briar_address(fields: {}, actor_json: {},
                                actor_changed: bool) -> bool:
    """ HTTP POST change briar address
    """
    current_briar_address = get_briar_address(actor_json)
    if fields.get('briarAddress'):
        if fields['briarAddress'] != current_briar_address:
            set_briar_address(actor_json,
                              fields['briarAddress'])
            actor_changed = True
    else:
        if current_briar_address:
            set_briar_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_tox_address(fields: {}, actor_json: {},
                              actor_changed: bool) -> bool:
    """ HTTP POST change tox address
    """
    current_tox_address = get_tox_address(actor_json)
    if fields.get('toxAddress'):
        if fields['toxAddress'] != current_tox_address:
            set_tox_address(actor_json,
                            fields['toxAddress'])
            actor_changed = True
    else:
        if current_tox_address:
            set_tox_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_birthday(fields: {}, actor_json: {},
                           actor_changed: bool) -> bool:
    """ HTTP POST birthday on edit profile screen
    """
    birth_date = ''
    if actor_json.get('vcard:bday'):
        birth_date = actor_json['vcard:bday']
    if fields.get('birthDate'):
        if fields['birthDate'] != birth_date:
            new_birth_date = fields['birthDate']
            if '-' in new_birth_date and \
               len(new_birth_date.split('-')) == 3:
                # set birth date
                actor_json['vcard:bday'] = new_birth_date
                actor_changed = True
    else:
        # set birth date
        if birth_date:
            actor_json['vcard:bday'] = ''
            actor_changed = True
    return actor_changed


def _profile_post_max_preview(base_dir: str, nickname: str, domain: str,
                              fields: {}) -> None:
    """ HTTP POST set maximum preview posts on profile screen
    """
    max_profile_posts = \
        get_max_profile_posts(base_dir, nickname, domain, 20)
    if fields.get('maxRecentProfilePosts'):
        if fields['maxRecentProfilePosts'] != \
           str(max_profile_posts):
            max_profile_posts = \
                fields['maxRecentProfilePosts']
            set_max_profile_posts(base_dir, nickname, domain,
                                  max_profile_posts)
    else:
        set_max_profile_posts(base_dir, nickname, domain, 20)


def _profile_post_expiry(base_dir: str, nickname: str, domain: str,
                         fields: {}, actor_changed: bool) -> bool:
    """ HTTP POST set post expiry period in days
    """
    post_expiry_period_days = \
        get_post_expiry_days(base_dir, nickname, domain)
    if fields.get('postExpiryPeriod'):
        if fields['postExpiryPeriod'] != \
           str(post_expiry_period_days):
            post_expiry_period_days = \
                fields['postExpiryPeriod']
            set_post_expiry_days(base_dir, nickname, domain,
                                 post_expiry_period_days)
            actor_changed = True
    else:
        if post_expiry_period_days > 0:
            set_post_expiry_days(base_dir, nickname, domain, 0)
            actor_changed = True
    return actor_changed


def _profile_post_time_zone(base_dir: str, nickname: str, domain: str,
                            fields: {}, actor_changed: bool, self) -> bool:
    """ HTTP POST change time zone
    """
    timezone = get_account_timezone(base_dir, nickname, domain)
    if fields.get('timeZone'):
        if fields['timeZone'] != timezone:
            set_account_timezone(base_dir,
                                 nickname, domain,
                                 fields['timeZone'])
            self.server.account_timezone[nickname] = \
                fields['timeZone']
            actor_changed = True
    else:
        if timezone:
            set_account_timezone(base_dir,
                                 nickname, domain, '')
            del self.server.account_timezone[nickname]
            actor_changed = True
    return actor_changed


def _profile_post_show_languages(actor_json: {}, fields: {},
                                 actor_changed: bool) -> bool:
    """ HTTP POST change Languages shown
    """
    current_show_languages = get_actor_languages(actor_json)
    if fields.get('showLanguages'):
        if fields['showLanguages'] != current_show_languages:
            set_actor_languages(actor_json,
                                fields['showLanguages'])
            actor_changed = True
    else:
        if current_show_languages:
            set_actor_languages(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_blog_address(curr_session,
                               base_dir: str, http_prefix: str,
                               nickname: str, domain: str,
                               actor_json: {}, fields: {},
                               actor_changed: bool,
                               debug: bool) -> bool:
    """ HTTP POST change blog address
    """
    current_blog_address = get_blog_address(actor_json)
    if fields.get('blogAddress'):
        if fields['blogAddress'] != current_blog_address:
            set_blog_address(actor_json,
                             fields['blogAddress'])
            actor_changed = True
        site_is_verified(curr_session,
                         base_dir, http_prefix,
                         nickname, domain,
                         fields['blogAddress'],
                         True, debug)
    else:
        if current_blog_address:
            set_blog_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_ssb_address(actor_json: {}, fields: {},
                              actor_changed: bool) -> bool:
    """ HTTP POST change SSB address
    """
    current_ssb_address = get_ssb_address(actor_json)
    if fields.get('ssbAddress'):
        if fields['ssbAddress'] != current_ssb_address:
            set_ssb_address(actor_json,
                            fields['ssbAddress'])
            actor_changed = True
    else:
        if current_ssb_address:
            set_ssb_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_matrix_address(actor_json: {}, fields: {},
                                 actor_changed: bool) -> bool:
    """ HTTP POST change matrix address
    """
    current_matrix_address = get_matrix_address(actor_json)
    if fields.get('matrixAddress'):
        if fields['matrixAddress'] != current_matrix_address:
            set_matrix_address(actor_json,
                               fields['matrixAddress'])
            actor_changed = True
    else:
        if current_matrix_address:
            set_matrix_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_xmpp_address(actor_json: {}, fields: {},
                               actor_changed: bool) -> bool:
    """ HTTP POST change xmpp address
    """
    current_xmpp_address = get_xmpp_address(actor_json)
    if fields.get('xmppAddress'):
        if fields['xmppAddress'] != current_xmpp_address:
            set_xmpp_address(actor_json,
                             fields['xmppAddress'])
            actor_changed = True
    else:
        if current_xmpp_address:
            set_xmpp_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_email_address(actor_json: {}, fields: {},
                                actor_changed: bool) -> bool:
    """ HTTP POST change email address
    """
    current_email_address = get_email_address(actor_json)
    if fields.get('email'):
        if fields['email'] != current_email_address:
            set_email_address(actor_json, fields['email'])
            actor_changed = True
    else:
        if current_email_address:
            set_email_address(actor_json, '')
            actor_changed = True
    return actor_changed


def _profile_post_memorial_accounts(base_dir: str, domain: str,
                                    person_cache: {}, fields: {}) -> None:
    """ HTTP POST change memorial accounts
    """
    curr_memorial = get_memorials(base_dir)
    if fields.get('memorialAccounts'):
        if fields['memorialAccounts'] != \
           curr_memorial:
            set_memorials(base_dir, domain,
                          fields['memorialAccounts'])
            update_memorial_flags(base_dir,
                                  person_cache)
    else:
        if curr_memorial:
            set_memorials(base_dir, domain, '')
            update_memorial_flags(base_dir, person_cache)


def _profile_post_instance_desc(base_dir: str, fields: {}) -> None:
    """ HTTP POST change instance description
    """
    curr_instance_description = \
        get_config_param(base_dir, 'instanceDescription')
    if fields.get('instanceDescription'):
        if fields['instanceDescription'] != \
           curr_instance_description:
            set_config_param(base_dir,
                             'instanceDescription',
                             fields['instanceDescription'])
    else:
        if curr_instance_description:
            set_config_param(base_dir,
                             'instanceDescription', '')


def _profile_post_instance_short_desc(base_dir: str, fields: {}) -> None:
    """ HTTP POST change instance short description
    """
    curr_instance_description_short = \
        get_config_param(base_dir,
                         'instanceDescriptionShort')
    if fields.get('instanceDescriptionShort'):
        if fields['instanceDescriptionShort'] != \
           curr_instance_description_short:
            idesc = fields['instanceDescriptionShort']
            set_config_param(base_dir,
                             'instanceDescriptionShort', idesc)
    else:
        if curr_instance_description_short:
            set_config_param(base_dir,
                             'instanceDescriptionShort', '')


def _profile_post_content_license(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST change instance content license
    """
    if fields.get('contentLicenseUrl'):
        if fields['contentLicenseUrl'] != \
           self.server.content_license_url:
            license_str = fields['contentLicenseUrl']
            if '://' not in license_str:
                license_str = \
                    license_link_from_name(license_str)
            set_config_param(base_dir,
                             'contentLicenseUrl',
                             license_str)
            self.server.content_license_url = \
                license_str
    else:
        license_str = \
            'https://creativecommons.org/' + \
            'licenses/by-nc/4.0'
        set_config_param(base_dir,
                         'contentLicenseUrl',
                         license_str)
        self.server.content_license_url = license_str


def _profile_post_libretranslate_api_key(base_dir: str, fields: {}) -> None:
    """ HTTP POST libretranslate API Key
    """
    curr_libretranslate_api_key = \
        get_config_param(base_dir,
                         'libretranslateApiKey')
    if fields.get('libretranslateApiKey'):
        if fields['libretranslateApiKey'] != \
           curr_libretranslate_api_key:
            lt_api_key = fields['libretranslateApiKey']
            set_config_param(base_dir,
                             'libretranslateApiKey',
                             lt_api_key)
    else:
        if curr_libretranslate_api_key:
            set_config_param(base_dir,
                             'libretranslateApiKey', '')


def _profile_post_registrations_remaining(base_dir: str, fields: {}) -> None:
    """ HTTP POST change registrations remaining
    """
    reg_str = "registrationsRemaining"
    remaining = get_config_param(base_dir, reg_str)
    if fields.get('regRemaining'):
        if fields['regRemaining'] != remaining:
            remaining = int(fields['regRemaining'])
            if remaining < 0:
                remaining = 0
            elif remaining > 10:
                remaining = 10
            set_config_param(base_dir, reg_str,
                             remaining)


def _profile_post_libretranslate_url(base_dir: str, fields: {}) -> None:
    """ HTTP POST libretranslate URL
    """
    curr_libretranslate_url = \
        get_config_param(base_dir,
                         'libretranslateUrl')
    if fields.get('libretranslateUrl'):
        if fields['libretranslateUrl'] != \
           curr_libretranslate_url:
            lt_url = fields['libretranslateUrl']
            if resembles_url(lt_url):
                set_config_param(base_dir,
                                 'libretranslateUrl',
                                 lt_url)
    else:
        if curr_libretranslate_url:
            set_config_param(base_dir,
                             'libretranslateUrl', '')


def _profile_post_replies_unlisted(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST change public replies unlisted
    """
    pub_replies_unlisted = False
    if self.server.public_replies_unlisted or \
       get_config_param(base_dir,
                        "publicRepliesUnlisted") is True:
        pub_replies_unlisted = True
    if fields.get('publicRepliesUnlisted'):
        if fields['publicRepliesUnlisted'] != \
           pub_replies_unlisted:
            pub_replies_unlisted = \
                fields['publicRepliesUnlisted']
            set_config_param(base_dir,
                             'publicRepliesUnlisted',
                             True)
            self.server.public_replies_unlisted = \
                pub_replies_unlisted
    else:
        if pub_replies_unlisted:
            set_config_param(base_dir,
                             'publicRepliesUnlisted',
                             False)
            self.server.public_replies_unlisted = False


def _profile_post_registrations_open(base_dir: str, fields: {}, self) -> None:
    """ HTTP POST change registrations open status
    """
    registrations_open = False
    if self.server.registration or \
       get_config_param(base_dir,
                        "registration") == 'open':
        registrations_open = True
    if fields.get('regOpen'):
        if fields['regOpen'] != registrations_open:
            registrations_open = fields['regOpen']
            set_config_param(base_dir, 'registration',
                             'open')
            remaining = \
                get_config_param(base_dir,
                                 'registrationsRemaining')
            if not remaining:
                set_config_param(base_dir,
                                 'registrationsRemaining',
                                 10)
            self.server.registration = True
    else:
        if registrations_open:
            set_config_param(base_dir, 'registration',
                             'closed')
            self.server.registration = False


def _profile_post_submit_button(base_dir: str, fields: {}) -> None:
    """ HTTP POST change custom post submit button text
    """
    curr_custom_submit_text = get_config_param(base_dir, 'customSubmitText')
    if fields.get('customSubmitText'):
        if fields['customSubmitText'] != \
           curr_custom_submit_text:
            custom_text = fields['customSubmitText']
            set_config_param(base_dir, 'customSubmitText', custom_text)
    else:
        if curr_custom_submit_text:
            set_config_param(base_dir, 'customSubmitText', '')


def _profile_post_twitter_alt_domain(base_dir: str, fields: {},
                                     self) -> None:
    """ HTTP POST change twitter alternate domain
    """
    if fields.get('twitterdomain'):
        curr_twitter_domain = \
            self.server.twitter_replacement_domain
        if fields['twitterdomain'] != curr_twitter_domain:
            new_twitter_domain = fields['twitterdomain']
            if '://' in new_twitter_domain:
                new_twitter_domain = \
                    new_twitter_domain.split('://')[1]
            if '/' in new_twitter_domain:
                new_twitter_domain = \
                    new_twitter_domain.split('/')[0]
            if '.' in new_twitter_domain:
                set_config_param(base_dir, 'twitterdomain',
                                 new_twitter_domain)
                self.server.twitter_replacement_domain = \
                    new_twitter_domain
    else:
        set_config_param(base_dir, 'twitterdomain', '')
        self.server.twitter_replacement_domain = None


def _profile_post_youtube_alt_domain(base_dir: str, fields: {},
                                     self) -> None:
    """ HTTP POST change YouTube alternate domain
    """
    if fields.get('ytdomain'):
        curr_yt_domain = self.server.yt_replace_domain
        if fields['ytdomain'] != curr_yt_domain:
            new_yt_domain = fields['ytdomain']
            if '://' in new_yt_domain:
                new_yt_domain = \
                    new_yt_domain.split('://')[1]
            if '/' in new_yt_domain:
                new_yt_domain = new_yt_domain.split('/')[0]
            if '.' in new_yt_domain:
                set_config_param(base_dir, 'youtubedomain',
                                 new_yt_domain)
                self.server.yt_replace_domain = \
                    new_yt_domain
    else:
        set_config_param(base_dir, 'youtubedomain', '')
        self.server.yt_replace_domain = None


def _profile_post_instance_title(base_dir: str, fields: {}) -> None:
    """ HTTP POST change instance title
    """
    if fields.get('instanceTitle'):
        curr_instance_title = \
            get_config_param(base_dir, 'instanceTitle')
        if fields['instanceTitle'] != curr_instance_title:
            set_config_param(base_dir, 'instanceTitle',
                             fields['instanceTitle'])


def _profile_post_blog_instance_status(base_dir: str, fields: {},
                                       self) -> None:
    """ HTTP POST blog instance status
    """
    if fields.get('blogsInstance'):
        self.server.blogs_instance = False
        self.server.default_timeline = 'inbox'
        if fields['blogsInstance'] == 'on':
            self.server.blogs_instance = True
            self.server.media_instance = False
            self.server.news_instance = False
            self.server.default_timeline = 'tlblogs'
        set_config_param(base_dir, "blogsInstance",
                         self.server.blogs_instance)
        set_config_param(base_dir, "mediaInstance",
                         self.server.media_instance)
        set_config_param(base_dir, "newsInstance",
                         self.server.news_instance)
    else:
        if self.server.blogs_instance:
            self.server.blogs_instance = False
            self.server.default_timeline = 'inbox'
            set_config_param(base_dir, "blogsInstance",
                             self.server.blogs_instance)


def _profile_post_news_instance_status(base_dir: str, fields: {},
                                       self) -> None:
    """ HTTP POST change news instance status
    """
    if fields.get('newsInstance'):
        self.server.news_instance = False
        self.server.default_timeline = 'inbox'
        if fields['newsInstance'] == 'on':
            self.server.news_instance = True
            self.server.blogs_instance = False
            self.server.media_instance = False
            self.server.default_timeline = 'tlfeatures'
        set_config_param(base_dir, "mediaInstance",
                         self.server.media_instance)
        set_config_param(base_dir, "blogsInstance",
                         self.server.blogs_instance)
        set_config_param(base_dir, "newsInstance",
                         self.server.news_instance)
    else:
        if self.server.news_instance:
            self.server.news_instance = False
            self.server.default_timeline = 'inbox'
            set_config_param(base_dir, "newsInstance",
                             self.server.media_instance)


def _profile_post_media_instance_status(base_dir: str, fields: {},
                                        self) -> None:
    """ HTTP POST change media instance status
    """
    if fields.get('mediaInstance'):
        self.server.media_instance = False
        self.server.default_timeline = 'inbox'
        if fields['mediaInstance'] == 'on':
            self.server.media_instance = True
            self.server.blogs_instance = False
            self.server.news_instance = False
            self.server.default_timeline = 'tlmedia'
        set_config_param(base_dir, "mediaInstance",
                         self.server.media_instance)
        set_config_param(base_dir, "blogsInstance",
                         self.server.blogs_instance)
        set_config_param(base_dir, "newsInstance",
                         self.server.news_instance)
    else:
        if self.server.media_instance:
            self.server.media_instance = False
            self.server.default_timeline = 'inbox'
            set_config_param(base_dir, "mediaInstance",
                             self.server.media_instance)


def _profile_post_theme_change(base_dir: str, nickname: str,
                               domain: str, domain_full: str,
                               admin_nickname: str, fields: {},
                               theme_name: str, http_prefix: str,
                               allow_local_network_access: bool,
                               system_language: str,
                               dyslexic_font: bool, self) -> None:
    """ HTTP POST change the theme from edit profile screen
    """
    if nickname == admin_nickname or \
       is_artist(base_dir, nickname):
        if fields.get('themeDropdown'):
            if theme_name != \
               fields['themeDropdown']:
                theme_name = \
                    fields['themeDropdown']
                set_theme(base_dir, theme_name,
                          domain, allow_local_network_access,
                          system_language,
                          dyslexic_font, True)
                self.server.text_mode_banner = \
                    get_text_mode_banner(base_dir)
                self.server.iconsCache = {}
                self.server.fontsCache = {}
                self.server.css_cache = {}
                self.server.show_publish_as_icon = \
                    get_config_param(base_dir,
                                     'showPublishAsIcon')
                self.server.full_width_tl_button_header = \
                    get_config_param(base_dir,
                                     'fullWidthTlButtonHeader')
                self.server.icons_as_buttons = \
                    get_config_param(base_dir,
                                     'iconsAsButtons')
                self.server.rss_icon_at_top = \
                    get_config_param(base_dir,
                                     'rssIconAtTop')
                self.server.publish_button_at_top = \
                    get_config_param(base_dir,
                                     'publishButtonAtTop')
                set_news_avatar(base_dir,
                                fields['themeDropdown'],
                                http_prefix,
                                domain, domain_full)


def _profile_post_change_displayed_name(base_dir: str,
                                        nickname: str, domain: str,
                                        system_language: str,
                                        actor_json: {},
                                        fields: {},
                                        check_name_and_bio: bool,
                                        actor_changed: bool,
                                        redirect_path: str) -> (bool, str):
    """ HTTP POST change displayed name
    """
    if fields.get('displayNickname'):
        if fields['displayNickname'] != actor_json['name']:
            display_name = \
                remove_html(fields['displayNickname'])
            if not is_filtered(base_dir,
                               nickname, domain,
                               display_name,
                               system_language):
                actor_json['name'] = display_name
            else:
                actor_json['name'] = nickname
                if check_name_and_bio:
                    redirect_path = '/welcome_profile'
            actor_changed = True
    else:
        if check_name_and_bio:
            redirect_path = '/welcome_profile'
    return actor_changed, redirect_path


def _profile_post_change_city(base_dir: str, nickname: str, domain: str,
                              fields: {}) -> None:
    """ HTTP POST change city
    """
    if fields.get('cityDropdown'):
        city_filename = \
            acct_dir(base_dir, nickname, domain) + '/city.txt'
        try:
            with open(city_filename, 'w+',
                      encoding='utf-8') as fp_city:
                fp_city.write(fields['cityDropdown'])
        except OSError:
            print('EX: edit profile unable to write city ' + city_filename)


def _profile_post_set_reply_interval(base_dir: str, nickname: str, domain: str,
                                     fields: {}) -> None:
    """ HTTP POST reply interval in hours
    """
    if fields.get('replyhours'):
        if fields['replyhours'].isdigit():
            set_reply_interval_hours(base_dir,
                                     nickname, domain,
                                     fields['replyhours'])


def _profile_post_change_password(base_dir: str, nickname: str,
                                  fields: {}) -> None:
    """ HTTP POST change password
    """
    if fields.get('password') and \
       fields.get('passwordconfirm'):
        fields['password'] = \
            remove_eol(fields['password']).strip()
        fields['passwordconfirm'] = \
            remove_eol(fields['passwordconfirm']).strip()
        if valid_password(fields['password']) and \
           fields['password'] == fields['passwordconfirm']:
            # set password
            store_basic_credentials(base_dir, nickname,
                                    fields['password'])


def _profile_post_skill_level(actor_json: {},
                              fields: {},
                              base_dir: str, nickname: str, domain: str,
                              system_language: str,
                              translate: {},
                              actor_changed: bool) -> bool:
    """ HTTP POST set skill levels
    """
    skill_ctr = 1
    actor_skills_ctr = no_of_actor_skills(actor_json)
    while skill_ctr < 10:
        skill_name = \
            fields.get('skillName' + str(skill_ctr))
        if not skill_name:
            skill_ctr += 1
            continue
        if is_filtered(base_dir, nickname, domain, skill_name,
                       system_language):
            skill_ctr += 1
            continue
        skill_value = \
            fields.get('skillValue' + str(skill_ctr))
        if not skill_value:
            skill_ctr += 1
            continue
        if not actor_has_skill(actor_json, skill_name):
            actor_changed = True
        else:
            if actor_skill_value(actor_json, skill_name) != \
               int(skill_value):
                actor_changed = True
        set_actor_skill_level(actor_json,
                              skill_name, int(skill_value))
        skills_str = translate['Skills']
        skills_str = skills_str.lower()
        set_hashtag_category(base_dir, skill_name,
                             skills_str, False)
        skill_ctr += 1
    if no_of_actor_skills(actor_json) != \
       actor_skills_ctr:
        actor_changed = True
    return actor_changed


def _profile_post_avatar_image_ext(profile_media_types_uploaded: {},
                                   actor_json: {}) -> None:
    """ HTTP POST update the avatar/image url file extension
    """
    uploads = profile_media_types_uploaded.items()
    for m_type, last_part in uploads:
        rep_str = '/' + last_part
        if m_type == 'avatar':
            url_str = \
                get_url_from_post(actor_json['icon']['url'])
            actor_url = remove_html(url_str)
            last_part_of_url = actor_url.split('/')[-1]
            srch_str = '/' + last_part_of_url
            actor_url = actor_url.replace(srch_str, rep_str)
            actor_json['icon']['url'] = actor_url
            print('actor_url: ' + actor_url)
            if '.' in actor_url:
                img_ext = actor_url.split('.')[-1]
                if img_ext == 'jpg':
                    img_ext = 'jpeg'
                actor_json['icon']['mediaType'] = \
                    'image/' + img_ext
        elif m_type == 'image':
            url_str = \
                get_url_from_post(actor_json['image']['url'])
            im_url = \
                remove_html(url_str)
            last_part_of_url = im_url.split('/')[-1]
            srch_str = '/' + last_part_of_url
            actor_json['image']['url'] = \
                im_url.replace(srch_str, rep_str)
            if '.' in im_url:
                img_ext = im_url.split('.')[-1]
                if img_ext == 'jpg':
                    img_ext = 'jpeg'
                actor_json['image']['mediaType'] = \
                    'image/' + img_ext


def profile_edit(self, calling_domain: str, cookie: str,
                 path: str, base_dir: str, http_prefix: str,
                 domain: str, domain_full: str,
                 onion_domain: str, i2p_domain: str,
                 debug: bool, allow_local_network_access: bool,
                 system_language: str,
                 content_license_url: str,
                 curr_session, proxy_type: str) -> None:
    """Updates your user profile after editing via the Edit button
    on the profile screen
    """
    users_path = path.replace('/profiledata', '')
    users_path = users_path.replace('/editprofile', '')
    actor_str = \
        get_instance_url(calling_domain,
                         self.server.http_prefix,
                         self.server.domain_full,
                         self.server.onion_domain,
                         self.server.i2p_domain) + \
        users_path

    boundary = None
    if ' boundary=' in self.headers['Content-type']:
        boundary = self.headers['Content-type'].split('boundary=')[1]
        if ';' in boundary:
            boundary = boundary.split(';')[0]

    # get the nickname
    nickname = get_nickname_from_actor(actor_str)
    if not nickname:
        print('WARN: nickname not found in ' + actor_str)
        redirect_headers(self, actor_str, cookie, calling_domain)
        self.server.postreq_busy = False
        return

    if self.headers.get('Content-length'):
        length = int(self.headers['Content-length'])

        # check that the POST isn't too large
        if length > self.server.max_post_length:
            print('Maximum profile data length exceeded ' +
                  str(length))
            redirect_headers(self, actor_str, cookie, calling_domain)
            self.server.postreq_busy = False
            return

    try:
        # read the bytes of the http form POST
        post_bytes = self.rfile.read(length)
    except SocketError as ex:
        if ex.errno == errno.ECONNRESET:
            print('EX: connection was reset while ' +
                  'reading bytes from http form POST')
        else:
            print('EX: error while reading bytes ' +
                  'from http form POST')
        self.send_response(400)
        self.end_headers()
        self.server.postreq_busy = False
        return
    except ValueError as ex:
        print('EX: failed to read bytes for POST, ' + str(ex))
        self.send_response(400)
        self.end_headers()
        self.server.postreq_busy = False
        return

    admin_nickname = get_config_param(self.server.base_dir, 'admin')

    if not boundary:
        if b'--LYNX' in post_bytes:
            boundary = '--LYNX'

    if debug:
        print('post_bytes: ' + str(post_bytes))

    if boundary:
        # get the various avatar, banner and background images
        actor_changed = True
        send_move_activity = False
        profile_media_types = (
            'avatar', 'image',
            'banner', 'search_banner',
            'instanceLogo',
            'left_col_image', 'right_col_image',
            'importFollows',
            'importTheme'
        )
        profile_media_types_uploaded = {}
        for m_type in profile_media_types:
            # some images can only be changed by the admin
            if m_type == 'instanceLogo':
                if nickname != admin_nickname:
                    print('WARN: only the admin can change ' +
                          'instance logo')
                    continue

            if debug:
                print('DEBUG: profile update extracting ' + m_type +
                      ' image, zip, csv or font from POST')
            media_bytes, post_bytes = \
                extract_media_in_form_post(post_bytes, boundary, m_type)
            if media_bytes:
                if debug:
                    print('DEBUG: profile update ' + m_type +
                          ' image, zip, csv or font was found. ' +
                          str(len(media_bytes)) + ' bytes')
            else:
                if debug:
                    print('DEBUG: profile update, no ' + m_type +
                          ' image, zip, csv or font was found in POST')
                continue

            # Note: a .temp extension is used here so that at no
            # time is an image with metadata publicly exposed,
            # even for a few mS
            if m_type == 'instanceLogo':
                filename_base = \
                    base_dir + '/accounts/login.temp'
            elif m_type == 'importTheme':
                if not os.path.isdir(base_dir + '/imports'):
                    os.mkdir(base_dir + '/imports')
                filename_base = \
                    base_dir + '/imports/newtheme.zip'
                if os.path.isfile(filename_base):
                    try:
                        os.remove(filename_base)
                    except OSError:
                        print('EX: _profile_edit unable to delete ' +
                              filename_base)
            elif m_type == 'importFollows':
                filename_base = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/import_following.csv'
            else:
                filename_base = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/' + m_type + '.temp'

            filename, _ = \
                save_media_in_form_post(media_bytes, debug,
                                        filename_base)
            if filename:
                print('Profile update POST ' + m_type +
                      ' media, zip, csv or font filename is ' + filename)
            else:
                print('Profile update, no ' + m_type +
                      ' media, zip, csv or font filename in POST')
                continue

            if m_type == 'importFollows':
                if os.path.isfile(filename_base):
                    print(nickname + ' imported follows csv')
                else:
                    print('WARN: failed to import follows from csv for ' +
                          nickname)
                continue

            if m_type == 'importTheme':
                if nickname == admin_nickname or \
                   is_artist(base_dir, nickname):
                    if import_theme(base_dir, filename):
                        print(nickname + ' uploaded a theme')
                else:
                    print('Only admin or artist can import a theme')
                continue

            post_image_filename = filename.replace('.temp', '')
            if debug:
                print('DEBUG: POST ' + m_type +
                      ' media removing metadata')
            # remove existing etag
            if os.path.isfile(post_image_filename + '.etag'):
                try:
                    os.remove(post_image_filename + '.etag')
                except OSError:
                    print('EX: _profile_edit unable to delete ' +
                          post_image_filename + '.etag')

            city = get_spoofed_city(self.server.city,
                                    base_dir, nickname, domain)

            if self.server.low_bandwidth:
                convert_image_to_low_bandwidth(filename)
            process_meta_data(base_dir, nickname, domain,
                              filename, post_image_filename, city,
                              content_license_url)
            if os.path.isfile(post_image_filename):
                print('profile update POST ' + m_type +
                      ' image, zip or font saved to ' +
                      post_image_filename)
                if m_type != 'instanceLogo':
                    last_part_of_image_filename = \
                        post_image_filename.split('/')[-1]
                    profile_media_types_uploaded[m_type] = \
                        last_part_of_image_filename
                    actor_changed = True
            else:
                print('ERROR: profile update POST ' + m_type +
                      ' image or font could not be saved to ' +
                      post_image_filename)

        post_bytes_str = post_bytes.decode('utf-8')
        redirect_path = ''
        check_name_and_bio = False
        on_final_welcome_screen = False
        if 'name="previewAvatar"' in post_bytes_str:
            redirect_path = '/welcome_profile'
        elif 'name="initialWelcomeScreen"' in post_bytes_str:
            redirect_path = '/welcome'
        elif 'name="finalWelcomeScreen"' in post_bytes_str:
            check_name_and_bio = True
            redirect_path = '/welcome_final'
        elif 'name="welcomeCompleteButton"' in post_bytes_str:
            redirect_path = '/' + self.server.default_timeline
            welcome_screen_is_complete(self.server.base_dir, nickname,
                                       self.server.domain)
            on_final_welcome_screen = True
        elif 'name="submitExportTheme"' in post_bytes_str:
            print('submitExportTheme')
            theme_download_path = actor_str
            if export_theme(self.server.base_dir,
                            self.server.theme_name):
                theme_download_path += \
                    '/exports/' + self.server.theme_name + '.zip'
            print('submitExportTheme path=' + theme_download_path)
            redirect_headers(self, theme_download_path,
                             cookie, calling_domain)
            self.server.postreq_busy = False
            return
        elif 'name="submitExportBlocks"' in post_bytes_str:
            print('submitExportBlocks')
            blocks_download_path = actor_str + '/exports/blocks.csv'
            print('submitExportBlocks path=' + blocks_download_path)
            redirect_headers(self, blocks_download_path,
                             cookie, calling_domain)
            self.server.postreq_busy = False
            return

        # extract all of the text fields into a dict
        fields = \
            extract_text_fields_in_post(post_bytes, boundary, debug, None)
        if debug:
            if fields:
                print('DEBUG: profile update text ' +
                      'field extracted from POST ' + str(fields))
            else:
                print('WARN: profile update, no text ' +
                      'fields could be extracted from POST')

        # load the json for the actor for this user
        actor_filename = \
            acct_dir(base_dir, nickname, domain) + '.json'
        if os.path.isfile(actor_filename):
            actor_json = load_json(actor_filename)
            if actor_json:
                if not actor_json.get('discoverable'):
                    # discoverable in profile directory
                    # which isn't implemented in Epicyon
                    actor_json['discoverable'] = True
                    actor_changed = True
                if actor_json.get('capabilityAcquisitionEndpoint'):
                    del actor_json['capabilityAcquisitionEndpoint']
                    actor_changed = True

                _profile_post_avatar_image_ext(profile_media_types_uploaded,
                                               actor_json)

                actor_changed = \
                    _profile_post_skill_level(actor_json,
                                              fields,
                                              base_dir, nickname, domain,
                                              system_language,
                                              self.server.translate,
                                              actor_changed)

                _profile_post_change_password(base_dir, nickname, fields)

                _profile_post_set_reply_interval(base_dir, nickname, domain,
                                                 fields)
                _profile_post_change_city(base_dir, nickname, domain,
                                          fields)
                actor_changed, redirect_path = \
                    _profile_post_change_displayed_name(base_dir,
                                                        nickname, domain,
                                                        system_language,
                                                        actor_json,
                                                        fields,
                                                        check_name_and_bio,
                                                        actor_changed,
                                                        redirect_path)
                _profile_post_theme_change(base_dir, nickname,
                                           domain, domain_full,
                                           admin_nickname, fields,
                                           self.server.theme_name,
                                           http_prefix,
                                           allow_local_network_access,
                                           system_language,
                                           self.server.dyslexic_font, self)

                # is this the admin profile?
                if nickname == admin_nickname:
                    _profile_post_media_instance_status(base_dir, fields, self)

                    # is this a news theme?
                    if is_news_theme_name(self.server.base_dir,
                                          self.server.theme_name):
                        fields['newsInstance'] = 'on'

                    _profile_post_news_instance_status(base_dir, fields, self)

                    _profile_post_blog_instance_status(base_dir, fields, self)

                    _profile_post_instance_title(base_dir, fields)

                    _profile_post_youtube_alt_domain(base_dir, fields,
                                                     self)

                    _profile_post_twitter_alt_domain(base_dir, fields, self)

                    _profile_post_submit_button(base_dir, fields)

                    _profile_post_registrations_open(base_dir, fields, self)

                    _profile_post_replies_unlisted(base_dir, fields, self)

                    _profile_post_registrations_remaining(base_dir, fields)

                    _profile_post_libretranslate_url(base_dir, fields)

                    _profile_post_libretranslate_api_key(base_dir, fields)

                    _profile_post_content_license(base_dir, fields, self)

                    _profile_post_instance_short_desc(base_dir, fields)

                    _profile_post_instance_desc(base_dir, fields)

                    _profile_post_memorial_accounts(base_dir,
                                                    self.server.domain,
                                                    self.server.person_cache,
                                                    fields)
                actor_changed = \
                    _profile_post_email_address(actor_json, fields,
                                                actor_changed)

                actor_changed = \
                    _profile_post_xmpp_address(actor_json, fields,
                                               actor_changed)

                actor_changed = \
                    _profile_post_matrix_address(actor_json, fields,
                                                 actor_changed)

                actor_changed = \
                    _profile_post_ssb_address(actor_json, fields,
                                              actor_changed)

                actor_changed = \
                    _profile_post_blog_address(curr_session,
                                               self.server.base_dir,
                                               self.server.http_prefix,
                                               nickname, domain,
                                               actor_json, fields,
                                               actor_changed,
                                               self.server.debug)

                actor_changed = \
                    _profile_post_show_languages(actor_json, fields,
                                                 actor_changed)

                actor_changed = \
                    _profile_post_time_zone(base_dir, nickname, domain,
                                            fields, actor_changed, self)

                actor_changed = \
                    _profile_post_expiry(base_dir, nickname, domain,
                                         fields, actor_changed)

                _profile_post_max_preview(base_dir, nickname, domain, fields)

                actor_changed = \
                    _profile_post_birthday(fields, actor_json, actor_changed)

                actor_changed = \
                    _profile_post_tox_address(fields, actor_json,
                                              actor_changed)

                actor_changed = \
                    _profile_post_briar_address(fields, actor_json,
                                                actor_changed)

                actor_changed = \
                    _profile_post_cwtch_address(fields, actor_json,
                                                actor_changed)

                _profile_post_ntfy_url(base_dir, nickname, domain, fields)

                _profile_post_ntfy_topic(base_dir, nickname, domain, fields)

                actor_changed = \
                    _profile_post_enigma_pubkey(actor_json, fields,
                                                actor_changed)

                actor_changed = \
                    _profile_post_pgp_pubkey(actor_json, fields,
                                             actor_changed)

                actor_changed = \
                    _profile_post_pgp_fingerprint(actor_json, fields,
                                                  actor_changed)

                actor_changed = \
                    _profile_post_donation_link(actor_json, fields,
                                                actor_changed)

                actor_changed = \
                    _profile_post_website(curr_session,
                                          self.server.base_dir,
                                          self.server.http_prefix,
                                          nickname, domain,
                                          actor_json, fields,
                                          actor_changed,
                                          self.server.translate,
                                          self.server.debug)

                actor_changed = \
                    _profile_post_gemini_link(actor_json, fields,
                                              actor_changed)

                actor_changed, send_move_activity = \
                    _profile_post_moved(actor_json, fields,
                                        actor_changed,
                                        send_move_activity)

                actor_changed = \
                    _profile_post_occupation(actor_json, fields,
                                             actor_changed)

                actor_changed = \
                    _profile_post_featured_hashtags(actor_json, fields,
                                                    actor_changed)

                actor_changed = \
                    _profile_post_alsoknownas(actor_json, fields,
                                              actor_changed)

                actor_changed, redirect_path = \
                    _profile_post_bio(actor_json, fields,
                                      base_dir, http_prefix,
                                      nickname, domain, domain_full,
                                      system_language, self.server.translate,
                                      actor_changed,
                                      redirect_path,
                                      check_name_and_bio)

                admin_nickname = \
                    get_config_param(base_dir, 'admin')

                if admin_nickname:
                    # whether to require jsonld signatures
                    # on all incoming posts
                    if path.startswith('/users/' +
                                       admin_nickname + '/'):
                        _profile_post_show_nodeinfo(base_dir, fields, self)

                        _profile_post_show_nodeinfo_version(base_dir, fields,
                                                            self)
                        _profile_post_verify_all_signatures(base_dir, fields,
                                                            self)

                        _profile_post_broch_mode(base_dir, domain_full, fields)

                        _profile_post_shared_item_federation_domains(base_dir,
                                                                     fields,
                                                                     self)
                    # change moderators list
                    set_roles_from_list(base_dir, domain, admin_nickname,
                                        'moderators', 'moderator', fields,
                                        path, 'moderators.txt')

                    # change site editors list
                    set_roles_from_list(base_dir, domain, admin_nickname,
                                        'editors', 'editor', fields,
                                        path, 'editors.txt')

                    # change site devops list
                    set_roles_from_list(base_dir, domain, admin_nickname,
                                        'devopslist', 'devops', fields,
                                        path, 'devops.txt')

                    # change site counselors list
                    set_roles_from_list(base_dir, domain, admin_nickname,
                                        'counselors', 'counselor', fields,
                                        path, 'counselors.txt')

                    # change site artists list
                    set_roles_from_list(base_dir, domain, admin_nickname,
                                        'artists', 'artist', fields,
                                        path, 'artists.txt')

                # remove scheduled posts
                if fields.get('removeScheduledPosts'):
                    if fields['removeScheduledPosts'] == 'on':
                        remove_scheduled_posts(base_dir, nickname, domain)

                actor_changed = \
                    _profile_post_approve_followers(on_final_welcome_screen,
                                                    actor_json, fields,
                                                    actor_changed)

                _profile_post_reject_spam_actors(base_dir,
                                                 nickname, domain, fields)

                actor_changed = \
                    _profile_post_keep_dms(base_dir,
                                           nickname, domain,
                                           fields, actor_changed)

                _profile_post_remove_custom_font(base_dir, nickname, domain,
                                                 system_language,
                                                 admin_nickname,
                                                 self.server.dyslexic_font,
                                                 path, fields, self)

                actor_changed = \
                    _profile_post_dms_from_followers(base_dir,
                                                     nickname, domain,
                                                     on_final_welcome_screen,
                                                     fields,
                                                     actor_changed)

                _profile_post_remove_retweets(base_dir, nickname, domain,
                                              fields)

                _profile_post_hide_like_button(base_dir, nickname, domain,
                                               fields)

                min_img_acct = self.server.min_images_for_accounts
                _profile_post_minimize_images(base_dir, nickname, domain,
                                              fields, min_img_acct)

                _profile_post_hide_reaction_button(base_dir,
                                                   nickname, domain,
                                                   fields)

                _profile_post_bold_reading(base_dir, nickname, domain,
                                           fields, self)

                _profile_post_reverse_timelines(base_dir,
                                                nickname, domain,
                                                fields, self)

                account_dir = acct_dir(base_dir, nickname, domain)

                _profile_post_show_questions(base_dir,
                                             nickname, domain,
                                             fields, account_dir)

                _profile_post_only_follower_replies(fields, account_dir)

                _profile_post_mutuals_replies(account_dir, fields)

                # TODO
                # hide follows checkbox
                hide_follows_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/.hideFollows'
                hide_follows = False
                if fields.get('hideFollows'):
                    if fields['hideFollows'] == 'on':
                        hide_follows = True
                        self.server.hide_follows[nickname] = True
                        actor_json['hideFollows'] = True
                        actor_changed = True
                        try:
                            with open(hide_follows_filename, 'w+',
                                      encoding='utf-8') as rfile:
                                rfile.write('\n')
                        except OSError:
                            print('EX: unable to write hideFollows ' +
                                  hide_follows_filename)
                if not hide_follows:
                    actor_json['hideFollows'] = False
                    if self.server.hide_follows.get(nickname):
                        del self.server.hide_follows[nickname]
                        actor_changed = True
                    if os.path.isfile(hide_follows_filename):
                        try:
                            os.remove(hide_follows_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  hide_follows_filename)

                # block military instances
                block_mil_instances = False
                if fields.get('blockMilitary'):
                    if fields['blockMilitary'] == 'on':
                        block_mil_instances = True
                if block_mil_instances:
                    if not self.server.block_military.get(nickname):
                        self.server.block_military[nickname] = True
                        save_blocked_military(self.server.base_dir,
                                              self.server.block_military)
                else:
                    if self.server.block_military.get(nickname):
                        del self.server.block_military[nickname]
                        save_blocked_military(self.server.base_dir,
                                              self.server.block_military)

                # notify about new Likes
                if on_final_welcome_screen:
                    # default setting from welcome screen
                    try:
                        with open(notify_likes_filename, 'w+',
                                  encoding='utf-8') as rfile:
                            rfile.write('\n')
                    except OSError:
                        print('EX: unable to write notify likes ' +
                              notify_likes_filename)
                    actor_changed = True
                else:
                    notify_likes_active = False
                    if fields.get('notifyLikes'):
                        if fields['notifyLikes'] == 'on' and \
                           not hide_like_button_active:
                            notify_likes_active = True
                            try:
                                with open(notify_likes_filename, 'w+',
                                          encoding='utf-8') as rfile:
                                    rfile.write('\n')
                            except OSError:
                                print('EX: unable to write notify likes ' +
                                      notify_likes_filename)
                    if not notify_likes_active:
                        if os.path.isfile(notify_likes_filename):
                            try:
                                os.remove(notify_likes_filename)
                            except OSError:
                                print('EX: _profile_edit ' +
                                      'unable to delete ' +
                                      notify_likes_filename)

                notify_reactions_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/.notifyReactions'
                if on_final_welcome_screen:
                    # default setting from welcome screen
                    notify_react_filename = notify_reactions_filename
                    try:
                        with open(notify_react_filename, 'w+',
                                  encoding='utf-8') as rfile:
                            rfile.write('\n')
                    except OSError:
                        print('EX: unable to write notify reactions ' +
                              notify_reactions_filename)
                    actor_changed = True
                else:
                    notify_reactions_active = False
                    if fields.get('notifyReactions'):
                        if fields['notifyReactions'] == 'on' and \
                           not hide_reaction_button_active:
                            notify_reactions_active = True
                            try:
                                with open(notify_reactions_filename, 'w+',
                                          encoding='utf-8') as rfile:
                                    rfile.write('\n')
                            except OSError:
                                print('EX: unable to write ' +
                                      'notify reactions ' +
                                      notify_reactions_filename)
                    if not notify_reactions_active:
                        if os.path.isfile(notify_reactions_filename):
                            try:
                                os.remove(notify_reactions_filename)
                            except OSError:
                                print('EX: _profile_edit ' +
                                      'unable to delete ' +
                                      notify_reactions_filename)

                # this account is a bot
                if fields.get('isBot'):
                    if fields['isBot'] == 'on' and \
                       actor_json.get('type'):
                        if actor_json['type'] != 'Service':
                            actor_json['type'] = 'Service'
                            actor_changed = True
                else:
                    # this account is a group
                    if fields.get('isGroup'):
                        if fields['isGroup'] == 'on' and \
                           actor_json.get('type'):
                            if actor_json['type'] != 'Group':
                                # only allow admin to create groups
                                if path.startswith('/users/' +
                                                   admin_nickname + '/'):
                                    actor_json['type'] = 'Group'
                                    actor_changed = True
                    else:
                        # this account is a person (default)
                        if actor_json.get('type'):
                            if actor_json['type'] != 'Person':
                                actor_json['type'] = 'Person'
                                actor_changed = True

                # grayscale theme
                if path.startswith('/users/' + admin_nickname + '/') or \
                   is_artist(base_dir, nickname):
                    grayscale = False
                    if fields.get('grayscale'):
                        if fields['grayscale'] == 'on':
                            grayscale = True
                    if grayscale:
                        enable_grayscale(base_dir)
                    else:
                        disable_grayscale(base_dir)

                # dyslexic font
                if path.startswith('/users/' + admin_nickname + '/') or \
                   is_artist(base_dir, nickname):
                    dyslexic_font = False
                    if fields.get('dyslexicFont'):
                        if fields['dyslexicFont'] == 'on':
                            dyslexic_font = True
                    if dyslexic_font != self.server.dyslexic_font:
                        self.server.dyslexic_font = dyslexic_font
                        set_config_param(base_dir, 'dyslexicFont',
                                         self.server.dyslexic_font)
                        set_theme(base_dir,
                                  self.server.theme_name,
                                  self.server.domain,
                                  self.server.allow_local_network_access,
                                  self.server.system_language,
                                  self.server.dyslexic_font, False)

                # low bandwidth images checkbox
                if path.startswith('/users/' + admin_nickname + '/') or \
                   is_artist(base_dir, nickname):
                    curr_low_bandwidth = \
                        get_config_param(base_dir, 'lowBandwidth')
                    low_bandwidth = False
                    if fields.get('lowBandwidth'):
                        if fields['lowBandwidth'] == 'on':
                            low_bandwidth = True
                    if curr_low_bandwidth != low_bandwidth:
                        set_config_param(base_dir, 'lowBandwidth',
                                         low_bandwidth)
                        self.server.low_bandwidth = low_bandwidth

                # save filtered words list
                filter_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/filters.txt'
                if fields.get('filteredWords'):
                    try:
                        with open(filter_filename, 'w+',
                                  encoding='utf-8') as filterfile:
                            filterfile.write(fields['filteredWords'])
                    except OSError:
                        print('EX: unable to write filter ' +
                              filter_filename)
                else:
                    if os.path.isfile(filter_filename):
                        try:
                            os.remove(filter_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete filter ' +
                                  filter_filename)

                # save filtered words within bio list
                filter_bio_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/filters_bio.txt'
                if fields.get('filteredWordsBio'):
                    try:
                        with open(filter_bio_filename, 'w+',
                                  encoding='utf-8') as filterfile:
                            filterfile.write(fields['filteredWordsBio'])
                    except OSError:
                        print('EX: unable to write bio filter ' +
                              filter_bio_filename)
                else:
                    if os.path.isfile(filter_bio_filename):
                        try:
                            os.remove(filter_bio_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete bio filter ' +
                                  filter_bio_filename)

                # word replacements
                switch_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/replacewords.txt'
                if fields.get('switchwords'):
                    try:
                        with open(switch_filename, 'w+',
                                  encoding='utf-8') as switchfile:
                            switchfile.write(fields['switchwords'])
                    except OSError:
                        print('EX: unable to write switches ' +
                              switch_filename)
                else:
                    if os.path.isfile(switch_filename):
                        try:
                            os.remove(switch_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  switch_filename)

                # autogenerated tags
                auto_tags_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/autotags.txt'
                if fields.get('autoTags'):
                    try:
                        with open(auto_tags_filename, 'w+',
                                  encoding='utf-8') as autofile:
                            autofile.write(fields['autoTags'])
                    except OSError:
                        print('EX: unable to write auto tags ' +
                              auto_tags_filename)
                else:
                    if os.path.isfile(auto_tags_filename):
                        try:
                            os.remove(auto_tags_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  auto_tags_filename)

                # autogenerated content warnings
                auto_cw_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/autocw.txt'
                if fields.get('autoCW'):
                    try:
                        with open(auto_cw_filename, 'w+',
                                  encoding='utf-8') as auto_cw_file:
                            auto_cw_file.write(fields['autoCW'])
                    except OSError:
                        print('EX: unable to write auto CW ' +
                              auto_cw_filename)
                    self.server.auto_cw_cache[nickname] = \
                        fields['autoCW'].split('\n')
                else:
                    if os.path.isfile(auto_cw_filename):
                        try:
                            os.remove(auto_cw_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  auto_cw_filename)
                        self.server.auto_cw_cache[nickname] = []

                # save blocked accounts list
                if fields.get('blocked'):
                    add_account_blocks(base_dir,
                                       nickname, domain,
                                       fields['blocked'])
                else:
                    add_account_blocks(base_dir,
                                       nickname, domain, '')
                # import blocks from csv file
                if fields.get('importBlocks'):
                    blocks_str = fields['importBlocks']
                    while blocks_str.startswith('\n'):
                        blocks_str = blocks_str[1:]
                    blocks_lines = blocks_str.split('\n')
                    if import_blocking_file(base_dir, nickname, domain,
                                            blocks_lines):
                        print('blocks imported for ' + nickname)
                    else:
                        print('blocks not imported for ' + nickname)

                if fields.get('importFollows'):
                    filename_base = \
                        acct_dir(base_dir, nickname, domain) + \
                        '/import_following.csv'
                    follows_str = fields['importFollows']
                    while follows_str.startswith('\n'):
                        follows_str = follows_str[1:]
                    try:
                        with open(filename_base, 'w+',
                                  encoding='utf-8') as fp_foll:
                            fp_foll.write(follows_str)
                    except OSError:
                        print('EX: unable to write imported follows ' +
                              filename_base)

                if fields.get('importTheme'):
                    if not os.path.isdir(base_dir + '/imports'):
                        os.mkdir(base_dir + '/imports')
                    filename_base = \
                        base_dir + '/imports/newtheme.zip'
                    if os.path.isfile(filename_base):
                        try:
                            os.remove(filename_base)
                        except OSError:
                            print('EX: _profile_edit unable to delete ' +
                                  filename_base)
                    if nickname == admin_nickname or \
                       is_artist(base_dir, nickname):
                        if import_theme(base_dir, filename_base):
                            print(nickname + ' uploaded a theme')
                    else:
                        print('Only admin or artist can import a theme')

                # Save DM allowed instances list.
                # The allow list for incoming DMs,
                # if the .followDMs flag file exists
                dm_allowed_instances_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/dmAllowedInstances.txt'
                if fields.get('dmAllowedInstances'):
                    try:
                        with open(dm_allowed_instances_filename, 'w+',
                                  encoding='utf-8') as afile:
                            afile.write(fields['dmAllowedInstances'])
                    except OSError:
                        print('EX: unable to write allowed DM instances ' +
                              dm_allowed_instances_filename)
                else:
                    if os.path.isfile(dm_allowed_instances_filename):
                        try:
                            os.remove(dm_allowed_instances_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  dm_allowed_instances_filename)

                # save allowed instances list
                # This is the account level allow list
                allowed_instances_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/allowedinstances.txt'
                if fields.get('allowedInstances'):
                    inst_filename = allowed_instances_filename
                    try:
                        with open(inst_filename, 'w+',
                                  encoding='utf-8') as afile:
                            afile.write(fields['allowedInstances'])
                    except OSError:
                        print('EX: unable to write allowed instances ' +
                              allowed_instances_filename)
                else:
                    if os.path.isfile(allowed_instances_filename):
                        try:
                            os.remove(allowed_instances_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  allowed_instances_filename)

                if is_moderator(self.server.base_dir, nickname):
                    # set selected content warning lists
                    new_lists_enabled = ''
                    for name, _ in self.server.cw_lists.items():
                        list_var_name = get_cw_list_variable(name)
                        if fields.get(list_var_name):
                            if fields[list_var_name] == 'on':
                                if new_lists_enabled:
                                    new_lists_enabled += ', ' + name
                                else:
                                    new_lists_enabled += name
                    if new_lists_enabled != self.server.lists_enabled:
                        self.server.lists_enabled = new_lists_enabled
                        set_config_param(self.server.base_dir,
                                         "listsEnabled",
                                         new_lists_enabled)

                    # save blocked user agents
                    user_agents_blocked = []
                    if fields.get('userAgentsBlockedStr'):
                        user_agents_blocked_str = \
                            fields['userAgentsBlockedStr']
                        user_agents_blocked_list = \
                            user_agents_blocked_str.split('\n')
                        for uagent in user_agents_blocked_list:
                            if uagent in user_agents_blocked:
                                continue
                            user_agents_blocked.append(uagent.strip())
                    if str(self.server.user_agents_blocked) != \
                       str(user_agents_blocked):
                        self.server.user_agents_blocked = \
                            user_agents_blocked
                        user_agents_blocked_str = ''
                        for uagent in user_agents_blocked:
                            if user_agents_blocked_str:
                                user_agents_blocked_str += ','
                            user_agents_blocked_str += uagent
                        set_config_param(base_dir, 'userAgentsBlocked',
                                         user_agents_blocked_str)

                    # save allowed web crawlers
                    crawlers_allowed = []
                    if fields.get('crawlersAllowedStr'):
                        crawlers_allowed_str = \
                            fields['crawlersAllowedStr']
                        crawlers_allowed_list = \
                            crawlers_allowed_str.split('\n')
                        for uagent in crawlers_allowed_list:
                            if uagent in crawlers_allowed:
                                continue
                            crawlers_allowed.append(uagent.strip())
                    if str(self.server.crawlers_allowed) != \
                       str(crawlers_allowed):
                        self.server.crawlers_allowed = \
                            crawlers_allowed
                        crawlers_allowed_str = ''
                        for uagent in crawlers_allowed:
                            if crawlers_allowed_str:
                                crawlers_allowed_str += ','
                            crawlers_allowed_str += uagent
                        set_config_param(base_dir, 'crawlersAllowed',
                                         crawlers_allowed_str)

                    # save allowed buy domains
                    buy_sites = {}
                    if fields.get('buySitesStr'):
                        buy_sites_str = \
                            fields['buySitesStr']
                        buy_sites_list = \
                            buy_sites_str.split('\n')
                        for site_url in buy_sites_list:
                            if ' ' in site_url:
                                site_url = site_url.split(' ')[-1]
                                buy_icon_text = \
                                    site_url.replace(site_url, '').strip()
                                if not buy_icon_text:
                                    buy_icon_text = site_url
                            else:
                                buy_icon_text = site_url
                            if buy_sites.get(buy_icon_text):
                                continue
                            if '<' in site_url:
                                continue
                            if not site_url.strip():
                                continue
                            buy_sites[buy_icon_text] = site_url.strip()
                    if str(self.server.buy_sites) != \
                       str(buy_sites):
                        self.server.buy_sites = buy_sites
                        buy_sites_filename = \
                            base_dir + '/accounts/buy_sites.json'
                        if buy_sites:
                            save_json(buy_sites, buy_sites_filename)
                        else:
                            if os.path.isfile(buy_sites_filename):
                                try:
                                    os.remove(buy_sites_filename)
                                except OSError:
                                    print('EX: unable to delete ' +
                                          buy_sites_filename)

                    # save blocking API endpoints
                    block_ep_new = []
                    if fields.get('blockFederated'):
                        block_federated_str = \
                            fields['blockFederated']
                        block_ep_new = \
                            block_federated_str.split('\n')
                    if str(self.server.block_federated_endpoints) != \
                       str(block_ep_new):
                        base_dir = self.server.base_dir
                        self.server.block_federated_endpoints = \
                            save_block_federated_endpoints(base_dir,
                                                           block_ep_new)
                        if not block_ep_new:
                            self.server.block_federated = []

                    # save peertube instances list
                    peertube_instances_file = \
                        base_dir + '/accounts/peertube.txt'
                    if fields.get('ptInstances'):
                        self.server.peertube_instances.clear()
                        try:
                            with open(peertube_instances_file, 'w+',
                                      encoding='utf-8') as afile:
                                afile.write(fields['ptInstances'])
                        except OSError:
                            print('EX: unable to write peertube ' +
                                  peertube_instances_file)
                        pt_instances_list = \
                            fields['ptInstances'].split('\n')
                        if pt_instances_list:
                            for url in pt_instances_list:
                                url = url.strip()
                                if not url:
                                    continue
                                if url in self.server.peertube_instances:
                                    continue
                                self.server.peertube_instances.append(url)
                    else:
                        if os.path.isfile(peertube_instances_file):
                            try:
                                os.remove(peertube_instances_file)
                            except OSError:
                                print('EX: _profile_edit ' +
                                      'unable to delete ' +
                                      peertube_instances_file)
                        self.server.peertube_instances.clear()

                # save git project names list
                git_projects_filename = \
                    acct_dir(base_dir, nickname, domain) + \
                    '/gitprojects.txt'
                if fields.get('gitProjects'):
                    try:
                        with open(git_projects_filename, 'w+',
                                  encoding='utf-8') as afile:
                            afile.write(fields['gitProjects'].lower())
                    except OSError:
                        print('EX: unable to write git ' +
                              git_projects_filename)
                else:
                    if os.path.isfile(git_projects_filename):
                        try:
                            os.remove(git_projects_filename)
                        except OSError:
                            print('EX: _profile_edit ' +
                                  'unable to delete ' +
                                  git_projects_filename)

                # change memorial status
                if is_memorial_account(base_dir, nickname):
                    if not actor_json.get('memorial'):
                        actor_json['memorial'] = True
                        actor_changed = True
                elif actor_json.get('memorial'):
                    actor_json['memorial'] = False
                    actor_changed = True

                # save actor json file within accounts
                if actor_changed:
                    add_name_emojis_to_tags(base_dir, http_prefix,
                                            domain, self.server.port,
                                            actor_json)
                    # update the context for the actor
                    actor_json['@context'] = [
                        'https://www.w3.org/ns/activitystreams',
                        'https://w3id.org/security/v1',
                        get_default_person_context()
                    ]
                    if actor_json.get('nomadicLocations'):
                        del actor_json['nomadicLocations']
                    if not actor_json.get('featured'):
                        actor_json['featured'] = \
                            actor_json['id'] + '/collections/featured'
                    if not actor_json.get('featuredTags'):
                        actor_json['featuredTags'] = \
                            actor_json['id'] + '/collections/tags'
                    randomize_actor_images(actor_json)
                    add_actor_update_timestamp(actor_json)
                    # save the actor
                    save_json(actor_json, actor_filename)
                    webfinger_update(base_dir,
                                     nickname, domain,
                                     onion_domain, i2p_domain,
                                     self.server.cached_webfingers)
                    # also copy to the actors cache and
                    # person_cache in memory
                    store_person_in_cache(base_dir,
                                          actor_json['id'], actor_json,
                                          self.server.person_cache,
                                          True)
                    # clear any cached images for this actor
                    id_str = actor_json['id'].replace('/', '-')
                    remove_avatar_from_cache(base_dir, id_str)
                    # save the actor to the cache
                    actor_cache_filename = \
                        base_dir + '/cache/actors/' + \
                        actor_json['id'].replace('/', '#') + '.json'
                    save_json(actor_json, actor_cache_filename)
                    # send profile update to followers
                    update_actor_json = get_actor_update_json(actor_json)
                    print('Sending actor update: ' +
                          str(update_actor_json))
                    post_to_outbox(self, update_actor_json,
                                   self.server.project_version,
                                   nickname,
                                   curr_session, proxy_type)
                    # send move activity if necessary
                    if send_move_activity:
                        move_actor_json = get_actor_move_json(actor_json)
                        print('Sending Move activity: ' +
                              str(move_actor_json))
                        post_to_outbox(self, move_actor_json,
                                       self.server.project_version,
                                       nickname,
                                       curr_session, proxy_type)

                # deactivate the account
                if fields.get('deactivateThisAccount'):
                    if fields['deactivateThisAccount'] == 'on':
                        deactivate_account(base_dir,
                                           nickname, domain)
                        clear_login_details(self, nickname,
                                            calling_domain)
                        self.server.postreq_busy = False
                        return

    # redirect back to the profile screen
    redirect_headers(self, actor_str + redirect_path,
                     cookie, calling_domain)
    self.server.postreq_busy = False