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} ->


Reply via email to