225 lines
5.4 KiB
Lua
225 lines
5.4 KiB
Lua
--
|
|
-- Minetest password manager
|
|
--
|
|
-- © Copyright 2019 by luk3yx.
|
|
--
|
|
-- This program is free software; you can redistribute it and/or modify
|
|
-- it under the terms of the GNU Lesser General Public License as published by
|
|
-- the Free Software Foundation; either version 2.1 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This program is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU Lesser General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU Lesser General Public License
|
|
-- along with this program; if not, write to the Free Software Foundation, Inc.,
|
|
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
--
|
|
|
|
pwmgr = {}
|
|
|
|
local db_path = core.get_modpath() .. DIR_DELIM .. ".." .. DIR_DELIM ..
|
|
".saved_passwords"
|
|
local db = {}
|
|
|
|
-- Convert a gamedata table to a key
|
|
local function gamedata_to_key(data)
|
|
data = data or gamedata
|
|
return "P" .. tonumber(data.port) .. " "
|
|
.. data.playername:gsub(" ", "_") .. " "
|
|
.. data.address
|
|
end
|
|
|
|
-- Convert a table key back to a gamedata table
|
|
local function key_to_gamedata(key)
|
|
local port, name, address = key:match("^P([0-9]+) ([^ ]+) (.*)$")
|
|
port = tonumber(port)
|
|
if not port or port ~= port or not name or not address then return end
|
|
return {
|
|
address = address,
|
|
port = port,
|
|
playername = name
|
|
}
|
|
end
|
|
|
|
-- Load the database
|
|
local function load_db()
|
|
local f = io.open(db_path, "rb")
|
|
if not f then return end
|
|
|
|
local data = f:read()
|
|
f:close()
|
|
|
|
data = core.deserialize(data)
|
|
if type(data) == "table" then
|
|
db = data
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Save the database
|
|
local function save_db()
|
|
local data = core.serialize(db)
|
|
if not data then return end
|
|
|
|
if core.safe_write_file then
|
|
return core.safe_write_file(db_path, data)
|
|
end
|
|
|
|
local f = io.open(db_path, "wb")
|
|
if not f then return end
|
|
f:write(data)
|
|
f:close()
|
|
return true
|
|
end
|
|
|
|
-- Get and set passwords
|
|
function pwmgr.get_password(data)
|
|
load_db()
|
|
if type(data) ~= "string" then data = gamedata_to_key(data) end
|
|
local pw = db[data]
|
|
if type(pw) == "string" and pw ~= "" then
|
|
return pw
|
|
end
|
|
end
|
|
|
|
function pwmgr.set_password(data, new_pw)
|
|
if new_pw == nil then
|
|
if type(data) == "table" then
|
|
new_pw = data.password
|
|
else
|
|
new_pw = data
|
|
end
|
|
new_pw = data or gamedata.password
|
|
data = false
|
|
end
|
|
|
|
load_db()
|
|
db[gamedata_to_key(data)] = new_pw or nil
|
|
save_db()
|
|
end
|
|
|
|
-- List passwords
|
|
function pwmgr.list_passwords()
|
|
load_db()
|
|
local res = {}
|
|
for k, pw in pairs(db) do
|
|
local data = key_to_gamedata(k)
|
|
if data and pw then
|
|
data.raw = k
|
|
data.password = pw
|
|
res[#res + 1] = data
|
|
end
|
|
end
|
|
return res
|
|
end
|
|
|
|
-- Get the disable confirmation dialog
|
|
local function get_disable_confirm_formspec()
|
|
return "size[10,2.75,true]" ..
|
|
"label[0.5,0.5;Disable the password manager?\n"
|
|
.. "You can re-enable it at any time in \"All Settings\".]" ..
|
|
"button[0.5,2;2.5,0.5;pwd_confirm_disable;" .. fgettext("Disable")
|
|
.. "]" ..
|
|
"button[7,2;2.5,0.5;pwd_back;" .. fgettext("Cancel") .. "]"
|
|
end
|
|
|
|
local function get_disable_confirm_buttonhandler(this, fields)
|
|
if fields.pwd_confirm_disable then
|
|
-- Don't load pwmgr later
|
|
core.settings:set_bool("enable_pwmgr", false)
|
|
core.settings:write()
|
|
|
|
-- Delete the "pwmgr" global variable
|
|
pwmgr = nil
|
|
|
|
-- Continue joining
|
|
core.start()
|
|
return true
|
|
end
|
|
|
|
if fields.pwd_back then
|
|
this:delete()
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Get the "Should this password be saved?" formspec
|
|
local function get_prejoin_formspec()
|
|
local msg = "Should this password be saved (in plaintext)?\n"
|
|
.. "\nAddress: " .. gamedata.address
|
|
.. "\nPort: " .. gamedata.port
|
|
.. "\nName: " .. gamedata.playername
|
|
.. "\nPassword: " .. string.rep("*", #gamedata.password)
|
|
|
|
return "size[10,5,true]" ..
|
|
"label[0.5,0.5;Password manager]" ..
|
|
"label[0.5,1;" .. core.formspec_escape(msg) .. "]" ..
|
|
"button[1,4;2,1;pwd_cancel;< Cancel]" ..
|
|
"button[3,4;2,1;pwd_disable;Never]" ..
|
|
"button[5,4;2,1;pwd_no_save;Don't save]" ..
|
|
"button[7,4;2,1;pwd_save;Save >]"
|
|
end
|
|
|
|
local function get_prejoin_buttonhandler(this, fields)
|
|
if fields.pwd_cancel then
|
|
this:delete()
|
|
return true
|
|
end
|
|
|
|
if fields.pwd_disable then
|
|
local dlg = dialog_create("pwmgr_confirm_disable",
|
|
get_disable_confirm_formspec,
|
|
get_disable_confirm_buttonhandler,
|
|
nil)
|
|
dlg:set_parent(this)
|
|
this:hide()
|
|
dlg:show()
|
|
return true
|
|
end
|
|
|
|
if fields.pwd_save then
|
|
pwmgr.set_password()
|
|
end
|
|
|
|
if fields.pwd_save or fields.pwd_no_save then
|
|
core.start()
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function pwmgr.prejoin(this)
|
|
local port = tonumber(gamedata.port)
|
|
if not port or port ~= port or gamedata.playername:find(" ") or
|
|
gamedata.playername == "" or gamedata.address == "" or
|
|
not gamedata.playername or not gamedata.address then
|
|
return
|
|
end
|
|
|
|
if not gamedata.password or gamedata.password == "" then
|
|
gamedata.password = pwmgr.get_password()
|
|
if not gamedata.password or gamedata.password == "" then
|
|
return true
|
|
end
|
|
elseif this then
|
|
local dlg = dialog_create("pwmgr_save_password",
|
|
get_prejoin_formspec,
|
|
get_prejoin_buttonhandler,
|
|
nil)
|
|
dlg:set_parent(this)
|
|
this:hide()
|
|
dlg:show()
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Wrapper
|
|
function pwmgr.display_manager(...)
|
|
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "dlg_pwmgr_manage.lua")
|
|
return pwmgr.display_manager(...)
|
|
end
|