416 lines
14 KiB
Python
416 lines
14 KiB
Python
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
|
|
import os
|
|
from platform import system
|
|
import json
|
|
from pprint import pprint
|
|
|
|
from utils.util import *
|
|
from utils.util import log, join_path
|
|
from common.event import Event
|
|
import utils.ui as util_ui
|
|
import utils.util as util_utils
|
|
import user_data
|
|
import wrapper_tests.support_testing as ts
|
|
|
|
global LOG
|
|
import logging
|
|
LOG = logging.getLogger('settings')
|
|
|
|
def merge_args_into_settings(args, settings):
|
|
if args:
|
|
print(repr(args.__dict__.keys()))
|
|
if not hasattr(args, 'audio'):
|
|
LOG.warn('No audio ' +repr(args))
|
|
settings['audio'] = getattr(args, 'audio')
|
|
if not hasattr(args, 'video'):
|
|
LOG.warn('No video ' +repr(args))
|
|
settings['video'] = getattr(args, 'video')
|
|
for key in settings.keys():
|
|
# proxy_type proxy_port proxy_host
|
|
not_key = 'not_' +key
|
|
if hasattr(args, key):
|
|
val = getattr(args, key)
|
|
if type(val) == bytes:
|
|
# proxy_host - ascii?
|
|
# filenames - ascii?
|
|
val = str(val, 'UTF-8')
|
|
settings[key] = val
|
|
elif hasattr(args, not_key):
|
|
val = not getattr(args, not_key)
|
|
settings[key] = val
|
|
clean_settings(settings)
|
|
return
|
|
|
|
def clean_settings(self):
|
|
# failsafe to ensure C tox is bytes and Py settings is str
|
|
|
|
# overrides
|
|
self['mirror_mode'] = False
|
|
# REQUIRED!!
|
|
if not os.path.exists('/proc/sys/net/ipv6'):
|
|
LOG.warn('Disabling IPV6 because /proc/sys/net/ipv6 does not exist')
|
|
self['ipv6_enabled'] = False
|
|
|
|
if 'proxy_type' in self and self['proxy_type'] == 0:
|
|
self['proxy_host'] = ''
|
|
self['proxy_port'] = 0
|
|
|
|
if 'proxy_type' in self and self['proxy_type'] != 0 and \
|
|
'proxy_host' in self and self['proxy_host'] != '' and \
|
|
'proxy_port' in self and self['proxy_port'] != 0:
|
|
if 'udp_enabled' in self and self['udp_enabled']:
|
|
# We don't currently support UDP over proxy.
|
|
LOG.info("UDP enabled and proxy set: disabling UDP")
|
|
self['udp_enabled'] = False
|
|
if 'local_discovery_enabled' in self and self['local_discovery_enabled']:
|
|
LOG.info("local_discovery_enabled enabled and proxy set: disabling local_discovery_enabled")
|
|
self['local_discovery_enabled'] = False
|
|
if 'dht_announcements_enabled' in self and self['dht_announcements_enabled']:
|
|
LOG.info("dht_announcements_enabled enabled and proxy set: disabling dht_announcements_enabled")
|
|
self['dht_announcements_enabled'] = False
|
|
|
|
if 'auto_accept_path' in self and \
|
|
type(self['auto_accept_path']) == bytes:
|
|
self['auto_accept_path'] = str(self['auto_accept_path'], 'UTF-8')
|
|
|
|
for key in Settings.get_default_settings():
|
|
if key not in self: continue
|
|
if type(self[key]) == bytes:
|
|
LOG.warn('bytes setting in: ' +key \
|
|
+' ' + repr(self[key]))
|
|
# ascii?
|
|
# self[key] = str(self[key], 'utf-8')
|
|
LOG.debug("Cleaned settings")
|
|
|
|
def get_user_config_path():
|
|
system = util_utils.get_platform()
|
|
if system == 'Windows':
|
|
return os.path.join(os.getenv('APPDATA'), 'Tox/')
|
|
elif system == 'Darwin':
|
|
return os.path.join(os.getenv('HOME'), 'Library/Application Support/Tox/')
|
|
else:
|
|
return os.path.join(os.getenv('HOME'), '.config/tox/')
|
|
|
|
def supported_languages():
|
|
return {
|
|
'English': 'en_EN',
|
|
'French': 'fr_FR',
|
|
'Russian': 'ru_RU',
|
|
'Ukrainian': 'uk_UA'
|
|
}
|
|
|
|
def built_in_themes():
|
|
return {
|
|
'dark': 'dark_style.qss',
|
|
'default': 'style.qss'
|
|
}
|
|
|
|
def get_global_settings_path():
|
|
return os.path.join(get_base_directory(), 'toxygen.json')
|
|
|
|
def is_active_profile(profile_path):
|
|
sFile = profile_path + '.lock'
|
|
if not os.path.isfile(sFile):
|
|
return False
|
|
try:
|
|
import psutil
|
|
except Exception as e:
|
|
return True
|
|
with open(sFile, 'rb') as iFd:
|
|
sPid = iFd.read()
|
|
if sPid and int(sPid.strip()) in psutil.pids():
|
|
return True
|
|
LOG.debug('Unlinking stale lock file ' +sFile)
|
|
try:
|
|
os.unlink(sFile)
|
|
except:
|
|
pass
|
|
return False
|
|
|
|
class Settings(dict):
|
|
"""
|
|
Settings of current profile + global app settings
|
|
"""
|
|
|
|
def __init__(self, toxes, path, app):
|
|
self._path = path
|
|
self._profile_path = path.replace('.json', '.tox')
|
|
self._toxes = toxes
|
|
self._app = app
|
|
self._args = app._oArgs
|
|
self._oArgs = app._oArgs
|
|
self._log = lambda l: LOG.log(self._oArgs.loglevel, l)
|
|
|
|
self._settings_saved_event = Event()
|
|
if path and os.path.isfile(path):
|
|
try:
|
|
with open(path, 'rb') as fl:
|
|
data = fl.read()
|
|
if toxes.is_data_encrypted(data):
|
|
data = toxes.pass_decrypt(data)
|
|
info = json.loads(str(data, 'utf-8'))
|
|
LOG.debug('Parsed settings from: ' + str(path))
|
|
except Exception as ex:
|
|
title = 'Error opening/parsing settings file: '
|
|
text = title + path
|
|
LOG.error(title +str(ex))
|
|
util_ui.message_box(text, title)
|
|
info = Settings.get_default_settings(app._oArgs)
|
|
user_data.settings.clean_settings(info)
|
|
else:
|
|
LOG.debug('get_default_settings for: ' + repr(path))
|
|
info = Settings.get_default_settings(app._oArgs)
|
|
|
|
if not os.path.exists(path):
|
|
merge_args_into_settings(app._oArgs, info)
|
|
else:
|
|
aC = self._changed(app._oArgs, info)
|
|
if aC:
|
|
title = 'Override profile with commandline - '
|
|
if path:
|
|
title += os.path.basename(path)
|
|
text = 'Override profile with command-line settings? \n'
|
|
# text += '\n'.join([str(key) +'=' +str(val) for
|
|
# key,val in self._changed(app._oArgs).items()])
|
|
text += repr(aC)
|
|
reply = util_ui.question(text, title)
|
|
if reply:
|
|
merge_args_into_settings(app._oArgs, info)
|
|
info['audio'] = getattr(app._oArgs, 'audio')
|
|
info['video'] = getattr(app._oArgs, 'video')
|
|
super().__init__(info)
|
|
self._upgrade()
|
|
|
|
LOG.info('Parsed settings from: ' + str(path))
|
|
ex = f"self=id(self) {self!r}"
|
|
LOG.debug(ex)
|
|
|
|
self.save()
|
|
self.locked = False
|
|
self.closing = False
|
|
self.unlockScreen = False
|
|
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Properties
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def get_settings_saved_event(self):
|
|
return self._settings_saved_event
|
|
|
|
settings_saved_event = property(get_settings_saved_event)
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Public methods
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def save(self):
|
|
text = json.dumps(self)
|
|
if self._toxes.has_password():
|
|
text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8')))
|
|
else:
|
|
text = bytes(text, 'utf-8')
|
|
tmp = self._path + str(os.getpid())
|
|
try:
|
|
with open(tmp, 'wb') as fl:
|
|
fl.write(text)
|
|
os.rename(tmp, self._path)
|
|
except Exception as e:
|
|
LOG.warn(f'Error saving to {self._path} ' +str(e))
|
|
else:
|
|
self._settings_saved_event(text)
|
|
|
|
def close(self):
|
|
path = self._profile_path + '.lock'
|
|
if os.path.isfile(path):
|
|
os.remove(path)
|
|
|
|
def set_active_profile(self):
|
|
"""
|
|
Mark current profile as active
|
|
"""
|
|
path = self._profile_path + '.lock'
|
|
try:
|
|
import shutil
|
|
except:
|
|
pass
|
|
else:
|
|
shutil.copy2(self._profile_path, path)
|
|
# need to open this with the same perms as _profile_path
|
|
# copy profile_path and then write?
|
|
with open(path, 'wb') as fl:
|
|
fl.write(bytes(str(os.getpid()), 'ascii'))
|
|
|
|
def export(self, path):
|
|
text = json.dumps(self)
|
|
name = os.path.basename(self._path)
|
|
with open(join_path(path, str(name)), 'w') as fl:
|
|
fl.write(text)
|
|
|
|
def update_path(self, new_path):
|
|
self._path = new_path
|
|
self.save()
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Static methods
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
@staticmethod
|
|
def get_auto_profile():
|
|
p = get_global_settings_path()
|
|
if not os.path.isfile(p):
|
|
return None
|
|
with open(p) as fl:
|
|
data = fl.read()
|
|
try:
|
|
auto = json.loads(data)
|
|
except Exception as ex:
|
|
LOG.warn(f"json.loads {data}: {ex!s}")
|
|
auto = {}
|
|
if 'profile_path' in auto:
|
|
path = str(auto['profile_path'])
|
|
if not os.path.isabs(path):
|
|
path = join_path(path, curr_directory(__file__))
|
|
if os.path.isfile(path):
|
|
return path
|
|
|
|
@staticmethod
|
|
def supported_languages():
|
|
# backwards
|
|
return supported_languages()
|
|
|
|
@staticmethod
|
|
def set_auto_profile(path):
|
|
p = get_global_settings_path()
|
|
if os.path.isfile(p):
|
|
with open(p) as fl:
|
|
data = fl.read()
|
|
data = json.loads(data)
|
|
else:
|
|
data = {}
|
|
data['profile_path'] = str(path)
|
|
with open(p, 'w') as fl:
|
|
fl.write(json.dumps(data))
|
|
|
|
@staticmethod
|
|
def reset_auto_profile():
|
|
p = get_global_settings_path()
|
|
if os.path.isfile(p):
|
|
with open(p) as fl:
|
|
data = fl.read()
|
|
data = json.loads(data)
|
|
else:
|
|
data = {}
|
|
if 'profile_path' in data:
|
|
del data['profile_path']
|
|
with open(p, 'w') as fl:
|
|
fl.write(json.dumps(data))
|
|
|
|
@staticmethod
|
|
def get_default_settings(args=None):
|
|
"""
|
|
Default profile settings
|
|
"""
|
|
retval = {
|
|
# FixMe: match? /var/local/src/c-toxcore/toxcore/tox.h
|
|
'ipv6_enabled': True,
|
|
'udp_enabled': True,
|
|
'trace_enabled': False,
|
|
'local_discovery_enabled': True,
|
|
'dht_announcements_enabled': True,
|
|
'proxy_type': 0,
|
|
'proxy_host': '',
|
|
'proxy_port': 0,
|
|
'start_port': 0,
|
|
'end_port': 0,
|
|
'tcp_port': 0,
|
|
'local_discovery_enabled': True,
|
|
'hole_punching_enabled': False,
|
|
# tox_log_cb *log_callback;
|
|
'experimental_thread_safety': False,
|
|
# operating_system
|
|
|
|
'theme': 'default',
|
|
'notifications': False,
|
|
'sound_notifications': False,
|
|
'language': 'English',
|
|
'calls_sound': False, # was True
|
|
|
|
'save_history': True,
|
|
'save_unsent_only': False,
|
|
'allow_inline': True,
|
|
'allow_auto_accept': True,
|
|
'auto_accept_path': None,
|
|
'sorting': 0,
|
|
'auto_accept_from_friends': [],
|
|
'paused_file_transfers': {},
|
|
'resend_files': True,
|
|
'friends_aliases': [],
|
|
'show_avatars': False,
|
|
'typing_notifications': False,
|
|
'blocked': [],
|
|
'plugins': [],
|
|
'notes': {},
|
|
'smileys': True,
|
|
'smiley_pack': 'default',
|
|
'mirror_mode': False,
|
|
'width': 920,
|
|
'height': 500,
|
|
'x': 400,
|
|
'y': 400,
|
|
'message_font_size': 14,
|
|
'unread_color': 'red',
|
|
'compact_mode': False,
|
|
'identicons': True,
|
|
'show_welcome_screen': True,
|
|
'close_app': 0,
|
|
'font': 'Times New Roman',
|
|
'update': 0,
|
|
'group_notifications': True,
|
|
'download_nodes_list': False, #
|
|
'download_nodes_url': 'https://nodes.tox.chat/json',
|
|
'notify_all_gc': False,
|
|
'backup_directory': None,
|
|
|
|
'audio': {'input': -1,
|
|
'output': -1,
|
|
'enabled': True},
|
|
'video': {'device': -1,
|
|
'width': 320,
|
|
'height': 240,
|
|
'x': 0,
|
|
'y': 0},
|
|
'current_nodes': None,
|
|
'network': 'new',
|
|
'tray_icon': False,
|
|
}
|
|
return retval
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Private methods
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def _upgrade(self):
|
|
default = Settings.get_default_settings()
|
|
for key in default:
|
|
if key not in self:
|
|
print(key)
|
|
self[key] = default[key]
|
|
|
|
def _changed(self, aArgs, info):
|
|
aRet = dict()
|
|
default = Settings.get_default_settings()
|
|
for key in default:
|
|
if key in ['audio', 'video']: continue
|
|
if key not in aArgs.__dict__: continue
|
|
val = aArgs.__dict__[key]
|
|
if val in ['0.0.0.0']: continue
|
|
if key in aArgs.__dict__ and key not in info:
|
|
# dunno = network
|
|
continue
|
|
if key in aArgs.__dict__ and info[key] != val:
|
|
aRet[key] = val
|
|
return aRet
|
|
|