forked from michael.heier/citadel-core
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import re
|
|
|
|
# Helper functions
|
|
|
|
|
|
# Return a list of env vars in a string, supports both $NAME and ${NAME} format for the env var
|
|
# This can potentially be used to get around permissions, so this check is critical for security
|
|
# Please report any security vulnerabilities you find in this check to aaron.dewes@protonmail.com
|
|
def getEnvVars(string: str):
|
|
string = str(string)
|
|
envVars = re.findall(r'\$\{.*?\}', string)
|
|
newEnvVars = re.findall(r"\$(?!{)([A-z1-9]+)", string)
|
|
return [envVar[2:-1] for envVar in envVars] + newEnvVars
|
|
|
|
|
|
# Check if an array only contains values which are also in another array
|
|
def checkArrayContainsAllElements(array: list, otherArray: list):
|
|
for element in array:
|
|
if element not in otherArray:
|
|
return False
|
|
return True
|
|
|
|
# Parse a dotenv file
|
|
# Values can either be KEY=VALUE or KEY="VALUE" or KEY='VALUE'
|
|
# Returns all env vars as a dict
|
|
def parse_dotenv(file_path):
|
|
envVars: dict = {}
|
|
with open(file_path, 'r') as file:
|
|
for line in file:
|
|
line = line.strip()
|
|
if line.startswith('#') or len(line) == 0:
|
|
continue
|
|
if '=' in line:
|
|
key, value = line.split('=', 1)
|
|
value = value.strip('"').strip("'")
|
|
envVars[key] = value
|
|
else:
|
|
print("Error: Invalid line in {}: {}".format(file_path, line))
|
|
print("Line should be in the format KEY=VALUE or KEY=\"VALUE\" or KEY='VALUE'")
|
|
exit(1)
|
|
return envVars
|
|
|
|
# Combines an object and a class
|
|
# If the key exists in both objects, the value of the second object is used
|
|
# If the key does not exist in the first object, the value from the second object is used
|
|
# If a key contains a list, the second object's list is appended to the first object's list
|
|
# If a key contains another object, these objects are combined
|
|
def combineObjectAndClass(theClass, obj: dict):
|
|
for key, value in obj.items():
|
|
if key in theClass.__dict__:
|
|
if isinstance(value, list):
|
|
if isinstance(theClass.__dict__[key], list):
|
|
theClass.__dict__[key].extend(value)
|
|
else:
|
|
theClass.__dict__[key] = [theClass.__dict__[key]] + value
|
|
elif isinstance(value, dict):
|
|
if isinstance(theClass.__dict__[key], dict):
|
|
theClass.__dict__[key].update(value)
|
|
else:
|
|
theClass.__dict__[key] = {theClass.__dict__[key]: value}
|
|
else:
|
|
theClass.__dict__[key] = value
|
|
else:
|
|
theClass.__dict__[key] = value
|
|
|
|
def is_builtin_type(obj):
|
|
return isinstance(obj, (int, float, str, bool, list, dict))
|
|
|
|
# Convert a class to a dict
|
|
# Also strip any class member which is null or empty
|
|
def classToDict(theClass):
|
|
obj: dict = {}
|
|
for key, value in theClass.__dict__.items():
|
|
if type(value).__name__ == "NoneType" or (isinstance(value, list) and len(value) == 0):
|
|
continue
|
|
if isinstance(value, list):
|
|
newList = []
|
|
for element in value:
|
|
if is_builtin_type(element):
|
|
newList.append(element)
|
|
else:
|
|
if type(element).__name__ != "NoneType":
|
|
newList.append(classToDict(element))
|
|
obj[key] = newList
|
|
elif isinstance(value, dict):
|
|
newDict = {}
|
|
for subkey, subvalue in value.items():
|
|
if is_builtin_type(subvalue):
|
|
newDict[subkey] = subvalue
|
|
else:
|
|
newDict[subkey] = classToDict(subvalue)
|
|
obj[key] = newDict
|
|
elif is_builtin_type(value):
|
|
obj[key] = value
|
|
elif type(value).__name__ != "NoneType":
|
|
obj[key] = classToDict(value)
|
|
return obj
|
|
|