import parsetoml, strutils, sequtils, random, base64 import ./[types, utils] proc findKey(profile: Profile, path: string): TomlValueRef = let keys = path.split(".") let target = keys[keys.high] var current = profile for i in 0 ..< keys.high: let temp = current.getOrDefault(keys[i]) if temp == nil: return nil current = temp return current.getOrDefault(target) # Takes a specific "."-separated path as input and returns a default value if the key does not exits # Example: cq.profile.getString("http-get.agent.heartbeat.prefix", "not found") returns the string value of the # prefix key, or "not found" if the target key or any sub-tables don't exist # '#' characters represent wildcard characters and are replaced with a random alphanumerical character proc randomChar(): char = let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" return alphabet[rand(alphabet.len - 1)] proc randomNumber(): char = let numbers = "0123456789" return numbers[rand(numbers.len - 1)] proc getRandom*(values: seq[TomlValueRef]): TomlValueRef = if values.len == 0: return nil return values[rand(values.len - 1)] proc getStringValue*(key: TomlValueRef, default: string = ""): string = # In some cases, the profile can define multiple values for a key, e.g. for HTTP headers # A random entry is selected from these specifications var value: string = "" if key.kind == TomlValueKind.String: value = key.getStr(default) elif key.kind == TomlValueKind.Array: value = key.getElems().getRandom().getStr(default) # Replace '#' with a random alphanumerical character and return the resulting string return value.mapIt(if it == '#': randomChar() elif it == '$': randomNumber() else: it).join("") proc getString*(profile: Profile, path: string, default: string = ""): string = let key = profile.findKey(path) if key == nil: return default return key.getStringValue(default) proc getBool*(profile: Profile, path: string, default: bool = false): bool = let key = profile.findKey(path) if key == nil: return default return key.getBool(default) proc getInt*(profile: Profile, path: string, default = 0): int = let key = profile.findKey(path) if key == nil: return default return key.getInt(default) proc getTable*(profile: Profile, path: string): TomlTableRef = let key = profile.findKey(path) if key == nil: return new TomlTableRef return key.getTable() proc getArray*(profile: Profile, path: string): seq[TomlValueRef] = let key = profile.findKey(path) if key == nil: return @[] return key.getElems() proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]): string = # 1. Encoding var dataString: string case profile.getString(path & protect(".encoding.type"), default = protect("none")) of protect("base64"): dataString = encode(data, safe = profile.getBool(path & protect(".encoding.url-safe"))).replace("=", "") of protect("hex"): dataString = Bytes.toString(data).toHex().toLowerAscii() of protect("rot"): dataString = Bytes.toString(encodeRot(data, profile.getInt(path & ".encoding.key", default = 13))) of protect("xor"): dataString = Bytes.toString(xorBytes(data, profile.getInt(path & ".encoding.key", default = 1))) of protect("none"): dataString = Bytes.toString(data) # 2. Add prefix & suffix let prefix = profile.getString(path & protect(".prefix")) let suffix = profile.getString(path & protect(".suffix")) return prefix & dataString & suffix proc reverseDataTransformation*(profile: Profile, path: string, data: string): seq[byte] = # 1. Remove prefix & suffix let prefix = profile.getString(path & protect(".prefix")) suffix = profile.getString(path & protect(".suffix")) dataString = data[len(prefix) ..^ len(suffix) + 1] # 2. Decoding case profile.getString(path & protect(".encoding.type"), default = protect("none")): of protect("base64"): result = string.toBytes(decode(dataString)) of protect("hex"): result = string.toBytes(parseHexStr(dataString)) of protect("rot"): result = decodeRot(string.toBytes(dataString), profile.getInt(path & ".encoding.key", default = 13)) of protect("xor"): result = xorBytes(string.toBytes(dataString), profile.getInt(path & ".encoding.key", default = 1)) of protect("none"): result = string.toBytes(dataString)