#################################################################################
# This file is part of Mana Launcher.
# Copyright (C) 2021 Jesusalva <jesusalva@tmw2.org>
#
# Distributed under the MIT license, except for Steam parts.
#################################################################################
# This is for Vault accounts
default uedit = {"mail": "",
"pasd": "",
"totp": "",
"rbmx": ifte(persistent.email is None, False, True),
"rbpd": ifte(persistent.passd is None, False, True)}
init python:
def ueditor_input(key, temp="", tp="str"):
#temp=renpy.input("Please insert new value for this variable.\nCurrent value: [variable]")
global uedit
variable=""
# Convert input if needed
# tp int → convert to int
# tp key → Replace the key
# tp bool → Toogles the value
if tp == "int":
try:
variable=int(temp)
except:
renpy.notify("Invalid numeric input")
variable=0 # Sorry, but gets bad otherwise
else:
try:
variable=str(temp)
except:
renpy.notify("Invalid string input")
# Save input
uedit[key] = variable
return
class UEditorInputValue(InputValue):
# inpu: str or int
def __init__(self, key, placeholder="", inpu="str"):
global uedit
self.key = key
self.variable = uedit[key] or str(placeholder)
self.inpu = inpu
self.default=True
self.editable=True
# Conditional hacks
if (persistent.email is not None and self.variable == "a@a.com"):
self.variable = str(persistent.email)
uedit[key] = str(self.variable)
if (persistent.passd is not None and self.variable == "***"):
if LEGACY:
self.variable = str(bytearray((x ^ int(persistent.rhash/mp.sub) for x in bytearray(persistent.passd, 'utf-8'))))
else:
self.variable = bytearray((x ^ int(persistent.rhash/mp.sub) for x in persistent.passd)).decode('utf-8')
uedit[key] = str(self.variable)
def get_text(self):
try:
return str(self.variable)
except:
traceback.print_exc()
return ""
#return globals()[self.variable]
def set_text(self, s):
#globals()[self.variable] = s
ueditor_input(self.key, s, self.inpu)
def enter(self):
renpy.restart_interaction()
#renpy.run(self.Disable())
#raise renpy.IgnoreEvent()
screen register_vault():
## Ensure other screens do not get input while this screen is displayed.
modal True
zorder 200
style_prefix "confirm"
add "gui/overlay/confirm.png"
frame:
vbox:
xalign .5
yalign .5
spacing 30
label _("Welcome to the {b}Mana Launcher{/b}!\n\nIf you do not have an account, one will be created for you."):
style "confirm_prompt"
xalign 0.5
null height 24
hbox:
spacing 10
label _("Email: ")
$ input=Input(
value=UEditorInputValue("mail", "a@a.com", "str"),
copypaste=True,
allow="qwertyuiopasdfghjklçzxcvbnm QWERTYUIOPASDFGHJKLÇZXCVBNM1234567890-+=!(),.:;@_",
length=52)
button:
#key_events True
action input.enable
add input
hbox:
spacing 10
label _("Password: ")
# For some reason, we don't accept all ascii special codes
# WARNING: Non-ascii will break the hashing.
$ input=Input(
value=UEditorInputValue("pasd", "***", "str"),
copypaste=True,
allow="qwertyuiopasdfghjklçzxcvbnm QWERTYUIOPASDFGHJKLÇZXCVBNM1234567890-+=!(),.:;@*^_",
length=52,
mask=ifte(persistent.maskpass, "*", None))
button:
#key_events True
action input.enable
add input
showif persistent.totp is None:
hbox:
spacing 10
label _("TOTP: ")
$ input=Input(
value=UEditorInputValue("totp", "000000", "str"),
copypaste=True,
allow="1234567890",
length=6)
button:
#key_events True
action input.enable
add input
# FIXME: Checkboxes
# * Remember me
# * Disable 2FA/TOTP
null height 24
button:
#xmaximum 32
#ymaximum 32
hbox:
spacing 10
if (uedit["rbmx"]):
add "gui/button/check_1.png"
else:
add "gui/button/check_0.png"
null width 5
text _("Remember email") size 18
action [SetDict(uedit, "rbmx", not uedit["rbmx"]), Function(renpy.restart_interaction)]
button:
#xmaximum 32
#ymaximum 32
hbox:
spacing 10
if (uedit["rbpd"]):
add "gui/button/check_1.png"
else:
add "gui/button/check_0.png"
null width 5
text _("Remember password (unsafe!)") size 18
action [SetDict(uedit, "rbpd", not uedit["rbpd"]), Function(renpy.restart_interaction)]
hbox:
xalign 0.5
spacing 100
textbutton _("OK"):
action ifte(len(uedit["pasd"]) >= 4, Return(), None)
keysym ['K_KP_ENTER', 'K_RETURN']
label register_vault:
call screen register_vault()
$ status_update(pc=92)
if persistent.totp is not None:
python:
key = base64.b32decode(persistent.totp.encode('utf-8'), True)
msg = struct.pack(">Q", int(time.time()/30))
h = hmac.new(key, msg, hashlib.sha1).digest()
if LEGACY:
o = ord(h[19]) & 15
else:
o = (h[19] & 15)
_return = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
_return = "%06d" % _return
print("TOTP: %s" % _return)
uedit["totp"] = _return
del key, msg, h, o
$ email = uedit["mail"]
$ password = uedit["pasd"]
$ code2FA = uedit["totp"]
$ status_update(pc=95)
$ data = {"mail": email,
"pass": password,
"totp": code2FA[:6]
}
$ r = vault.post(VAULT_HOST+"/user_auth", json=data)
# We save the variables on the meanwhile
if uedit["rbmx"]:
$ persistent.email = str(email)
if uedit["rbpd"]:
$ hsh = renpy.random.randint(11, 127)
$ mp.sub = renpy.random.randint(127, 16777215)
$ persistent.rhash = int(hsh)*mp.sub
if LEGACY:
$ persistent.passd = str(bytearray(x ^ int(hsh) for x in bytearray(password, 'utf-8')))
else:
$ persistent.passd = bytearray(ord(x) ^ int(hsh) for x in password)
$ mp.save()
# Wait for Vault to confirm.
if (r.status_code != 200):
if (r.status_code not in [401, 403]):
call screen notice(_("Vault returned error %d\n\nPlease try again later." % r.status_code))
else:
call screen notice(_("Vault returned error %d (incorrect login/password)\n\nPlease try again later." % r.status_code))
return
# Check if we have success
python:
try:
status_update(pc=98)
stdout("Vault result: (%d) %s" % (r.status_code, ifte(config.developer, r.text, "OK")))
auth2 = r.json()
vaultId = auth2["vaultId"]
vaultToken = auth2["token"]
vaultOTP = auth2["otp"]
except:
traceback.print_exc()
stdout("Error - Vault result is bad.")
# Maybe we got a message informing this is a new account?
try:
if (auth2["status"] == 1):
status_update("Creating account and logging in...")
renpy.notify("Account created! Check email.")
time.sleep(1.0)
# Attempt to save the TOTP credential (DISCOURAGED)
if persistent.autologin and "totp" in auth2.keys():
persistent.totp = auth2["totp"].split("secret=")[1].split("&")[0]
except:
pass
############
## Cleanup
$ del data
$ del code2FA
$ del email
$ del method
if vaultId:
$ status_update(_("Success!"), 100)
else:
$ status_update(_("{color=#F00}Failure!{/color}"), pc=100)
return