Author: bdonlan
Date: 2005-12-10 02:09:50 -0500 (Sat, 10 Dec 2005)
New Revision: 953
Modified:
trunk/erlang/server/haver_worker.erl
trunk/erlang/server/schema.hrl
trunk/erlang/server/server.erl
Log:
INFO and CLOSE
Modified: trunk/erlang/server/haver_worker.erl
===================================================================
--- trunk/erlang/server/haver_worker.erl 2005-12-10 05:47:29 UTC (rev
952)
+++ trunk/erlang/server/haver_worker.erl 2005-12-10 07:09:50 UTC (rev
953)
@@ -34,11 +34,12 @@
available_digests() -> ["sha1", "md5"].
digest("sha1", Data) -> crypto:sha(Data);
digest("md5", Data) -> crypto:md5(Data);
-digest(M, _) -> notfound.
+digest(_, _) -> notfound.
haver_sendline(Line) ->
+% io:format("~p~n", [Line]),
Socket = get(socket),
- io:format("lineenc ~p~n", [haver_protocol:encode(Line)]),
+% io:format("lineenc ~p~n", [haver_protocol:encode(Line)]),
% gen_tcp:send(Socket, haver_protocol:encode(Line)),
% gen_tcp:send(Socket, "\r\n").
gen_tcp:send(Socket, lists:append(haver_protocol:encode(Line), "\r\n")).
@@ -54,12 +55,14 @@
put(socket, Socket),
{ ok, { Address, RemotePort } } = inet:peername(Socket),
put(address, Address),
+ put(authenticated, no),
receive
{ line, [ "HAVER", Vers | Capabilities ] } ->
put(can_auth, no),
+ put(client_version, Vers),
check_caps(Capabilities),
- % XXX configgable hostname
- haver_sendline(["HAVER", "bd.beginyourfear.com", "Erver/0.0"]),
+ GI = haver_util:get_globinfo(),
+ haver_sendline(["HAVER", GI#globinfo.hostname,
GI#globinfo.version]),
haver_login();
{ line, _ } ->
gen_tcp:close(Socket),
@@ -113,7 +116,9 @@
ER = http_base_64:encode(binary_to_list(R)),
SR = lists:takewhile(fun(C) -> C =/= $= end, ER),
case SR of
- Response -> Callback();
+ Response ->
+ put(authenticated, yes),
+ Callback();
_ ->
haver_sendline(["FAIL", "AUTH:BASIC",
"auth.fail"]),
haver_login()
@@ -124,6 +129,7 @@
end.
try_ident(Uid) ->
+ Version = get(client_version),
F = fun() ->
Q = query
[ E || E <- table(users), E.name = Uid ]
@@ -131,7 +137,7 @@
R = mnemosyne:eval(Q),
io:format("~p~n", [R]),
case R of
- [] -> mnesia:write(#users{name = Uid, pid = self(), ip =
get(address), auth = no}),
+ [] -> mnesia:write(#users{name = Uid, pid = self(), ip =
get(address), auth = get(authenticated), version = Version, lastmsg = now()}),
ok;
_ -> collision
end
@@ -254,7 +260,7 @@
end,
R = mnemosyne:eval(Q),
case R of
- [] -> mnesia:write(#channels{name = Channel, owner = Uid}),
+ [] -> mnesia:write(#channels{name = Channel, owner = Uid, created
= now()}),
ok;
_ -> collision
end end,
@@ -400,6 +406,7 @@
ok -> haver_sendline(["OPEN", Channel]);
collision -> haver_sendline(["FAIL", "OPEN", "exists.channel",
Channel])
end, ok;
+ { line, [ "CLOSE", Channel ] } -> handle_close(Channel);
{ line, [ "JOIN", Channel ] } ->
case haver_join(Channel) of
ok -> ok;
@@ -412,12 +419,14 @@
notfound -> haver_sendline(["FAIL", "PART", "not.joined",
Channel])
end, ok;
{ line, [ "TO", Uid | Message ] } ->
+ update_idle(),
Pid = find_user(Uid),
case Pid of
notfound -> haver_sendline(["FAIL", "TO", "unknown.user",
Uid]);
_ -> Pid ! { privmsg, get(uid), Message }
end, ok;
{ line, [ "IN", Channel | Message ] } ->
+ update_idle(),
haver_sendchan(Channel, Message);
{ line, [ "LIST", Channel, Type ] } -> haver_list(Channel, Type), ok;
{ line, [ "POKE" | L ] } -> haver_sendline(["OUCH" | L]);
@@ -428,6 +437,7 @@
{ line, [ "BYE" ] } -> self() ! { line, [ "BYE", "bye" ] };
{ line, [ "REG:ACCOUNT", Email, Passcode ] } ->
haver_register(Email, Passcode);
+ { line, [ "INFO", NS, Name ] } -> handle_info(NS, Name);
{ line, [ L | T ] } ->
io:format("unmatched line: ~p~n", [[L|T]]),
haver_sendline(["FAIL", L, "unknown.cmd"]),
@@ -445,3 +455,105 @@
Other -> io:format("unknown msg: ~p~n", [Other]), exit(Other)
end,
haver_mainloop().
+
+
+info_for(Uid, "user") ->
+ UInfo = case mnesia:dirty_read(users, Uid) of
+ [] -> [{"state", "offline"}];
+ [URec] ->
+ Idle = haver_util:tdelta(URec#users.lastmsg, now()),
+ IP = haver_util:maskIP(URec#users.ip), % XXX admins
+ [{"idle", io_lib:format("~B", [Idle])},
+ {"version", URec#users.version},
+ {"address", IP},
+ {"state", "online"}]
+ end,
+ ARec = case get_userinfo(Uid) of
+ notfound -> [];
+ Record -> [{"email", Record#userattr.email}]
+ end,
+ case lists:append(UInfo, ARec) of
+ [{"state", "offline"}] -> { notfound, "unknown.user", Uid };
+ L -> L
+ end;
+
+info_for("&lobby", "channel") ->
+ GI = haver_util:get_globinfo(),
+ [{"booted", haver_util:format_time(GI#globinfo.booted)},
+ {"hostname", GI#globinfo.hostname}, {"version", GI#globinfo.version}];
+
+info_for(Cid, "channel") ->
+ case mnesia:dirty_read(channels, Cid) of
+ [] -> { notfound, "unknown.user", Cid };
+ [R] ->
+ OR = case R#channels.owner of
+ "" -> [];
+ N -> [ { "owner", N } ]
+ end,
+ [{"created", haver_util:format_time(R#channels.created)} | OR]
+ end;
+
+info_for(_, T) -> { notfound, "invalid.type", T }.
+
+transform_info(I) ->
+ lists:concat(lists:map(fun(T) -> tuple_to_list(T) end, I)).
+
+handle_info(NS, Name) ->
+ Info = info_for(Name, NS),
+ case Info of
+ { notfound, Err, Detail } ->
+ haver_sendline(["FAIL", "INFO", Err, Detail]);
+ L -> haver_sendline(["INFO", NS, Name | transform_info(L)])
+ end.
+
+update_idle() ->
+ Uid = get(uid),
+ T = fun() ->
+ [R] = query [ V || V <- table(users), V.name = Uid ] end,
+ mnesia:write(R#users{lastmsg = now()})
+ end,
+ mnesia:transaction(T).
+
+handle_close(Cid) ->
+ Uid = get(uid),
+ T = fun() ->
+ case get(authenticated) of
+ no -> mnesia:abort(["not.authenticated"]);
+ yes -> ok
+ end,
+ Channelrec = case mnesia:read({channels, Cid}) of
+ [] -> mnesia:abort(["unknown.channel"]);
+ [CRec] -> CRec
+ end,
+ case Channelrec#channels.owner of
+ Uid -> ok;
+ _ -> mnesia:abort(["not.owner"])
+ end,
+ mnesia:delete_object(Channelrec),
+ io:format("cp~n"),
+ mnesia:delete_object({chanjoin, Cid, Uid}),
+ io:format("cp~n"),
+ CJ = query [ { CJ, U.pid }
+ || CJ <- table(chanjoin),
+ CJ.channel = Cid,
+ U <- table(users),
+ U.name = CJ.user
+ ] end,
+ CJR = mnemosyne:eval(CJ),
+ io:format("cp~n"),
+ lists:map(fun( { J, _ } ) -> mnesia:delete_object(J) end, CJR),
+ io:format("cp~n"),
+ { ok, CJR }
+ end,
+ R = mnesia:transaction(T),
+ io:format("~p~n", [R]),
+ case R of
+ { atomic, { ok, CJ } } ->
+ lists:map(fun({ _, Pid}) -> Pid ! {sendline, ["CLOSE", Cid]} end,
CJ),
+ haver_sendline(["CLOSE", Cid]);
+ { aborted, X } ->
+ case X of
+ [_|_] -> haver_sendline(["FAIL", "CLOSE" | X]);
+ _ -> io:format("unk: ~p~n", [X])
+ end
+ end.
Modified: trunk/erlang/server/schema.hrl
===================================================================
--- trunk/erlang/server/schema.hrl 2005-12-10 05:47:29 UTC (rev 952)
+++ trunk/erlang/server/schema.hrl 2005-12-10 07:09:50 UTC (rev 953)
@@ -1,8 +1,11 @@
% vim: set ft=erlang :
--record(users, {name, pid, ip, auth}).
--record(channels, {name, owner}).
+-record(users, {name, pid, ip, auth, version, lastmsg}).
+-record(channels, {name, owner, created}).
-record(chanjoin, {channel, user}).
-record(userattr, {name,
passcode,
email,
admin}).
+-record(globinfo, {booted, hostname, version}).
+
+% c(server). c(haver_worker). c(haver_util). c(haver_protocol).
server:start(7575, "bd.beginyourfear.com").
Modified: trunk/erlang/server/server.erl
===================================================================
--- trunk/erlang/server/server.erl 2005-12-10 05:47:29 UTC (rev 952)
+++ trunk/erlang/server/server.erl 2005-12-10 07:09:50 UTC (rev 953)
@@ -1,5 +1,5 @@
-module(server).
--export([cstart/1, start/1, stop/0, worker/2, setup_schema/0,
+-export([cstart/2, start/2, stop/0, worker/2, setup_schema/0,
clear_transients/0, reap_child/2]).
-import(haver_worker, []).
-import(haver_protocol, [encode/1, decode/1]).
@@ -28,15 +28,22 @@
{attributes, record_info(fields, userattr)},
{disc_copies, [node()]},
{type, set}
+ ]),
+ mnesia:delete_table(globinfo),
+ mnesia:create_table(globinfo, [
+ {attributes, record_info(fields, globinfo)},
+ {type, set}
]).
clear_transients() ->
mnesia:clear_table(users),
- mnesia:clear_table(chanjoin).
+ mnesia:clear_table(chanjoin),
+ mnesia:clear_table(globinfo).
-cstart(Port) ->
+cstart(Port, Hostname) ->
register(haver, self()),
clear_transients(),
+ mnesia:dirty_write(#globinfo{ booted = now(), hostname = Hostname, version
= "Erver/0.1" }),
{ok, LSock} = gen_tcp:listen(Port, [list, {packet, 0},
{active, true},
{reuseaddr, true}]),
@@ -45,18 +52,18 @@
init_tables() ->
io:format("Waiting for table init...", []),
- case mnesia:wait_for_tables([users, chanjoin, channels], 5 * 1000) of
+ case mnesia:wait_for_tables([users, chanjoin, channels, userattr,
globinfo], 5 * 1000) of
ok -> io:format("Table init done.~n", []), ok;
R -> io:format("Table init failed: ~p~n", [R]), R
end.
-start(Port) ->
+start(Port, Hostname) ->
case whereis(haver) of
- undefined -> do_start(Port);
+ undefined -> do_start(Port, Hostname);
Pid -> { already_running, Pid }
end.
-do_start(Port) ->
+do_start(Port, Hostname) ->
io:format("Starting mnesia...~n", []),
mnesia:start(),
io:format("Starting crypto...~n", []),
@@ -64,7 +71,7 @@
io:format("Starting mnemosyne...~n", []),
application:start(mnemosyne),
case init_tables() of
- ok -> { ok, spawn(?MODULE, cstart, [Port]) };
+ ok -> { ok, spawn(?MODULE, cstart, [Port, Hostname]) };
Err -> { nok, Err }
end.
stop() ->
@@ -150,7 +157,7 @@
Buf2 = peer_proc(Buf, Pid),
receive
{tcp, Socket, Data} ->
- io:format("incoming packet ~p~n", [Data]),
+% io:format("incoming packet ~p~n", [Data]),
peer(Socket, lists:append(Buf2, Data), Pid);
{tcp_closed, Why} -> exit({socket_closed, Why});
Something ->
@@ -159,7 +166,7 @@
end.
peer_proc(Buf, Pid) ->
- io:format("peer_proc ~p~n", [Buf]),
+% io:format("peer_proc ~p~n", [Buf]),
case lists:splitwith(fun(C) -> not ((C == $\r) or (C == $\n)) end, Buf) of
{ _, [] } -> Buf;
{ L, Rem} ->