diff --git a/zx b/zx index 91d3505..6bfc07b 100755 --- a/zx +++ b/zx @@ -1403,6 +1403,11 @@ dialyze() -> %%% Create Realm & Sysop create_realm() -> + ConfFile = filename:join(zomp_dir(), "zomp.conf"), + {ok, ZompConf} = file:consult(ConfFile), + create_realm(ZompConf). + +create_realm(ZompConf) -> Instructions = "~n" " Enter a name for your new realm.~n" @@ -1415,22 +1420,64 @@ create_realm() -> RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"), case filelib:is_regular(RealmFile) of false -> - create_realm(Realm); + create_realm(ZompConf, Realm); true -> ok = io:format("That realm already exists. Be more original.~n"), - create_realm() + create_realm(ZompConf) end; false -> ok = io:format("Bad realm name ~tp. Try again.~n", [Realm]), - create_realm() + create_realm(ZompConf) end. -create_realm(Realm) -> +create_realm(ZompConf, Realm) -> + {external_address, XA} = lists:keyfind(external_address, 1, ZompConf), + XAString = + case inet:ntoa(XA) of + {error, einval} -> XA; + String -> String + end, + Message = + "~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" + " The current public address is: ~ts. Press to keep this address.~n" + " DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n", + ok = io:format(Message, [XAString]), + Input = get_input(), + ExAddress = + case + + + + {Host, Port} = + case proplists:get(external_address, 1, ZompConf) of + {external_address, ExAddress} -> + {external_port, ExPort} = lists:keyfind(external_port, 1, ZompConf), + {ExAddress, ExPort}; + false -> + prompt_prime(Realm) + end, + HostString = + Instructions = + "~n" + " Accept current config?~n" + " Host: ~ts Port: ~w~n", + ok = io:format(Instructions, [Host, Port]), + case string:trim(io:get_line("(^C to quit) [Y]/n: ")) of + "" -> create_realm(Realm, Prime); + "Y" -> create_realm(Realm, Prime); + "y" -> create_realm(Realm, Prime); + _ -> prompt_prime(Realm) + end. + +prompt_prime(Realm) -> HostInstructions = "~n" - " Enter the prime (permanent) server's publicly accessible hostname or~n" - " address. If prime is identified by an address and not a name, enter it~n" - " as either a normal IPv4 dot-notation or IPv6 colon-notation.~n" + " Enter the prime (permanent) server's publicly accessible hostname or " + "address. If prime is identified by an address and not a name, enter it " + "as either a normal IPv4 dot-notation or IPv6 colon-notation.~n" " Note that address ranges will not be checked for validity.~n", ok = io:format(HostInstructions), HostString = get_input(), @@ -1439,29 +1486,43 @@ create_realm(Realm) -> {ok, Address} -> Address; {error, einval} -> HostString end, - PortInstructions = - "~n" - " Enter the port number the host will be listening on.~n" - " The port can be any number from 1 to 65535.~n" - " [Press enter for the default: 11311]~n", - ok = io:format(PortInstructions), + +create_prime(Realm, Host) -> + ok = io:format("~n Enter the local port number on which the host listen.~n"), + case prompt_port_number() of + {ok, ExPort} -> create_prime(Realm, Host, ExPort); + error -> create_prime(Realm, Host) + end. + + +create_realm(Realm, Host, InPort) -> + Message = "~n Enter the global port number on which the host will be available.~n", + ok = io:format(Message), + case prompt_port_number() of + {ok, ExPort} -> create_prime(Realm, Host, ExPort, InPort); + error -> create_prime(Realm, Host, ExPort) + end. + +prompt_port_number() -> + Instructions = + " A port can be any number from 1 to 65535.~n" + " [Press enter to accept the default port: 11311]~n", + ok = io:format(Instructions), case get_input() of "" -> - create_realm(Realm, {HostString, 11311}); + {ok, 11311}; S -> try case list_to_integer(S) of Port when 16#ffff >= Port, Port > 0 -> - create_realm(Realm, {Host, Port}); + {ok, Port}; Illegal -> Whoops = "~p is out of bounds (1~65535). Try again...", - ok = io:format(Whoops, [Illegal]), - create_realm(Realm) + error end catch error:badarg -> - Error = "~tp is not a port number. Try again...", - ok = io:format(Error, [S]), - create_realm(Realm) + ok = io:format("~tp is not a port number. Try again...", [S]), + error end end. @@ -1485,9 +1546,9 @@ create_realm(Realm, Prime, UserName) -> Instructions = "~n" " Enter an email address for the realm sysop.~n" - " Valid email address rules apply though the checking done here is quite~n" - " minimal. Check the address you enter carefully. The only people who will~n" - " suffer from an invalid address are your users.~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 = get_input(), [User, Host] = string:lexemes(Email, "@"), @@ -1519,7 +1580,8 @@ create_realm(Realm, Prime, UserName, Email) -> {ok, RealmKey, RealmPub} = generate_rsa({Realm, Realm ++ ".1.realm"}), {ok, PackageKey, PackagePub} = generate_rsa({Realm, Realm ++ ".1.package"}), {ok, SysopKey, SysopPub} = generate_rsa({Realm, UserName ++ ".1"}), - AllKeys = [RealmKey, RealmPub, PackageKey, PackagePub, SysopKey, SysopPub], + AllKeys = [RealmKey, RealmPub, PackageKey, PackagePub, SysopKey, SysopPub], + DangerousKeys = [PackageKey, SysopKey], Copy = fun(From) -> To = filename:basename(From), @@ -1540,21 +1602,23 @@ create_realm(Realm, Prime, UserName, Email) -> ok = file:delete(File), log(info, "Deleting ~ts", [File]) end, - DangerousKeys = [PackageKey, SysopKey], ok = lists:foreach(Copy, AllKeys), ok = lists:foreach(Drop, DangerousKeys), Message = "~n" " All of the keys generated have been moved to the current directory.~n" + "~n" " MAKE AND SECURELY STORE COPIES OF THESE KEYS.~n" - " The private package and sysop login keys have been deleted from the~n" - " key directory. These should only exist on your local system, not a prime.~n" - " realm server (particularly if other services are run on that machine).~n" - " The package and sysop keys will need to be copied to the ~~/.zomp/keys/~p/~n" + "~n" + " The private package and sysop login keys have been deleted from the " + "key directory. These should only exist on your local system, not a prime " + "realm server (particularly if other services are run on that machine).~n" + " The package and sysop keys will need to be copied to the ~~/.zomp/keys/~s/~n" " directory on your personal or dev machine.~n", ok = io:format(Message, [Realm]), Timestamp = calendar:now_to_universal_time(erlang:timestamp()), UserRecord = {{UserName, Realm}, [SysopPub], Email, RealName, 1, Timestamp}, + RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"), RealmMeta = [{realm, Realm}, {revision, 0}, @@ -1564,7 +1628,15 @@ create_realm(Realm, Prime, UserName, Email) -> {sysops, [UserRecord]}, {realm_keys, [RealmPub]}, {package_keys, [PackagePub]}], - ok = log(info, "Would be writing~n ~tp", [RealmMeta]), + ZompFile = filename:join(zomp_dir(), "zomp.conf"), + ZompConf = + [{prime, Prime}, + {external_port, ExPort}, + {internal_port, InPort}], + ok = write_terms(RealmFile, RealmMeta), + ok = write_terms(ZompFile, ZompConf), + ok = log(info, "Wrote to ~ts:~n ~tp", [RealmFile, RealmMeta]), + ok = log(info, "Wrote to ~ts:~n ~tp", [ZompFile, ZompConf]), halt(0). @@ -2124,6 +2196,7 @@ ensure_zomp_home() -> SubDirs = ["tmp", "key", "var", "lib", "zrp", "etc"], ok = lists:foreach(fun file:make_dir/1, SubDirs), ok = write_terms(default_realm_file(), default_realm()), + ok = write_terms("zomp.conf", default_conf()), ok = file:write_file(default_pubkey_file(), default_pubkey()), ok = log(info, "Zomp userland directory initialized."), file:set_cwd(CWD) @@ -2240,18 +2313,31 @@ realm_file(Realm) -> Realm ++ ".realm". --spec default_realm() -> RealmData - when RealmData :: [{atom(), term()}]. +-spec default_realm() -> [{Key :: atom(), Value :: term()}]. %% @private %% Returns the default realm file's data contents for the default "otpr" realm. default_realm() -> - [{name, "otpr"}, -% {prime, {"repo.psychobitch.party", 11311}}, - {prime, {"localhost", 11311}}, - {pubkey, default_pubkey_file()}, - {serial, 0}, - {mirrors, []}]. + [{realm, "otpr"}, + {revision, 0}, + {prime, {"repo.psychobitch.party", 11311}}, + {private, [{"localhost", 11311}]}, + {mirrors, []}, + {sysops, [{"otpr, ""zxq9"}]}, + {realm_keys, []}, + {package_keys, [default_pubkey_file()]}]. + + +-spec default_conf() -> [{Key :: atom(), Value :: term()}]. +%% @private +%% Return the default local config values for a zomp server. +%% The external and local port values are global values needed to make a zomp server +%% work in the face of unique port forwarding and NAT configurations outside the control +%% of the zomp server itself. zx references these values in a few places (namely when +%% setting up a mirror or prime realm). +default_conf() -> + [{external_port, 11311}, + {local_port, 11311}]. -spec default_pubkey_file() -> file:filename().