Merge pull request #5 from zxq9/dev
Adjust dev scripts. Make interaction better. Say, y'all, this here's sharp!
This commit is contained in:
commit
5c0f5e0e89
10
test_prep
Executable file
10
test_prep
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
pushd $(dirname $BASH_SOURCE) > /dev/null
|
||||
ZX_DEV_ROOT=$PWD
|
||||
popd > /dev/null
|
||||
ZOMP_DIR="$ZX_DEV_ROOT/tester"
|
||||
rm -rf "$ZOMP_DIR"
|
||||
cp -r "$ZX_DEV_ROOT/zomp" "$ZOMP_DIR"
|
||||
|
||||
echo "Done. Make sure to export \"export ZOMP_DIR=$ZOMP_DIR\" before running zx"
|
||||
@ -27,7 +27,7 @@
|
||||
-export_type([serial/0, package_id/0, package/0, realm/0, name/0, version/0,
|
||||
identifier/0,
|
||||
host/0,
|
||||
key_id/0, key_name/0, key_data/0,
|
||||
key_id/0, key_name/0,
|
||||
user_id/0, user_name/0, contact_info/0, user_data/0,
|
||||
lower0_9/0, label/0,
|
||||
package_meta/0,
|
||||
@ -50,16 +50,13 @@
|
||||
-type host() :: {string() | inet:ip_address(), inet:port_number()}.
|
||||
-type key_id() :: {realm(), key_name()}.
|
||||
-type key_name() :: lower0_9().
|
||||
-type key_data() :: {ID :: key_id(),
|
||||
Public :: binary() | <<>>,
|
||||
Private :: binary() | <<>>}.
|
||||
-type user_id() :: {realm(), user_name()}.
|
||||
-type user_name() :: label().
|
||||
-type contact_info() :: {Type :: string(), Data :: string()}.
|
||||
-type user_data() :: {ID :: user_id(),
|
||||
RealName :: string(),
|
||||
Contact :: contact_info(),
|
||||
KeyData :: [key_data()]}.
|
||||
Contact :: [contact_info()],
|
||||
Keys :: [key_name()]}.
|
||||
-type lower0_9() :: [$a..$z | $0..$9 | $_].
|
||||
-type label() :: [$a..$z | $0..$9 | $_ | $- | $.].
|
||||
-type package_meta() :: #{package_id := package_id(),
|
||||
@ -136,8 +133,8 @@ do(["package", TargetDir]) ->
|
||||
end;
|
||||
do(["dialyze"]) ->
|
||||
done(zx_local:dialyze());
|
||||
do(["create", "user", Realm, Name]) ->
|
||||
done(zx_local:create_user(Realm, Name));
|
||||
do(["create", "user"]) ->
|
||||
done(zx_local:create_user());
|
||||
do(["create", "keypair"]) ->
|
||||
done(zx_local:grow_a_pair());
|
||||
do(["drop", "key", Realm, KeyName]) ->
|
||||
@ -646,7 +643,7 @@ usage() ->
|
||||
" zx add key Realm KeyName~n"
|
||||
" zx get key Realm KeyName~n"
|
||||
" zx rem key Realm KeyName~n"
|
||||
" zx create user Realm~n"
|
||||
" zx create user~n"
|
||||
" zx create userfiles Realm UserName~n"
|
||||
" zx create keypair Realm~n"
|
||||
" zx export user UserID~n"
|
||||
|
||||
@ -17,11 +17,26 @@
|
||||
takeover/1, abdicate/1,
|
||||
create_plt/0, dialyze/0,
|
||||
grow_a_pair/0, drop_key/1,
|
||||
create_user/2, create_realm/0, create_realmfile/2]).
|
||||
create_user/0, create_realm/0, create_realmfile/2]).
|
||||
|
||||
-include("zx_logger.hrl").
|
||||
|
||||
|
||||
-record(user_data,
|
||||
{realm = none :: none | zx:realm(),
|
||||
username = none :: none | zx:user_name(),
|
||||
realname = none :: none | string(),
|
||||
contact_info = none :: none | [zx:contact_info()],
|
||||
keys = none :: none | [zx:key_name()]}).
|
||||
|
||||
-record(realm_init,
|
||||
{realm = none :: none | zx:realm(),
|
||||
addr = none :: none | string() | inet:ip_address(),
|
||||
port = none :: none | inet:port_number(),
|
||||
sysop = none :: none | #user_data{},
|
||||
url = none :: none | string()}).
|
||||
|
||||
|
||||
|
||||
%%% Functions
|
||||
|
||||
@ -57,7 +72,7 @@ initialize2(Type, RawPackageString) ->
|
||||
initialize3(Type, PackageID) ->
|
||||
case package_exists(PackageID) of
|
||||
false ->
|
||||
Prefix = solicit_prefix(),
|
||||
Prefix = ask_prefix(),
|
||||
initialize(Type, PackageID, Prefix);
|
||||
true ->
|
||||
Message = "Package already exists. Try another.",
|
||||
@ -139,11 +154,11 @@ package_exists(PackageID) ->
|
||||
false.
|
||||
|
||||
|
||||
-spec solicit_prefix() -> string().
|
||||
-spec ask_prefix() -> string().
|
||||
%% @private
|
||||
%% Get a valid module prefix to use as a namespace for new modules.
|
||||
|
||||
solicit_prefix() ->
|
||||
ask_prefix() ->
|
||||
Instructions =
|
||||
"~nPICKING A PREFIX~n"
|
||||
"Most Erlang applications have a prefix on the front of their modules. This "
|
||||
@ -169,7 +184,7 @@ solicit_prefix() ->
|
||||
false ->
|
||||
Message = "The string \"~tp\" is problematic. Try \"[a-z]*_\".~n",
|
||||
ok = io:format(Message, [Prefix]),
|
||||
solicit_prefix()
|
||||
ask_prefix()
|
||||
end
|
||||
end.
|
||||
|
||||
@ -805,18 +820,6 @@ drop_key({Realm, KeyName}) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec create_user(zx:realm(), zx:user_name()) -> ok.
|
||||
%% @private
|
||||
%% Validate the realm and username provided, prompt the user to either select a keypair
|
||||
%% to use or generate a new one, and bundle a .zuser file for conveyance of the user
|
||||
%% data and his relevant keys (for import into an existing zomp server via `add'
|
||||
%% command like "add packager", "add maintainer" and "add sysop".
|
||||
|
||||
create_user(Realm, Username) ->
|
||||
Message = "Would be generating a user file for {~160tp, ~160to}.",
|
||||
log(info, Message, [Realm, Username]).
|
||||
|
||||
|
||||
-spec create_realm() -> ok.
|
||||
%% @private
|
||||
%% Prompt the user to input the information necessary to create a new zomp realm,
|
||||
@ -825,211 +828,58 @@ create_user(Realm, Username) ->
|
||||
|
||||
create_realm() ->
|
||||
ok = log(info, "WOOHOO! Making a new realm!"),
|
||||
create_realm(#realm_init{}).
|
||||
|
||||
|
||||
create_realm(R = #realm_init{realm = none}) ->
|
||||
create_realm(R#realm_init{realm = ask_realm()});
|
||||
create_realm(R = #realm_init{addr = none}) ->
|
||||
create_realm(R#realm_init{addr = ask_addr()});
|
||||
create_realm(R = #realm_init{port = none}) ->
|
||||
create_realm(R#realm_init{port = ask_port()});
|
||||
create_realm(R = #realm_init{url = none}) ->
|
||||
create_realm(R#realm_init{url = ask_url()});
|
||||
create_realm(R = #realm_init{realm = Realm, sysop = none}) ->
|
||||
create_realm(R#realm_init{sysop = create_sysop(#user_data{realm = Realm})});
|
||||
create_realm(R = #realm_init{realm = Realm, addr = Addr, port = Port, url = URL}) ->
|
||||
Instructions =
|
||||
"~nNAMING~n"
|
||||
"Enter a name for your new realm.~n"
|
||||
"Names can contain only lower-case letters, numbers and the underscore.~n"
|
||||
"Names must begin with a lower-case letter.~n",
|
||||
ok = io:format(Instructions),
|
||||
Realm = zx_tty:get_input(),
|
||||
case zx_lib:valid_lower0_9(Realm) of
|
||||
true ->
|
||||
case realm_exists(Realm) of
|
||||
false ->
|
||||
create_realm(Realm);
|
||||
true ->
|
||||
ok = io:format("That realm already exists. Be more original.~n"),
|
||||
create_realm()
|
||||
end;
|
||||
false ->
|
||||
ok = io:format("Bad realm name \"~ts\". Try again.~n", [Realm]),
|
||||
create_realm()
|
||||
end.
|
||||
|
||||
|
||||
-spec create_realm(Realm) -> ok
|
||||
when Realm :: zx:realm().
|
||||
|
||||
create_realm(Realm) ->
|
||||
Address = prompt_address(),
|
||||
create_realm(Realm, Address).
|
||||
|
||||
|
||||
-spec prompt_address() -> Result
|
||||
when Result :: inet:hostname() | inet:ip_address().
|
||||
|
||||
prompt_address() ->
|
||||
Message =
|
||||
"~nHOST ADDRESS~n"
|
||||
"Enter a static, valid hostname or IPv4 or IPv6 address at which this host "
|
||||
"can be reached from the public internet (or internal network if it will never "
|
||||
"need to be reached from the internet).~n"
|
||||
"DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n",
|
||||
ok = io:format(Message),
|
||||
"~nREALM DATA CONFIRMATION~n"
|
||||
"That's everything! Please confirm these settings:~n"
|
||||
"[1] Realm Name : ~ts ~n"
|
||||
"[2] Host Address: ~ts~n"
|
||||
"[3] Port Number : ~w~n"
|
||||
"[4] URL : ~ts~n"
|
||||
"Press a number to select something to change, or [ENTER] to continue.~n",
|
||||
ok = io:format(Instructions, [Realm, Addr, Port, URL]),
|
||||
case zx_tty:get_input() of
|
||||
"" ->
|
||||
ok = io:format("You need to enter an address.~n"),
|
||||
prompt_address();
|
||||
String ->
|
||||
parse_address(String)
|
||||
"1" -> create_realm(R#realm_init{realm = none});
|
||||
"2" -> create_realm(R#realm_init{addr = none});
|
||||
"3" -> create_realm(R#realm_init{port = none});
|
||||
"4" -> create_realm(R#realm_init{url = none});
|
||||
"" -> store_realm(R);
|
||||
_ ->
|
||||
ok = io:format("~nArglebargle, glop-glyf!?!~n~n"),
|
||||
create_realm(R)
|
||||
end.
|
||||
|
||||
|
||||
-spec parse_address(string()) -> inet:hostname() | inet:ip_address().
|
||||
|
||||
parse_address(String) ->
|
||||
case inet:parse_address(String) of
|
||||
{ok, Address} -> Address;
|
||||
{error, einval} -> String
|
||||
end.
|
||||
|
||||
|
||||
-spec create_realm(Realm, Address) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
Address :: inet:hostname() | inet:ip_address().
|
||||
|
||||
create_realm(Realm, Address) ->
|
||||
Message =
|
||||
"~nPUBLIC PORT NUMBER~n"
|
||||
"Enter the public (external) port number at which this service should be "
|
||||
"available. (This might be different from the local port number if you are "
|
||||
"forwarding ports or have a complex network layout.)~n",
|
||||
ok = io:format(Message),
|
||||
Port = prompt_port_number(11311),
|
||||
create_realm(Realm, Address, Port).
|
||||
|
||||
|
||||
-spec prompt_port_number(Current) -> Result
|
||||
when Current :: inet:port_number(),
|
||||
Result :: inet:port_number().
|
||||
|
||||
prompt_port_number(Current) ->
|
||||
Instructions =
|
||||
"A valid port is any number from 1 to 65535."
|
||||
"[Press enter to accept the current setting: ~tw]~n",
|
||||
ok = io:format(Instructions, [Current]),
|
||||
case zx_tty:get_input() of
|
||||
"" ->
|
||||
Current;
|
||||
S ->
|
||||
try
|
||||
case list_to_integer(S) of
|
||||
Port when 16#ffff >= Port, Port > 0 ->
|
||||
Port;
|
||||
Illegal ->
|
||||
Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again.~n",
|
||||
ok = io:format(Whoops, [Illegal]),
|
||||
prompt_port_number(Current)
|
||||
end
|
||||
catch error:badarg ->
|
||||
ok = io:format("~tp is not a port number. Try again...", [S]),
|
||||
prompt_port_number(Current)
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
-spec create_realm(Realm, Address, Port) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
Address :: inet:hostname() | inet:ip_address(),
|
||||
Port :: inet:port_number(),
|
||||
Port :: inet:port_number().
|
||||
|
||||
create_realm(Realm, Address, Port) ->
|
||||
Instructions =
|
||||
"~nSYSOP USERNAME~n"
|
||||
"Enter a username for the realm sysop.~n"
|
||||
"Names can contain only lower-case letters, numbers and the underscore.~n"
|
||||
"Names must begin with a lower-case letter.~n",
|
||||
ok = io:format(Instructions),
|
||||
UserName = zx_tty:get_input(),
|
||||
case zx_lib:valid_lower0_9(UserName) of
|
||||
true ->
|
||||
create_realm(Realm, Address, Port, UserName);
|
||||
false ->
|
||||
ok = io:format("Bad username ~tp. Try again.~n", [UserName]),
|
||||
create_realm(Realm, Address, Port)
|
||||
end.
|
||||
|
||||
|
||||
-spec create_realm(Realm, Address, Port, UserName) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
Address :: inet:hostname() | inet:ip_address(),
|
||||
Port :: inet:port_number(),
|
||||
UserName :: string().
|
||||
|
||||
create_realm(Realm, Address, Port, UserName) ->
|
||||
Instructions =
|
||||
"~nSYSOP EMAIL~n"
|
||||
"Enter an email address for the realm sysop.~n"
|
||||
"Valid email address rules apply though the checking done here is quite "
|
||||
"minimal. Check the address you enter carefully. The only people who will "
|
||||
"suffer from an invalid address are your users.~n",
|
||||
ok = io:format(Instructions),
|
||||
Email = zx_tty:get_input(),
|
||||
[User, Host] = string:lexemes(Email, "@"),
|
||||
case {zx_lib:valid_lower0_9(User), zx_lib:valid_label(Host)} of
|
||||
{true, true} ->
|
||||
create_realm(Realm, Address, Port, UserName, Email);
|
||||
{false, true} ->
|
||||
Message = "The user part of the email address seems invalid. Try again.~n",
|
||||
ok = io:format(Message),
|
||||
create_realm(Realm, Address, Port, UserName);
|
||||
{true, false} ->
|
||||
Message = "The host part of the email address seems invalid. Try again.~n",
|
||||
ok = io:format(Message),
|
||||
create_realm(Realm, Address, Port, UserName);
|
||||
{false, false} ->
|
||||
Message = "This email address seems like its totally bonkers. Try again.~n",
|
||||
ok = io:format(Message),
|
||||
create_realm(Realm, Address, Port, UserName)
|
||||
end.
|
||||
|
||||
|
||||
-spec create_realm(Realm, Address, Port, UserName, Email) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
Address :: inet:hostname() | inet:ip_address(),
|
||||
Port :: inet:port_number(),
|
||||
UserName :: string(),
|
||||
Email :: string().
|
||||
|
||||
create_realm(Realm, Address, Port, UserName, Email) ->
|
||||
Instructions =
|
||||
"~nSYSOP REAL NAME~n"
|
||||
"Enter the real name (or whatever name people recognize) for the sysop.~n"
|
||||
"There are no rules for this one. Any valid UTF-8 printables are legal.~n",
|
||||
ok = io:format(Instructions),
|
||||
RealName = zx_tty:get_input(),
|
||||
create_realm(Realm, Address, Port, UserName, Email, RealName).
|
||||
|
||||
|
||||
-spec create_realm(Realm, Address, Port, UserName, Email, RealName) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
Address :: inet:hostname() | inet:ip_address(),
|
||||
Port :: inet:port_number(),
|
||||
UserName :: string(),
|
||||
Email :: string(),
|
||||
RealName :: string().
|
||||
|
||||
create_realm(Realm, Address, Port, UserName, Email, RealName) ->
|
||||
ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"),
|
||||
KeyName = UserName ++ "-root",
|
||||
store_realm(#realm_init{realm = Realm,
|
||||
addr = Addr,
|
||||
port = Port,
|
||||
url = URL,
|
||||
sysop = Sysop = #user_data{username = UserName,
|
||||
keys = [KeyName]}}) ->
|
||||
ok = make_realm_dirs(Realm),
|
||||
ok = zx_key:generate_rsa({Realm, KeyName}),
|
||||
Address = parse_maybe_address(Addr),
|
||||
RealmConf =
|
||||
[{realm, Realm},
|
||||
{prime, {Address, Port}},
|
||||
{sysop, UserName},
|
||||
{key, KeyName}],
|
||||
UserConf =
|
||||
[{realm, Realm},
|
||||
{username, UserName},
|
||||
{realmname, RealName},
|
||||
{contact_info, [{"email", Email}]},
|
||||
{keys, [KeyName]}],
|
||||
{key, KeyName},
|
||||
{url, URL}],
|
||||
ok = store_user(Sysop),
|
||||
RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"),
|
||||
ok = zx_lib:write_terms(RealmConfPath, RealmConf),
|
||||
UserConfPath = filename:join(zx_lib:path(etc, Realm), UserName ++ ".user"),
|
||||
ok = zx_lib:write_terms(UserConfPath, UserConf),
|
||||
ok = create_realmfile(Realm, "."),
|
||||
ZRF = Realm ++ ".zrf",
|
||||
Message =
|
||||
@ -1054,6 +904,308 @@ create_realm(Realm, Address, Port, UserName, Email, RealName) ->
|
||||
io:format(Message, Substitutions).
|
||||
|
||||
|
||||
-spec ask_realm() -> string().
|
||||
|
||||
ask_realm() ->
|
||||
Instructions =
|
||||
"~nNAMING~n"
|
||||
"Enter a name for your new realm.~n"
|
||||
"Names can contain only lower-case letters, numbers and the underscore.~n"
|
||||
"Names must begin with a lower-case letter.~n",
|
||||
ok = io:format(Instructions),
|
||||
Realm = zx_tty:get_input(),
|
||||
case zx_lib:valid_lower0_9(Realm) of
|
||||
true ->
|
||||
case realm_exists(Realm) of
|
||||
false ->
|
||||
Realm;
|
||||
true ->
|
||||
ok = io:format("That realm already exists. Be more original.~n"),
|
||||
ask_realm()
|
||||
end;
|
||||
false ->
|
||||
ok = io:format("Bad realm name \"~ts\". Try again.~n", [Realm]),
|
||||
ask_realm()
|
||||
end.
|
||||
|
||||
|
||||
-spec ask_addr() -> string().
|
||||
|
||||
ask_addr() ->
|
||||
Message =
|
||||
"~nHOST ADDRESS~n"
|
||||
"Enter a static, valid hostname or IPv4 or IPv6 address at which this host "
|
||||
"can be reached from the public internet (or internal network if it will never "
|
||||
"need to be reached from the internet).~n"
|
||||
"DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n",
|
||||
ok = io:format(Message),
|
||||
case zx_tty:get_input() of
|
||||
"" ->
|
||||
ok = io:format("You need to enter an address.~n"),
|
||||
ask_addr();
|
||||
String ->
|
||||
String
|
||||
end.
|
||||
|
||||
|
||||
-spec parse_maybe_address(string()) -> inet:hostname() | inet:ip_address().
|
||||
|
||||
parse_maybe_address(String) ->
|
||||
case inet:parse_address(String) of
|
||||
{ok, Address} -> Address;
|
||||
{error, einval} -> String
|
||||
end.
|
||||
|
||||
|
||||
-spec ask_port() -> inet:port_number().
|
||||
|
||||
ask_port() ->
|
||||
Message =
|
||||
"~nPUBLIC PORT NUMBER~n"
|
||||
"Enter the publicly visible (external) port number at which this service "
|
||||
"should be available.~n"
|
||||
"(This might be different from the local port number if you are forwarding "
|
||||
"ports or have a complex network layout.)~n",
|
||||
ok = io:format(Message),
|
||||
prompt_port_number(11311).
|
||||
|
||||
|
||||
-spec prompt_port_number(Current) -> Result
|
||||
when Current :: inet:port_number(),
|
||||
Result :: inet:port_number().
|
||||
|
||||
prompt_port_number(Current) ->
|
||||
ok = io:format("A valid port is any number from 1 to 65535.~n"),
|
||||
case zx_tty:get_input("[~tw]", [Current]) of
|
||||
"" ->
|
||||
Current;
|
||||
S ->
|
||||
try
|
||||
case list_to_integer(S) of
|
||||
Port when 16#ffff >= Port, Port > 0 ->
|
||||
Port;
|
||||
Illegal ->
|
||||
Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again.~n",
|
||||
ok = io:format(Whoops, [Illegal]),
|
||||
prompt_port_number(Current)
|
||||
end
|
||||
catch error:badarg ->
|
||||
ok = io:format("~tp is not a port number. Try again...", [S]),
|
||||
prompt_port_number(Current)
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
-spec ask_url() -> string().
|
||||
|
||||
ask_url() ->
|
||||
Message =
|
||||
"~nURL~n"
|
||||
"Most public realms have a website, IRC channel, or similar location where its "
|
||||
"community (or customers) communicate. If you have such a URL enter it here.~n"
|
||||
"NOTE: No checking is performed on the input here. Confuse your users at "
|
||||
"your own peril!~n",
|
||||
ok = io:format(Message),
|
||||
zx_tty:get_input("[ENTER] to leave blank").
|
||||
|
||||
|
||||
-spec create_sysop(InitUser) -> FullUser
|
||||
when InitUser :: #user_data{},
|
||||
FullUser :: #user_data{}.
|
||||
|
||||
create_sysop(U = #user_data{username = none}) ->
|
||||
UserName = ask_username(),
|
||||
KeyName = UserName ++ ".root",
|
||||
create_sysop(U#user_data{username = UserName, keys = [KeyName]});
|
||||
create_sysop(U = #user_data{realname = none}) ->
|
||||
create_sysop(U#user_data{realname = ask_realname()});
|
||||
create_sysop(U = #user_data{contact_info = none}) ->
|
||||
create_sysop(U#user_data{contact_info = [ask_email()]});
|
||||
create_sysop(U = #user_data{username = UserName,
|
||||
realname = RealName,
|
||||
contact_info = [{"email", Email}]}) ->
|
||||
Instructions =
|
||||
"~nSYSOP DATA CONFIRMATION~n"
|
||||
"Please correct or confirm the sysop's data:~n"
|
||||
"[1] Username : ~ts~n"
|
||||
"[2] Real Name: ~ts~n"
|
||||
"[3] Email : ~ts~n"
|
||||
"Press a number to select something to change, or [ENTER] to accept.~n",
|
||||
ok = io:format(Instructions, [UserName, RealName, Email]),
|
||||
case zx_tty:get_input() of
|
||||
"1" -> create_sysop(U#user_data{username = none});
|
||||
"2" -> create_sysop(U#user_data{realname = none});
|
||||
"3" -> create_sysop(U#user_data{contact_info = none});
|
||||
"" -> U;
|
||||
_ ->
|
||||
ok = io:format("~nArglebargle, glop-glyf!?!~n~n"),
|
||||
create_sysop(U)
|
||||
end.
|
||||
|
||||
|
||||
-spec create_user() -> zx:outcome().
|
||||
|
||||
create_user() ->
|
||||
create_user(#user_data{}).
|
||||
|
||||
|
||||
-spec create_user(#user_data{}) -> zx:outcome().
|
||||
|
||||
create_user(U = #user_data{realm = none}) ->
|
||||
case pick_realm() of
|
||||
{ok, Realm} -> create_user(U#user_data{realm = Realm});
|
||||
{error, no_realms} -> {error, "No realms configured.", 1}
|
||||
end;
|
||||
create_user(U = #user_data{username = none}) ->
|
||||
UserName = ask_username(),
|
||||
KeyName = UserName ++ ".1",
|
||||
create_user(U#user_data{username = UserName, keys = [KeyName]});
|
||||
create_user(U = #user_data{realname = none}) ->
|
||||
create_user(U#user_data{realname = ask_realname()});
|
||||
create_user(U = #user_data{contact_info = none}) ->
|
||||
create_user(U#user_data{contact_info = [ask_email()]});
|
||||
create_user(U = #user_data{realm = Realm,
|
||||
username = UserName,
|
||||
realname = RealName,
|
||||
contact_info = [{"email", Email}]}) ->
|
||||
Instructions =
|
||||
"~nUSER DATA CONFIRMATION~n"
|
||||
"Please correct or confirm the user's data:~n"
|
||||
"[1] Realm : ~ts~n"
|
||||
"[2] Username : ~ts~n"
|
||||
"[3] Real Name: ~ts~n"
|
||||
"[4] Email : ~ts~n"
|
||||
"Press a number to select something to change, or [ENTER] to accept.~n",
|
||||
ok = io:format(Instructions, [Realm, UserName, RealName, Email]),
|
||||
case zx_tty:get_input() of
|
||||
"1" -> create_user(U#user_data{realm = none});
|
||||
"2" -> create_user(U#user_data{username = none});
|
||||
"3" -> create_user(U#user_data{realname = none});
|
||||
"4" -> create_user(U#user_data{contact_info = none});
|
||||
"" -> store_user(U);
|
||||
_ ->
|
||||
ok = io:format("~nArglebargle, glop-glyf!?!~n~n"),
|
||||
create_user(U)
|
||||
end.
|
||||
|
||||
|
||||
-spec store_user(#user_data{}) -> ok.
|
||||
|
||||
store_user(#user_data{realm = Realm,
|
||||
username = UserName,
|
||||
realname = RealName,
|
||||
contact_info = ContactInfo,
|
||||
keys = KeyNames}) ->
|
||||
UserConf =
|
||||
[{realm, Realm},
|
||||
{username, UserName},
|
||||
{realname, RealName},
|
||||
{contact_info, ContactInfo},
|
||||
{keys, KeyNames}],
|
||||
ok = gen_keys(Realm, KeyNames),
|
||||
UserConfPath = filename:join(zx_lib:path(etc, Realm), UserName ++ ".user"),
|
||||
ok = zx_lib:write_terms(UserConfPath, UserConf),
|
||||
log(info, "User ~tp created.", [{Realm, UserName}]).
|
||||
|
||||
|
||||
-spec gen_keys(Realm, KeyNames) -> ok
|
||||
when Realm :: zx:realm(),
|
||||
KeyNames :: [zx:key_names()].
|
||||
|
||||
gen_keys(Realm, KeyNames) ->
|
||||
ok = io:format("Generating keys. This might take a while, so settle in...~n"),
|
||||
GenRSA = fun(KeyName) -> zx_key:generate_rsa({Realm, KeyName}) end,
|
||||
lists:foreach(GenRSA, KeyNames).
|
||||
|
||||
|
||||
-spec pick_realm() -> Result
|
||||
when Result :: {ok, zx:realm()}
|
||||
| {error, no_realms}.
|
||||
|
||||
pick_realm() ->
|
||||
case zx_lib:list_realms() of
|
||||
[] ->
|
||||
ok = log(warning, "No realms configured! Exiting..."),
|
||||
{error, no_realms};
|
||||
Realms ->
|
||||
ok = io:format("Select a realm:~n"),
|
||||
Options = lists:zip(Realms, Realms),
|
||||
Realm = zx_tty:select(Options),
|
||||
{ok, Realm}
|
||||
end.
|
||||
|
||||
|
||||
-spec ask_username() -> zx:user_name().
|
||||
|
||||
ask_username() ->
|
||||
Instructions =
|
||||
"~nUSERNAME~n"
|
||||
"Enter a username.~n"
|
||||
"Names can contain only lower-case letters, numbers and the underscore.~n"
|
||||
"Names must begin with a lower-case letter.~n",
|
||||
ok = io:format(Instructions),
|
||||
case zx_tty:get_input() of
|
||||
"" ->
|
||||
Message = "You have to be called *something*. Let's try that again.~n",
|
||||
ok = io:format(Message),
|
||||
ask_username();
|
||||
UserName ->
|
||||
case zx_lib:valid_lower0_9(UserName) of
|
||||
true ->
|
||||
UserName;
|
||||
false ->
|
||||
ok = io:format("Bad username ~tp. Try again.~n", [UserName]),
|
||||
ask_username()
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
-spec ask_realname() -> string().
|
||||
|
||||
ask_realname() ->
|
||||
Instructions =
|
||||
"~nREAL NAME~n"
|
||||
"Enter the user's real name (or whatever name people recognize).~n"
|
||||
"There are no rules for this one. Any valid UTF-8 printables are legal.~n",
|
||||
ok = io:format(Instructions),
|
||||
zx_tty:get_input().
|
||||
|
||||
|
||||
-spec ask_email() -> {Type :: string(), Email :: string()}.
|
||||
|
||||
ask_email() ->
|
||||
Instructions =
|
||||
"~nEMAIL~n"
|
||||
"Enter an email address.~n"
|
||||
"Valid email address rules apply though the checking done here is quite "
|
||||
"minimal. Check the address you enter carefully. The only people who will "
|
||||
"suffer from an invalid address are other realm users.~n",
|
||||
ok = io:format(Instructions),
|
||||
Email = zx_tty:get_input(),
|
||||
case string:lexemes(Email, "@") of
|
||||
[User, Host] ->
|
||||
case {zx_lib:valid_lower0_9(User), zx_lib:valid_label(Host)} of
|
||||
{true, true} ->
|
||||
{"email", Email};
|
||||
{false, true} ->
|
||||
Message = "The user part of the email address seems invalid.~n",
|
||||
ok = io:format(Message),
|
||||
ask_email();
|
||||
{true, false} ->
|
||||
Message = "The host part of the email address seems invalid.~n",
|
||||
ok = io:format(Message),
|
||||
ask_email();
|
||||
{false, false} ->
|
||||
Message = "This email address is totally bonkers. Try again.~n",
|
||||
ok = io:format(Message),
|
||||
ask_email()
|
||||
end;
|
||||
_ ->
|
||||
ok = io:format("Don't get fresh with me. Try again. For real this time.~n"),
|
||||
ask_email()
|
||||
end.
|
||||
|
||||
|
||||
-spec realm_exists(zx:realm()) -> boolean().
|
||||
%% @private
|
||||
%% Checks for remnants of a realm.
|
||||
@ -1111,8 +1263,7 @@ drop_realm(Realm) ->
|
||||
case realm_exists(Realm) of
|
||||
true ->
|
||||
Message =
|
||||
"~n"
|
||||
" WARNING: Are you SURE you want to remove realm ~ts?~n"
|
||||
"~nWARNING: Are you SURE you want to remove realm ~ts?~n"
|
||||
"(Only \"Y\" will confirm this action.)~n",
|
||||
ok = io:format(Message, [Realm]),
|
||||
case zx_tty:get_input() of
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||
-license("GPL-3.0").
|
||||
|
||||
-export([get_input/0, select/1, select_string/1]).
|
||||
-export([get_input/0, get_input/1, get_input/2, select/1, select_string/1]).
|
||||
|
||||
|
||||
%%% Type Definitions
|
||||
@ -20,12 +20,38 @@
|
||||
|
||||
%%% User menu interface (terminal)
|
||||
|
||||
|
||||
-spec get_input() -> string().
|
||||
%% @private
|
||||
%% Provide a standard input prompt and newline sanitized return value.
|
||||
|
||||
get_input() ->
|
||||
string:trim(io:get_line("(^C to quit): ")).
|
||||
case string:trim(io:get_line("(or \"QUIT\"): ")) of
|
||||
"QUIT" -> what_a_quitter();
|
||||
String -> String
|
||||
end.
|
||||
|
||||
|
||||
-spec get_input(Prompt :: string()) -> string().
|
||||
%% @private
|
||||
%% Introduce
|
||||
|
||||
get_input(Prompt) ->
|
||||
get_input(Prompt, []).
|
||||
|
||||
|
||||
-spec get_input(Format, Args) -> string()
|
||||
when Format :: string(),
|
||||
Args :: [term()].
|
||||
%% @private
|
||||
%% Allow the caller to use io format strings and args to create a prompt.
|
||||
|
||||
get_input(Format, Args) ->
|
||||
Prompt = io_lib:format(Format, Args),
|
||||
case string:trim(io:get_line(["(or \"QUIT\") ", Prompt, ": "])) of
|
||||
"QUIT" -> what_a_quitter();
|
||||
String -> String
|
||||
end.
|
||||
|
||||
|
||||
-spec select(Options) -> Selected
|
||||
@ -37,13 +63,18 @@ get_input() ->
|
||||
|
||||
select(Options) ->
|
||||
Max = show(Options),
|
||||
case pick(string:to_integer(io:get_line("(or ^C to quit)~n ? ")), Max) of
|
||||
case string:trim(io:get_line("(or \"QUIT\"): ")) of
|
||||
"QUIT" ->
|
||||
what_a_quitter();
|
||||
String ->
|
||||
case pick(string:to_integer(String), Max) of
|
||||
error ->
|
||||
ok = hurr(),
|
||||
select(Options);
|
||||
I ->
|
||||
{_, Value} = lists:nth(I, Options),
|
||||
Index ->
|
||||
{_, Value} = lists:nth(Index, Options),
|
||||
Value
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
@ -78,7 +109,7 @@ show([], I) ->
|
||||
I;
|
||||
show([{Label, _} | Rest], I) ->
|
||||
Z = I + 1,
|
||||
ok = io:format(" ~2w - ~ts~n", [Z, Label]),
|
||||
ok = io:format("[~2w] ~ts~n", [Z, Label]),
|
||||
show(Rest, Z).
|
||||
|
||||
|
||||
@ -99,3 +130,12 @@ pick(_, _) -> error.
|
||||
%% Present an appropriate response when the user derps on selection.
|
||||
|
||||
hurr() -> io:format("That isn't an option.~n").
|
||||
|
||||
|
||||
-spec what_a_quitter() -> no_return().
|
||||
%% @private
|
||||
%% Halt the runtime if the user decides to quit.
|
||||
|
||||
what_a_quitter() ->
|
||||
ok = io:format("User abort: \"QUIT\".~nHalting.~n"),
|
||||
halt(0).
|
||||
|
||||
13
zomp/zx
13
zomp/zx
@ -1,10 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
ZOMP_DIR="$HOME/.zomp"
|
||||
VERSION=$(cat "$ZOMP_DIR/etc/version.txt")
|
||||
ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$VERSION"
|
||||
export ZOMP_DIR="${ZOMP_DIR:-$HOME/.zomp}"
|
||||
version=$(cat "$ZOMP_DIR/etc/version.txt")
|
||||
export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$version"
|
||||
|
||||
pushd "$ZX_DIR" > /dev/null
|
||||
start_dir="$PWD"
|
||||
cd "$ZX_DIR"
|
||||
./make_zx
|
||||
popd > /dev/null
|
||||
erl -pa "$ZX_DIR/ebin" -run zx run $@
|
||||
cd "$start_dir"
|
||||
erl -noshell -pa "$ZX_DIR/ebin" -run zx do $@
|
||||
|
||||
11
zomp/zxh
Executable file
11
zomp/zxh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
export ZOMP_DIR="${ZOMP_DIR:-$HOME/.zomp}"
|
||||
version=$(cat "$ZOMP_DIR/etc/version.txt")
|
||||
export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$version"
|
||||
|
||||
start_dir="$PWD"
|
||||
cd "$ZX_DIR"
|
||||
./make_zx
|
||||
cd "$start_dir"
|
||||
erl -pa "$ZX_DIR/ebin" -run zx do $@
|
||||
9
zomp/zxh.cmd
Normal file
9
zomp/zxh.cmd
Normal file
@ -0,0 +1,9 @@
|
||||
REM Prepare the environment for ZX and launch it
|
||||
|
||||
set ZOMP_DIR="%LOCALAPPDATA%\zomp"
|
||||
set VERSION=<"%ZOMP_DIR%\etc\version.txt"
|
||||
set ZX_DIR="%ZOMP_DIR%\lib\otpr\zx\%VERSION%"
|
||||
pushd "%ZX_DIR%"
|
||||
escript.exe make_zx
|
||||
popd
|
||||
erl.exe -pa "%ZX_DIR%/ebin" -run zx run "%*"
|
||||
15
zx_dev
15
zx_dev
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
pushd $(dirname $BASH_SOURCE) > /dev/null
|
||||
ZX_DEV_ROOT=$PWD
|
||||
popd > /dev/null
|
||||
export ZOMP_DIR="$ZX_DEV_ROOT/tester"
|
||||
rm -rf "$ZOMP_DIR"
|
||||
cp -r "$ZX_DEV_ROOT/zomp" "$ZOMP_DIR"
|
||||
VERSION=$(cat "$ZOMP_DIR/etc/version.txt")
|
||||
export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$VERSION"
|
||||
|
||||
pushd "$ZX_DIR" > /dev/null
|
||||
./make_zx
|
||||
popd > /dev/null
|
||||
erl -noshell -pa "$ZX_DIR/ebin" -run zx do $@
|
||||
15
zxh_dev
15
zxh_dev
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
pushd $(dirname $BASH_SOURCE) > /dev/null
|
||||
ZX_DEV_ROOT=$PWD
|
||||
popd > /dev/null
|
||||
export ZOMP_DIR="$ZX_DEV_ROOT/tester"
|
||||
rm -rf "$ZOMP_DIR"
|
||||
cp -r "$ZX_DEV_ROOT/zomp" "$ZOMP_DIR"
|
||||
VERSION=$(cat "$ZOMP_DIR/etc/version.txt")
|
||||
export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$VERSION"
|
||||
|
||||
pushd "$ZX_DIR" > /dev/null
|
||||
./make_zx
|
||||
popd > /dev/null
|
||||
erl -pa "$ZX_DIR/ebin" -run zx do $@
|
||||
Loading…
x
Reference in New Issue
Block a user