WIP: change default data directories, subdirectories and config file

This commit is contained in:
caryoscelus 2024-05-07 14:03:44 +00:00
parent e8da744744
commit bdddf58712
No known key found for this signature in database
GPG key ID: 254EDDB85B66CB1F
27 changed files with 263 additions and 121 deletions

View file

@ -1,7 +1,7 @@
### zeronet-conservancy 0.7.10+ ### zeronet-conservancy 0.7.10+
- disable site-plugins installed for security reasons (@caryoscelus) - disable site-plugins installed for security reasons (@caryoscelus)
- fix downloading geoip db (@caryoscelus) - fix downloading geoip db (@caryoscelus)
- python <3.6 is officially unsupported - python <3.6 is officially unsupported (3.8 is more likely minimum requirement)
- SafeRe improvements by @geekless - SafeRe improvements by @geekless
- remove and don't update muted files (@caryoscelus) - remove and don't update muted files (@caryoscelus)
- option to disable port checking (@caryoscelus) - option to disable port checking (@caryoscelus)
@ -10,6 +10,9 @@
- fix chromium compatibility (@caryoscelus) - fix chromium compatibility (@caryoscelus)
- better fix of local sites leak (@caryoscelus) - better fix of local sites leak (@caryoscelus)
- ipython-based repl via --repl for debug/interactive development (@caryoscelus) - ipython-based repl via --repl for debug/interactive development (@caryoscelus)
- changes in directory structure (split data and config, use user directories by default)
- use version information from git if available
- different build types (portable vs package)
- various improvements - various improvements
### zeronet-conservancy 0.7.10 (2023-07-26) (18d35d3bed4f0683e99) ### zeronet-conservancy 0.7.10 (2023-07-26) (18d35d3bed4f0683e99)

View file

@ -32,6 +32,7 @@ def write_to(args, target):
f"branch = {branch!r}", f"branch = {branch!r}",
f"commit = {commit!r}", f"commit = {commit!r}",
f"version = {args.version!r}", f"version = {args.version!r}",
f"platform = {args.platform!r}",
])) ]))
def main(): def main():
@ -40,6 +41,7 @@ def main():
parser.add_argument('--version') parser.add_argument('--version')
parser.add_argument('--branch') parser.add_argument('--branch')
parser.add_argument('--commit') parser.add_argument('--commit')
parser.add_argument('--platform', default='source')
parser.add_argument('--stdout', action=argparse.BooleanOptionalAction, default=False) parser.add_argument('--stdout', action=argparse.BooleanOptionalAction, default=False)
args = parser.parse_args() args = parser.parse_args()
if args.stdout: if args.stdout:

View file

@ -14,7 +14,7 @@ from util import helper
class TrackerStorage(object): class TrackerStorage(object):
def __init__(self): def __init__(self):
self.log = logging.getLogger("TrackerStorage") self.log = logging.getLogger("TrackerStorage")
self.file_path = "%s/trackers.json" % config.data_dir self.file_path = config.start_dir / 'trackers.json'
self.load() self.load()
self.time_discover = 0.0 self.time_discover = 0.0
atexit.register(self.save) atexit.register(self.save)

View file

@ -9,7 +9,7 @@ from Config import config
@pytest.mark.usefixtures("resetTempSettings") @pytest.mark.usefixtures("resetTempSettings")
class TestAnnounceShare: class TestAnnounceShare:
def testAnnounceList(self, file_server): def testAnnounceList(self, file_server):
open("%s/trackers.json" % config.data_dir, "w").write("{}") (config.start_dir / 'trackers.json').open('w').write('{}')
tracker_storage = AnnounceSharePlugin.tracker_storage tracker_storage = AnnounceSharePlugin.tracker_storage
tracker_storage.load() tracker_storage.load()
peer = Peer(file_server.ip, 1544, connection_server=file_server) peer = Peer(file_server.ip, 1544, connection_server=file_server)

View file

@ -6,7 +6,7 @@ import time
class ChartDb(Db): class ChartDb(Db):
def __init__(self): def __init__(self):
self.version = 2 self.version = 2
super(ChartDb, self).__init__(self.getSchema(), "%s/chart.db" % config.data_dir) super(ChartDb, self).__init__(self.getSchema(), config.start_dir / 'chart.db')
self.foreign_keys = True self.foreign_keys = True
self.checkTables() self.checkTables()
self.sites = self.loadSites() self.sites = self.loadSites()

View file

@ -14,7 +14,7 @@ from util import helper
class ContentFilterStorage(object): class ContentFilterStorage(object):
def __init__(self, site_manager): def __init__(self, site_manager):
self.log = logging.getLogger("ContentFilterStorage") self.log = logging.getLogger("ContentFilterStorage")
self.file_path = "%s/filters.json" % config.data_dir self.file_path = config.config_dir / 'filters.json'
self.site_manager = site_manager self.site_manager = site_manager
self.file_content = self.load() self.file_content = self.load()
@ -36,12 +36,12 @@ class ContentFilterStorage(object):
def load(self): def load(self):
# Rename previously used mutes.json -> filters.json # Rename previously used mutes.json -> filters.json
if os.path.isfile("%s/mutes.json" % config.data_dir): if (config.config_dir / 'mutes.json').is_file():
self.log.info("Renaming mutes.json to filters.json...") self.log.info("Renaming mutes.json to filters.json...")
os.rename("%s/mutes.json" % config.data_dir, self.file_path) os.rename(config.config_dir / 'mutes.json', self.file_path)
if os.path.isfile(self.file_path): if self.file_path.is_file():
try: try:
return json.load(open(self.file_path)) return json.load(self.file_path.open())
except Exception as err: except Exception as err:
self.log.error("Error loading filters.json: %s" % err) self.log.error("Error loading filters.json: %s" % err)
return None return None

View file

@ -44,7 +44,7 @@ class UiRequestPlugin(object):
if ".zip/" in path or ".tar.gz/" in path: if ".zip/" in path or ".tar.gz/" in path:
file_obj = None file_obj = None
path_parts = self.parsePath(path) path_parts = self.parsePath(path)
file_path = "%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"]) file_path = config.data_dir / path_parts["address"] / path_parts["inner_path"]
match = re.match(r"^(.*\.(?:tar.gz|zip))/(.*)", file_path) match = re.match(r"^(.*\.(?:tar.gz|zip))/(.*)", file_path)
archive_path, path_within = match.groups() archive_path, path_within = match.groups()
if archive_path not in archive_cache: if archive_path not in archive_cache:

View file

@ -686,7 +686,7 @@ class UiWebsocketPlugin(object):
if sys.platform == "linux": if sys.platform == "linux":
sys_db_paths += ['/usr/share/GeoIP/' + db_name] sys_db_paths += ['/usr/share/GeoIP/' + db_name]
data_dir_db_path = os.path.join(config.data_dir, db_name) data_dir_db_path = config.data_dir / db_name
db_paths = sys_db_paths + [data_dir_db_path] db_paths = sys_db_paths + [data_dir_db_path]

View file

@ -12,7 +12,7 @@ class BootstrapperDb(Db.Db):
def __init__(self): def __init__(self):
self.version = 7 self.version = 7
self.hash_ids = {} # hash -> id cache self.hash_ids = {} # hash -> id cache
super(BootstrapperDb, self).__init__({"db_name": "Bootstrapper"}, "%s/bootstrapper.db" % config.data_dir) super(BootstrapperDb, self).__init__({"db_name": "Bootstrapper"}, config.start_dir / 'bootstrapper.db')
self.foreign_keys = True self.foreign_keys = True
self.checkTables() self.checkTables()
self.updateHashCache() self.updateHashCache()

View file

@ -16,7 +16,7 @@ def importPluginnedClasses():
from User import UserManager from User import UserManager
try: try:
local_master_addresses = set(json.load(open("%s/users.json" % config.data_dir)).keys()) # Users in users.json local_master_addresses = set(json.load((config.private_dir / 'users.json').open()).keys()) # Users in users.json
except Exception as err: except Exception as err:
local_master_addresses = set() local_master_addresses = set()

View file

@ -8,7 +8,8 @@ from User import UserManager
class TestMultiuser: class TestMultiuser:
def testMemorySave(self, user): def testMemorySave(self, user):
# It should not write users to disk # It should not write users to disk
users_before = open("%s/users.json" % config.data_dir).read() users_json = config.private_dir / 'users.json'
users_before = users_json.open().read()
user = UserManager.user_manager.create() user = UserManager.user_manager.create()
user.save() user.save()
assert open("%s/users.json" % config.data_dir).read() == users_before assert users_json.open().read() == users_before

View file

@ -92,8 +92,8 @@ class Actions:
from Site import SiteManager from Site import SiteManager
SiteManager.site_manager.load() SiteManager.site_manager.load()
os.mkdir("%s/%s" % (config.data_dir, address)) (config.data_dir / address).mkdir()
open("%s/%s/index.html" % (config.data_dir, address), "w").write("Hello %s!" % address) (config.data_dir / address / 'index.html').open('w').write(f"Hello {address}!")
logging.info("Creating content.json...") logging.info("Creating content.json...")
site = Site(address) site = Site(address)

View file

@ -9,10 +9,18 @@ import logging
import logging.handlers import logging.handlers
import stat import stat
import time import time
from pathlib import Path
VERSION = "0.7.10+" VERSION = "0.7.10+"
class StartupError(RuntimeError):
pass
class Config: class Config:
"""Class responsible for storing and loading config.
Used as singleton `config`
"""
def __init__(self, argv): def __init__(self, argv):
try: try:
@ -23,11 +31,13 @@ class Config:
self.branch = Git.branch() or 'unknown' self.branch = Git.branch() or 'unknown'
self.commit = Git.commit() or 'unknown' self.commit = Git.commit() or 'unknown'
self.version = VERSION self.version = VERSION
self.platform = 'source'
else: else:
self.build_type = Build.build_type self.build_type = Build.build_type
self.branch = Build.branch self.branch = Build.branch
self.commit = Build.commit self.commit = Build.commit
self.version = Build.version or VERSION self.version = Build.version or VERSION
self.platform = Build.platform
self.version_full = f'{self.version} ({self.build_type} from {self.branch}-{self.commit})' self.version_full = f'{self.version} ({self.build_type} from {self.branch}-{self.commit})'
self.user_agent = "conservancy" self.user_agent = "conservancy"
# for compatibility # for compatibility
@ -45,15 +55,18 @@ class Config:
self.keys_restart_need = set([ self.keys_restart_need = set([
"tor", "fileserver_port", "fileserver_ip_type", "threads_fs_read", "threads_fs_write", "threads_crypt", "threads_db" "tor", "fileserver_port", "fileserver_ip_type", "threads_fs_read", "threads_fs_write", "threads_crypt", "threads_db"
]) ])
self.start_dir = self.getStartDir()
self.config_file = self.start_dir + "/zeronet.conf" self.config_file = None
self.private_dir = self.start_dir + '/private' self.config_dir = None
self.data_dir = self.start_dir + "/data" self.data_dir = None
self.log_dir = self.start_dir + "/log" self.private_dir = None
self.log_dir = None
self.configurePaths(argv)
self.openssl_lib_file = None self.openssl_lib_file = None
self.openssl_bin_file = None self.openssl_bin_file = None
self.trackers_file = False self.trackers_file = None
self.createParser() self.createParser()
self.createArguments() self.createArguments()
@ -70,46 +83,21 @@ class Config:
def strToBool(self, v): def strToBool(self, v):
return v.lower() in ("yes", "true", "t", "1") return v.lower() in ("yes", "true", "t", "1")
def getStartDir(self): def getStartDirOld(self):
"""Return directory with config & data""" """Get directory that would have been used by older versions (pre v0.7.11)"""
this_file = os.path.abspath(__file__).replace("\\", "/").rstrip("cd")
if "--start-dir" in self.argv: if "--start-dir" in self.argv:
return self.argv[self.argv.index("--start-dir") + 1] start_dir = self.argv[self.argv.index("--start-dir") + 1]
elif this_file.endswith("/Contents/Resources/core/src/Config.py"):
if '--portable' in self.argv or self.build_type == 'portable': # Running as ZeroNet.app
return '.' if this_file.startswith("/Application") or this_file.startswith("/private") or this_file.startswith(os.path.expanduser("~/Library")):
# Runnig from non-writeable directory, put data to Application Support
here = os.path.dirname(os.path.abspath(__file__).replace("\\", "/")).rstrip('/src')
if os.path.isdir(f'{here}/data') and not '--no-portable' in self.argv:
print('WARNING: found data in current directory')
print(' It used to be default behaviour to store data alongside project directory,')
print(' but now we default to place data and config in user home directory.')
print(' If you want to keep previous behaviour, please use --portable')
print('Assuming implicit --portable (use --no-portable to override)')
print(self.argv)
self.argv.insert(1, '--portable')
print(self.argv)
return '.'
home_zn = os.path.expanduser(f'~/ZeroNet')
if os.path.isdir(home_zn):
print(f'WARNING: found data in {home_zn}')
print( ' It is possible that this is from previous version or another installation')
print( ' altogether. If you want to use that data directory with zeronet-conservancy')
print(f' you have to run it with --start-dir "{home_zn}" option')
if platform.system() == 'Linux':
# TODO: XDG!
return os.path.expanduser('~/.local/zeronet-conservancy')
if platform.system() == 'Darwin':
return os.path.expanduser("~/Library/Application Support/zeronet-conservancy")
if platform.system() == 'Windows':
return os.path.expanduser('~/AppData/zeronet-conservancy')
elif here.endswith("/Contents/Resources/core/src"):
start_dir = os.path.expanduser("~/Library/Application Support/ZeroNet") start_dir = os.path.expanduser("~/Library/Application Support/ZeroNet")
elif this_file.endswith("/core/src"): else:
# Running from writeable directory put data next to .app
start_dir = re.sub("/[^/]+/Contents/Resources/core/src/Config.py", "", this_file)
elif this_file.endswith("/core/src/Config.py"):
# Running as exe or source is at Application Support directory, put var files to outside of core dir # Running as exe or source is at Application Support directory, put var files to outside of core dir
start_dir = this_file.replace("/core/src/Config.py", "") start_dir = this_file.replace("/core/src/Config.py", "")
elif not os.access(this_file.replace('/src/Config.py', ''), os.R_OK | os.W_OK): elif not os.access(this_file.replace('/src/Config.py', ''), os.R_OK | os.W_OK):
@ -117,6 +105,144 @@ class Config:
start_dir = os.path.expanduser("~/ZeroNet") start_dir = os.path.expanduser("~/ZeroNet")
else: else:
start_dir = "." start_dir = "."
return start_dir
def migrateOld(self, source):
print(f'[bold red]WARNING: found data {source}[/bold red]')
print( ' It used to be default behaviour to store data there,')
print( ' but now we default to place data and config in user home directory.')
print( '')
def configurePaths(self, argv):
if '--config-file' in argv:
self.config_file = argv[argv.index('--config-file') + 1]
old_dir = Path(self.getStartDirOld())
new_dir = Path(self.getStartDir())
no_migrate = '--no-migrate' in argv
silent_migrate = '--portable' in argv or '--migrate' in argv
try:
self.start_dir = self.maybeMigrate(old_dir, new_dir, no_migrate, silent_migrate)
except Exception as ex:
raise ex
self.updatePaths()
def updatePaths(self):
if self.config_file is None:
self.config_file = self.start_dir / 'znc.conf'
if self.config_dir is None:
self.config_dir = self.start_dir
if self.private_dir is None:
self.private_dir = self.start_dir / 'private'
if self.data_dir is None:
self.data_dir = self.start_dir / 'data'
if self.log_dir is None:
self.log_dir = self.start_dir / 'log'
def createPaths(self):
self.start_dir.mkdir(parents=True, exist_ok=True)
self.private_dir.mkdir(parents=True, exist_ok=True)
self.data_dir.mkdir(parents=True, exist_ok=True)
self.log_dir.mkdir(parents=True, exist_ok=True)
def checkDir(self, root):
return (root / 'znc.conf').is_file()
def doMigrate(self, old_dir, new_dir):
raise RuntimeError('Migration not implemented yet')
def askMigrate(self, old_dir, new_dir, silent):
if not sys.stdin.isatty():
raise StartupError('Migration refused: non-interactive shell')
while True:
r = input(f'You have old data in `{old_dir}`. Migrate to new format to `{new_dir}`? [Y/n]')
if r.lower().startswith('n'):
raise StartupError('Migration refused')
if r.lower().startswith('y'):
return self.doMigrate(old_dir, new_dir)
def createNewConfig(self, new_dir):
new_dir.mkdir(parents=True, exist_ok=True)
with (new_dir / 'znc.conf').open('w') as f:
f.write('# zeronet-conervancy config file')
def maybeMigrate(self, old_dir, new_dir, no_migrate, silent_migrate):
if old_dir.exists() and new_dir.exists():
if old_dir == new_dir:
if self.checkDir(new_dir):
return new_dir
elif no_migrate:
return StartError('Migration refused, but new directory should be migrated')
else:
return askMigrate(old_dir, new_dir, silent_migrate)
else:
if self.checkDir(new_dir):
if not no_migrate:
print("There's an old starting directory")
return new_dir
else:
raise StartupError('Bad startup directory')
elif old_dir.exists():
if no_migrate:
self.createNewConfig(new_dir)
return new_dir
else:
return self.askMigrate(old_dir, new_dir, silent_migrate)
elif new_dir.exists():
if self.checkDir(new_dir):
return new_dir
else:
return StartupError('Bad startup directory')
else:
self.createNewConfig(new_dir)
return new_dir
def getStartDir(self):
"""Return directory with config & data"""
if "--start-dir" in self.argv:
return self.argv[self.argv.index("--start-dir") + 1]
here = os.path.dirname(os.path.abspath(__file__).replace("\\", "/")).rstrip('/src')
if '--portable' in self.argv or self.build_type == 'portable':
return here
# if os.path.isdir(f'{here}/data') and not '--no-portable' in self.argv:
# print('WARNING: found data in current directory')
# print(' It used to be default behaviour to store data alongside project directory,')
# print(' but now we default to place data and config in user home directory.')
# print(' If you want to keep previous behaviour, please use --portable')
# print('Assuming implicit --portable (use --no-portable to override)')
# print(self.argv)
# self.argv.insert(1, '--portable')
# print(self.argv)
# return here
# home_zn = os.path.expanduser(f'~/ZeroNet')
# if os.path.isdir(home_zn):
# print(f'WARNING: found data in {home_zn}')
# print( ' It is possible that this is from previous version or another installation')
# print( ' altogether. If you want to use that data directory with zeronet-conservancy')
# print(f' you have to run it with --start-dir "{home_zn}" option')
MACOSX_DIR = '~/Library/Application Support/zeronet-conservancy'
WINDOWS_DIR = '~/AppData/zeronet-conservancy'
LIBREDESKTOP_DIR = '~/.local/share/zeronet-conservancy'
if self.platform == 'source':
if platform.system() == 'Darwin':
path = MACOSX_DIR
elif platform.system() == 'Windows':
path = WINDOWS_DIR
else:
path = LIBREDESKTOP_DIR
elif self.platform == 'macosx':
path = MACOSX_DIR
elif self.platform == 'windows':
path = WINDOWS_DIR
elif self.platform == 'libredesktop':
path = LIBREDESKTOP_DIR
else:
raise RuntimeError(f'UNKNOWN PLATFORM: {self.platform}. Something must have went terribly wrong!')
return os.path.expanduser(path)
# Create command line arguments # Create command line arguments
def createArguments(self): def createArguments(self):
@ -135,9 +261,9 @@ class Config:
else: else:
fix_float_decimals = False fix_float_decimals = False
config_file = self.start_dir + "/zeronet.conf" config_file = self.config_file
data_dir = self.start_dir + "/data" data_dir = self.data_dir
log_dir = self.start_dir + "/log" log_dir = self.log_dir
ip_local = ["127.0.0.1", "::1"] ip_local = ["127.0.0.1", "::1"]
@ -256,9 +382,10 @@ class Config:
self.parser.add_argument('--batch', help="Batch mode (No interactive input for commands)", action='store_true') self.parser.add_argument('--batch', help="Batch mode (No interactive input for commands)", action='store_true')
self.parser.add_argument('--portable', action=argparse.BooleanOptionalAction) self.parser.add_argument('--portable', action=argparse.BooleanOptionalAction)
self.parser.add_argument('--start-dir', help='Path of working dir for variable content (data, log, .conf)', default=self.start_dir, metavar="path") self.parser.add_argument('--start-dir', help='Path of working dir for variable content (data, log, config)', default=self.start_dir, metavar="path")
self.parser.add_argument('--config-file', help='Path of config file', default=config_file, metavar="path") self.parser.add_argument('--config-file', help='Path of config file', default=config_file, metavar="path")
self.parser.add_argument('--data-dir', help='Path of data directory', default=data_dir, metavar="path") self.parser.add_argument('--data-dir', help='Path of data directory', default=data_dir, metavar="path")
self.parser.add_argument('--no-migrate', help='Ignore data directories from old 0net versions', action=argparse.BooleanOptionalAction, default=False)
self.parser.add_argument('--console-log-level', help='Level of logging to console', default="default", choices=["default", "DEBUG", "INFO", "ERROR", "off"]) self.parser.add_argument('--console-log-level', help='Level of logging to console', default="default", choices=["default", "DEBUG", "INFO", "ERROR", "off"])
@ -352,7 +479,7 @@ class Config:
return self.parser return self.parser
def loadTrackersFile(self): def loadTrackersFile(self):
if not self.trackers_file: if self.trackers_file is None:
return None return None
self.trackers = self.arguments.trackers[:] self.trackers = self.arguments.trackers[:]
@ -362,16 +489,19 @@ class Config:
if trackers_file.startswith("/"): # Absolute if trackers_file.startswith("/"): # Absolute
trackers_file_path = trackers_file trackers_file_path = trackers_file
elif trackers_file.startswith("{data_dir}"): # Relative to data_dir elif trackers_file.startswith("{data_dir}"): # Relative to data_dir
trackers_file_path = trackers_file.replace("{data_dir}", self.data_dir) trackers_file_path = trackers_file.replace('{data_dir}', str(self.data_dir))
else: # Relative to zeronet.py else:
trackers_file_path = self.start_dir + "/" + trackers_file # Relative to zeronet.py or something else, unsupported
raise RuntimeError(f'trackers_file should be relative to {{data_dir}} or absolute path (not {trackers_file})')
for line in open(trackers_file_path): for line in open(trackers_file_path):
tracker = line.strip() tracker = line.strip()
if "://" in tracker and tracker not in self.trackers: if "://" in tracker and tracker not in self.trackers:
self.trackers.append(tracker) self.trackers.append(tracker)
except Exception as err: except Exception as err:
print("Error loading trackers file: %s" % err) print(self.trackers_file)
print(trackers_file)
print(f'Error loading trackers file: {err}')
# Find arguments specified for current action # Find arguments specified for current action
def getActionArguments(self): def getActionArguments(self):
@ -436,6 +566,8 @@ class Config:
self.parseCommandline(argv, silent) # Parse argv self.parseCommandline(argv, silent) # Parse argv
self.setAttributes() self.setAttributes()
self.updatePaths()
self.createPaths()
if parse_config: if parse_config:
argv = self.parseConfig(argv) # Add arguments from config file argv = self.parseConfig(argv) # Add arguments from config file
@ -460,7 +592,7 @@ class Config:
for arg in args: for arg in args:
if arg.startswith('--') and '_' in arg: if arg.startswith('--') and '_' in arg:
farg = arg.replace('_', '-') farg = arg.replace('_', '-')
print(f'WARNING: using deprecated flag in command line: {arg} should be {farg}') print(f'[bold red]WARNING: using deprecated flag in command line: {arg} should be {farg}[/bold red]')
print('Support for deprecated flags might be removed in the future') print('Support for deprecated flags might be removed in the future')
else: else:
farg = arg farg = arg
@ -494,9 +626,6 @@ class Config:
def parseConfig(self, argv): def parseConfig(self, argv):
argv = self.fixArgs(argv) argv = self.fixArgs(argv)
# Find config file path from parameters
if "--config-file" in argv:
self.config_file = argv[argv.index("--config-file") + 1]
# Load config file # Load config file
if os.path.isfile(self.config_file): if os.path.isfile(self.config_file):
config = configparser.RawConfigParser(allow_no_value=True, strict=False) config = configparser.RawConfigParser(allow_no_value=True, strict=False)
@ -539,7 +668,7 @@ class Config:
val = val[:] val = val[:]
if key in ("data_dir", "log_dir", "start_dir", "openssl_bin_file", "openssl_lib_file"): if key in ("data_dir", "log_dir", "start_dir", "openssl_bin_file", "openssl_lib_file"):
if val: if val:
val = val.replace("\\", "/") val = Path(val)
setattr(self, key, val) setattr(self, key, val)
def loadPlugins(self): def loadPlugins(self):

View file

@ -153,7 +153,7 @@ content_dbs = {}
def getContentDb(path=None): def getContentDb(path=None):
if not path: if not path:
path = "%s/content.db" % config.data_dir path = config.data_dir / 'content.db'
if path not in content_dbs: if path not in content_dbs:
content_dbs[path] = ContentDb(path) content_dbs[path] = ContentDb(path)
content_dbs[path].init() content_dbs[path].init()

View file

@ -24,20 +24,20 @@ class CryptConnectionManager:
self.context_server = None self.context_server = None
self.openssl_conf_template = "src/lib/openssl/openssl.cnf" self.openssl_conf_template = "src/lib/openssl/openssl.cnf"
self.openssl_conf = config.data_dir + "/openssl.cnf" self.openssl_conf = config.private_dir / "openssl.cnf"
self.openssl_env = { self.openssl_env = {
"OPENSSL_CONF": self.openssl_conf, "OPENSSL_CONF": self.openssl_conf,
"RANDFILE": config.data_dir + "/openssl-rand.tmp" "RANDFILE": config.private_dir / "openssl-rand.tmp"
} }
self.crypt_supported = [] # Supported cryptos self.crypt_supported = [] # Supported cryptos
self.cacert_pem = config.data_dir + "/cacert-rsa.pem" self.cacert_pem = config.private_dir / "cacert-rsa.pem"
self.cakey_pem = config.data_dir + "/cakey-rsa.pem" self.cakey_pem = config.private_dir / "cakey-rsa.pem"
self.cert_pem = config.data_dir + "/cert-rsa.pem" self.cert_pem = config.private_dir / "cert-rsa.pem"
self.cert_csr = config.data_dir + "/cert-rsa.csr" self.cert_csr = config.private_dir / "cert-rsa.csr"
self.key_pem = config.data_dir + "/key-rsa.pem" self.key_pem = config.private_dir / "key-rsa.pem"
self.log = logging.getLogger("CryptConnectionManager") self.log = logging.getLogger("CryptConnectionManager")
self.log.debug("Version: %s" % ssl.OPENSSL_VERSION) self.log.debug("Version: %s" % ssl.OPENSSL_VERSION)
@ -105,8 +105,8 @@ class CryptConnectionManager:
if config.keep_ssl_cert: if config.keep_ssl_cert:
return False return False
for file_name in ["cert-rsa.pem", "key-rsa.pem", "cacert-rsa.pem", "cakey-rsa.pem", "cacert-rsa.srl", "cert-rsa.csr", "openssl-rand.tmp"]: for file_name in ["cert-rsa.pem", "key-rsa.pem", "cacert-rsa.pem", "cakey-rsa.pem", "cacert-rsa.srl", "cert-rsa.csr", "openssl-rand.tmp"]:
file_path = "%s/%s" % (config.data_dir, file_name) file_path = config.data_dir / file_name
if os.path.isfile(file_path): if file_path.is_file():
os.unlink(file_path) os.unlink(file_path)
# Load and create cert files is necessary # Load and create cert files is necessary

View file

@ -21,7 +21,7 @@ else:
class DebugReloader: class DebugReloader:
def __init__(self, paths=None): def __init__(self, paths=None):
if not paths: if not paths:
paths = ["src", "plugins", config.data_dir + "/__plugins__"] paths = ["src", "plugins"]
self.log = logging.getLogger("DebugReloader") self.log = logging.getLogger("DebugReloader")
self.last_chaged = 0 self.last_chaged = 0
self.callbacks = [] self.callbacks = []

View file

@ -25,7 +25,7 @@ class PluginManager:
self.after_load = [] # Execute functions after loaded plugins self.after_load = [] # Execute functions after loaded plugins
self.function_flags = {} # Flag function for permissions self.function_flags = {} # Flag function for permissions
self.reloading = False self.reloading = False
self.config_path = config.data_dir + "/plugins.json" self.config_path = config.config_dir / 'plugins.json'
self.loadConfig() self.loadConfig()
self.config.setdefault("builtin", {}) self.config.setdefault("builtin", {})

View file

@ -88,9 +88,10 @@ class Site(object):
def loadSettings(self, settings=None): def loadSettings(self, settings=None):
if not settings: if not settings:
try: try:
settings = json.load(open(f'{config.data_dir}/sites.json')).get(self.address) with (config.private_dir / 'sites.json').open() as f:
settings = json.load(f).get(self.address)
except Exception as err: except Exception as err:
logging.error(f'Error loading {config.data_dir}/sites.json: {err}') logging.error(f'Error loading {config.private_dir}/sites.json: {err}')
settings = {} settings = {}
if settings: if settings:
self.settings = settings self.settings = settings

View file

@ -38,7 +38,7 @@ class SiteManager(object):
load_s = time.time() load_s = time.time()
# Load new adresses # Load new adresses
try: try:
json_path = f"{config.data_dir}/sites.json" json_path = config.private_dir / 'sites.json'
data = json.load(open(json_path)) data = json.load(open(json_path))
except Exception as err: except Exception as err:
self.log.error(f"Unable to load {json_path}: {err}") self.log.error(f"Unable to load {json_path}: {err}")
@ -48,7 +48,7 @@ class SiteManager(object):
for address, settings in data.items(): for address, settings in data.items():
if address not in self.sites: if address not in self.sites:
if os.path.isfile("%s/%s/content.json" % (config.data_dir, address)): if (config.data_dir / address / 'content.json').is_file():
# Root content.json exists, try load site # Root content.json exists, try load site
s = time.time() s = time.time()
try: try:
@ -121,7 +121,7 @@ class SiteManager(object):
s = time.time() s = time.time()
if data: if data:
helper.atomicWrite("%s/sites.json" % config.data_dir, helper.jsonDumps(data).encode("utf8")) helper.atomicWrite(config.private_dir / 'sites.json', helper.jsonDumps(data).encode("utf8"))
else: else:
self.log.debug("Save error: No data") self.log.debug("Save error: No data")
time_write = time.time() - s time_write = time.time() - s

View file

@ -29,7 +29,7 @@ thread_pool_fs_batch = ThreadPool.ThreadPool(1, name="FS batch")
class SiteStorage(object): class SiteStorage(object):
def __init__(self, site, allow_create=True): def __init__(self, site, allow_create=True):
self.site = site self.site = site
self.directory = f'{config.data_dir}/{self.site.address}' # Site data diretory self.directory = config.data_dir / self.site.address # Site data diretory
self.allowed_dir = os.path.abspath(self.directory) # Only serve file within this dir self.allowed_dir = os.path.abspath(self.directory) # Only serve file within this dir
self.log = site.log self.log = site.log
self.db = None # Db class self.db = None # Db class

View file

@ -784,7 +784,7 @@ class UiRequest:
address = path_parts["address"] address = path_parts["address"]
file_path = "%s/%s/%s" % (config.data_dir, address, path_parts["inner_path"]) file_path = config.data_dir / address / path_parts['inner_path']
if (config.debug or config.merge_media) and file_path.split("/")[-1].startswith("all."): if (config.debug or config.merge_media) and file_path.split("/")[-1].startswith("all."):
# If debugging merge *.css to all.css and *.js to all.js # If debugging merge *.css to all.css and *.js to all.js

View file

@ -35,7 +35,7 @@ class User(object):
# Save to data/users.json # Save to data/users.json
@util.Noparallel(queue=True, ignore_class=True) @util.Noparallel(queue=True, ignore_class=True)
def save(self): def save(self):
users_json = f'{config.private_dir}/users.json' users_json = config.private_dir / 'users.json'
s = time.time() s = time.time()
users = json.load(open(users_json)) users = json.load(open(users_json))
if self.master_address not in users: if self.master_address not in users:

View file

@ -25,7 +25,7 @@ class UserManager(object):
s = time.time() s = time.time()
# Load new users # Load new users
try: try:
json_path = f'{config.private_dir}/users.json' json_path = config.private_dir / 'users.json'
data = json.load(open(json_path)) data = json.load(open(json_path))
except Exception as err: except Exception as err:
raise Exception("Unable to load %s: %s" % (json_path, err)) raise Exception("Unable to load %s: %s" % (json_path, err))

View file

@ -34,7 +34,7 @@ def importBundle(bundle):
from Crypt.CryptBitcoin import isValidAddress from Crypt.CryptBitcoin import isValidAddress
import json import json
sites_json_path = f"{config.data_dir}/sites.json" sites_json_path = config.private_dir / 'sites.json'
try: try:
with open(sites_json_path) as f: with open(sites_json_path) as f:
sites = json.load(f) sites = json.load(f)
@ -68,16 +68,17 @@ def importBundle(bundle):
def init_dirs(): def init_dirs():
data_dir = Path(config.data_dir) data_dir = Path(config.data_dir)
private_dir = Path(config.private_dir)
need_bootstrap = (config.bootstrap need_bootstrap = (config.bootstrap
and not config.offline and not config.offline
and (not data_dir.is_dir() or not (data_dir / 'sites.json').is_file())) and (not data_dir.is_dir() or not (private_dir / 'sites.json').is_file()))
old_users_json = data_dir / 'users.json' # old_users_json = data_dir / 'users.json'
if old_users_json.is_file(): # if old_users_json.is_file():
print('Migrating existing users.json file to private/') # print('Migrating existing users.json file to private/')
old_sites_json = data_dir / 'sites.json' # old_sites_json = data_dir / 'sites.json'
if old_sites_json.is_file(): # if old_sites_json.is_file():
print('Migrating existing sites.json file to private/') # print('Migrating existing sites.json file to private/')
if not data_dir.is_dir(): if not data_dir.is_dir():
data_dir.mkdir(parents=True, exist_ok=True) data_dir.mkdir(parents=True, exist_ok=True)
@ -97,11 +98,11 @@ def init_dirs():
startupError(f"Cannot load boostrap bundle (response status: {response.status_code})") startupError(f"Cannot load boostrap bundle (response status: {response.status_code})")
importBundle(BytesIO(response.content)) importBundle(BytesIO(response.content))
sites_json = f"{data_dir}/sites.json" sites_json = private_dir / 'sites.json'
if not os.path.isfile(sites_json): if not os.path.isfile(sites_json):
with open(sites_json, "w") as f: with open(sites_json, "w") as f:
f.write("{}") f.write("{}")
users_json = f"{private_dir_dir}/users.json" users_json = private_dir / 'users.json'
if not os.path.isfile(users_json): if not os.path.isfile(users_json):
with open(users_json, "w") as f: with open(users_json, "w") as f:
f.write("{}") f.write("{}")
@ -117,7 +118,6 @@ def init():
config.initConsoleLogger() config.initConsoleLogger()
try: try:
print(config.start_dir)
init_dirs() init_dirs()
except: except:
import traceback as tb import traceback as tb
@ -129,7 +129,7 @@ def init():
if config.action == "main": if config.action == "main":
from util import helper from util import helper
try: try:
lock = helper.openLocked(f"{config.data_dir}/lock.pid", "w") lock = helper.openLocked(config.data_dir / 'lock.pid', "w")
lock.write(f"{os.getpid()}") lock.write(f"{os.getpid()}")
except BlockingIOError as err: except BlockingIOError as err:
startupError(f"Can't open lock file, your 0net client is probably already running, exiting... ({err})") startupError(f"Can't open lock file, your 0net client is probably already running, exiting... ({err})")

View file

@ -14,3 +14,9 @@ else:
return s.removeprefix(prefix) return s.removeprefix(prefix)
def removesuffix(s, suffix, /): def removesuffix(s, suffix, /):
return s.removesuffix(suffix) return s.removesuffix(suffix)
import argparse
if not hasattr(argparse, 'BooleanOptionalAction'):
from .argparseCompat import BooleanOptionalAction
argparse.BooleanOptionalAction = BooleanOptionalAction

View file

@ -16,17 +16,17 @@ from Config import config
def atomicWrite(dest, content, mode="wb"): def atomicWrite(dest, content, mode="wb"):
try: try:
with open(dest + "-tmpnew", mode) as f: with open(f'{dest}-tmpnew', mode) as f:
f.write(content) f.write(content)
f.flush() f.flush()
os.fsync(f.fileno()) os.fsync(f.fileno())
if os.path.isfile(dest + "-tmpold"): # Previous incomplete write if os.path.isfile(f'{dest}-tmpold'): # Previous incomplete write
os.rename(dest + "-tmpold", dest + "-tmpold-%s" % time.time()) os.rename(f'{dest}-tmpold', f'{dest}-tmpold-{time.time()}')
if os.path.isfile(dest): # Rename old file to -tmpold if os.path.isfile(dest): # Rename old file to -tmpold
os.rename(dest, dest + "-tmpold") os.rename(dest, f'{dest}-tmpold')
os.rename(dest + "-tmpnew", dest) os.rename(f'{dest}-tmpnew', dest)
if os.path.isfile(dest + "-tmpold"): if os.path.isfile(f'{dest}-tmpold'):
os.unlink(dest + "-tmpold") # Remove old file os.unlink(f'{dest}-tmpold') # Remove old file
return True return True
except Exception as err: except Exception as err:
from Debug import Debug from Debug import Debug
@ -34,8 +34,8 @@ def atomicWrite(dest, content, mode="wb"):
"File %s write failed: %s, (%s) reverting..." % "File %s write failed: %s, (%s) reverting..." %
(dest, Debug.formatException(err), Debug.formatStack()) (dest, Debug.formatException(err), Debug.formatStack())
) )
if os.path.isfile(dest + "-tmpold") and not os.path.isfile(dest): if os.path.isfile(f'{dest}-tmpold') and not os.path.isfile(dest):
os.rename(dest + "-tmpold", dest) os.rename(f'{dest}-tmpold', dest)
return False return False
@ -85,7 +85,7 @@ def openLocked(path, mode="wb"):
def getFreeSpace(): def getFreeSpace():
free_space = -1 free_space = -1
if "statvfs" in dir(os): # Unix if "statvfs" in dir(os): # Unix
statvfs = os.statvfs(config.data_dir.encode("utf8")) statvfs = os.statvfs(str(config.data_dir).encode("utf8"))
free_space = statvfs.f_frsize * statvfs.f_bavail free_space = statvfs.f_frsize * statvfs.f_bavail
else: # Windows else: # Windows
try: try:
@ -111,7 +111,7 @@ def shellquote(*args):
if len(args) == 1: if len(args) == 1:
return '"%s"' % args[0].replace('"', "") return '"%s"' % args[0].replace('"', "")
else: else:
return tuple(['"%s"' % arg.replace('"', "") for arg in args]) return tuple(['"%s"' % str(arg).replace('"', "") for arg in args])
def packPeers(peers): def packPeers(peers):

View file

@ -34,7 +34,7 @@ def launch():
except Exception as log_err: except Exception as log_err:
print("Failed to log error:", log_err) print("Failed to log error:", log_err)
traceback.print_exc() traceback.print_exc()
error_log_path = config.log_dir + "/error.log" error_log_path = config.log_dir / "error.log"
traceback.print_exc(file=open(error_log_path, "w")) traceback.print_exc(file=open(error_log_path, "w"))
print("---") print("---")
print("Please report it: https://github.com/zeronet-conservancy/zeronet-conservancy/issues/new?template=bug-report.md") print("Please report it: https://github.com/zeronet-conservancy/zeronet-conservancy/issues/new?template=bug-report.md")