Author: bdonlan
Date: 2005-12-09 16:42:22 -0500 (Fri, 09 Dec 2005)
New Revision: 946
Added:
trunk/erlang/server/haver_protocol.erl
trunk/erlang/server/haver_worker.erl
trunk/erlang/server/schema.hrl
Removed:
trunk/erlang/server/line_encode.erl
Modified:
trunk/erlang/server/server.erl
Log:
Split up the client worker and master server; add BYE and QUIT
Copied: trunk/erlang/server/haver_protocol.erl (from rev 944,
trunk/erlang/server/line_encode.erl)
===================================================================
--- trunk/erlang/server/line_encode.erl 2005-12-09 20:34:01 UTC (rev 944)
+++ trunk/erlang/server/haver_protocol.erl 2005-12-09 21:42:22 UTC (rev
946)
@@ -0,0 +1,32 @@
+-module(haver_protocol).
+-export([decode/1, encode/1]).
+
+escape([]) -> [];
+escape([$\t|T]) -> [$\e, $t | escape(T)];
+escape([$\n|T]) -> [$\e, $n | escape(T)];
+escape([$\r|T]) -> [$\e, $r | escape(T)];
+escape([$\e|T]) -> [$\e, $e | escape(T)];
+escape([H|T]) -> [H | escape(T)].
+
+unescape([]) -> [];
+unescape([$\e, $t | T]) -> [$\t | unescape(T)];
+unescape([$\e, $e | T]) -> [$\e | unescape(T)];
+unescape([$\e, $r | T]) -> [$\r | unescape(T)];
+unescape([$\e, $n | T]) -> [$\n | unescape(T)];
+unescape([H|T]) -> [H | unescape(T)].
+
+decode([]) -> [];
+decode(L) ->
+ { Pre, Post } = lists:splitwith(fun(C) -> C =/= $\t end, L),
+ Word = unescape(Pre),
+ case Post of
+ [] -> [Word];
+ [_|L2] ->
+ ArgsAfter = decode(L2),
+ [Word|ArgsAfter]
+ end.
+
+encode_([]) -> [];
+encode_([H|T]) -> lists:append(H, [$\t|encode_(T)]).
+
+encode(L) -> encode_(lists:map(fun(W) -> escape(W) end, L)).
Added: trunk/erlang/server/haver_worker.erl
===================================================================
--- trunk/erlang/server/haver_worker.erl 2005-12-09 20:52:40 UTC (rev
945)
+++ trunk/erlang/server/haver_worker.erl 2005-12-09 21:42:22 UTC (rev
946)
@@ -0,0 +1,252 @@
+-module(haver_worker).
+-export([entry/2]).
+-include_lib("mnemosyne/include/mnemosyne.hrl").
+-include("schema.hrl").
+
+haver_sendline(Line) ->
+ Socket = get(socket),
+ gen_tcp:send(Socket, haver_protocol:encode(Line)),
+ gen_tcp:send(Socket, "\r\n").
+
+entry(Parent, Socket) ->
+ link(Parent),
+ put(socket, Socket),
+ receive
+ { line, [ "HAVER", Vers | _ ] } ->
+ haver_sendline(["HAVER", "localhost", "Erver/0.0"]),
+ haver_login();
+ { line, _ } ->
+ gen_tcp:close(Socket),
+ exit(rude_peer);
+ Foo -> io:format("haver_entry unknown msg ~p~n", [Foo])
+ end.
+
+handle_ident(Uid) ->
+ F = fun() ->
+ Q = query
+ [ E || E <- table(users), E.name = Uid ]
+ end,
+ R = mnemosyne:eval(Q),
+ io:format("~p~n", [R]),
+ case R of
+ [] -> mnesia:write(#users{name = Uid, pid = self()}),
+ ok;
+ _ -> collision
+ end
+ end,
+ R = mnesia:transaction(F),
+ case R of
+ { atomic, ok } ->
+ haver_sendline(["HELLO", Uid]),
+ put(uid, Uid),
+ haver_mainloop();
+ { atomic, collision } ->
+ haver_sendline(["FAIL", "IDENT", "exists.user", Uid]),
+ haver_login();
+ Wtf -> io:format("wtf: ~p~n", [Wtf])
+ end.
+
+haver_login() ->
+ receive
+ { line, [ "IDENT", Uid ] } -> handle_ident(Uid);
+ { line, _ } ->
+ haver_sendline(["DIE", "ihateyou"]),
+ gen_tcp:close(get(socket)),
+ exit(rudepeer);
+ M -> exit({unexpected, M})
+ end.
+
+find_user(Uid) ->
+ T = fun() ->
+ Q = query
+ [ U.pid || U <- table(users), U.name = Uid ]
+ end,
+ mnemosyne:eval(Q)
+ end,
+ R = mnesia:transaction(T),
+ case R of
+ { atomic, [Pid] } -> Pid;
+ { atomic, [] } -> notfound;
+ { atomic, Wtf } ->
+ io:format("find_user wtf: ~p~n", [Wtf]),
+ notfound;
+ _ ->
+ io:format("find_user err: ~p~n", [R]),
+ notfound
+ end.
+
+haver_open(Channel) ->
+ T = fun() ->
+ Q = query
+ [ C || C <- table(channels), C.name = Channel ]
+ end,
+ R = mnemosyne:eval(Q),
+ case R of
+ [] -> mnesia:write(#channels{name = Channel, notused = Channel}),
+ ok;
+ _ -> collision
+ end end,
+ R = mnesia:transaction(T),
+ case R of
+ { atomic, Result } -> Result
+ end.
+
+chan_broadcast(Channel, Message) ->
+ T = fun() ->
+ Q = query
+ [ U.pid
+ || U <- table(users),
+ J <- table(chanjoin),
+ U.name = J.user,
+ J.channel = Channel
+ ] end,
+ mnemosyne:eval(Q)
+ end,
+ R = mnesia:transaction(T),
+ case R of
+ { atomic, L } -> lists:map(fun(P) -> P ! Message end, L);
+ _ -> io:format("err, bad mnesia txn response ~p~n", [R]),
+ exit({mnesia_fail, R})
+ end.
+
+haver_join(Channel) ->
+ Uid = get(uid),
+ T = fun() ->
+ case mnesia:read({channels, Channel}) of
+ [] -> mnesia:abort(notfound);
+ _ -> ok
+ end,
+ Q = query
+ [ J || J <- table(chanjoin), J.user = Uid, J.channel = Channel ]
+ end,
+ case mnemosyne:eval(Q) of
+ [_]->
+ mnesia:abort(already_joined);
+ [] ->
+ mnesia:write(#chanjoin{channel = Channel, user = Uid}),
+ ok
+ end end,
+ R = mnesia:transaction(T),
+ case R of
+ { aborted, already_joined } -> collision;
+ { aborted, notfound } -> notfound;
+ { atomic, ok } ->
+ chan_broadcast(Channel, { sendline, [ "JOIN", Channel, Uid ] }),
+ ok
+ end.
+
+haver_part(Channel) ->
+ Uid = get(uid),
+ T = fun() ->
+ Q = query
+ [ J || J <- table(chanjoin), J.user = Uid, J.channel = Channel ]
+ end,
+ case mnemosyne:eval(Q) of
+ [] -> notfound;
+ [R]->
+ io:format("R=~p~n", [R]),
+ mnesia:delete_object(R), ok
+ end end,
+ R = mnesia:transaction(T),
+ case R of
+ { atomic, notfound } -> notfound;
+ { atomic, ok } ->
+ chan_broadcast(Channel, { sendline, [ "PART", Channel, Uid ] }),
+ ok
+ end.
+
+haver_getlist("&lobby", "channel") -> { ok, mnesia:dirty_all_keys(channels) };
+haver_getlist("&lobby", "user") -> { ok, mnesia:dirty_all_keys(users) };
+haver_getlist(Channel, "user") ->
+ T = fun() ->
+ case mnesia:read({channels, Channel}) of
+ [] -> mnesia:abort(notfound);
+ _ -> ok
+ end,
+ Records = mnesia:read({chanjoin, Channel}),
+ Uids = lists:map(fun({chanjoin, _, Uid}) -> Uid end, Records),
+ { ok, Uids }
+ end,
+ case mnesia:transaction(T) of
+ { aborted, notfound } -> notfound;
+ { atomic, {ok, Uids} } -> { ok, Uids };
+ Other ->
+ io:format("getlist(~p~n, \"user\") unexpected: ~p~n", [Channel,
Other]),
+ exit(Other)
+ end;
+haver_getlist(_, "channel") -> { ok, [] };
+haver_getlist(_, Type) -> {badtype, Type}.
+
+haver_list(C, T) ->
+ R = haver_getlist(C, T),
+ case R of
+ { ok, Members } -> haver_sendline(["LIST", C, T | Members ]);
+ notfound -> haver_sendline(["FAIL", "LIST", "unknown.channel", C]);
+ badtype -> haver_sendline(["FAIL", "LIST", "unknown.type", C])
+ end.
+
+haver_sendchan(Channel, Message) ->
+ Uid = get(uid),
+ T = fun() ->
+ Q = query
+ [ J || J <- table(chanjoin),
+ J.user = Uid,
+ J.channel = Channel
+ ] end,
+ case mnemosyne:eval(Q) of
+ [] -> notjoined;
+ _ -> ok
+ end end,
+ R = mnesia:transaction(T),
+ case R of
+ { atomic, notjoined } ->
+ haver_sendline(["FAIL", "IN", "not.joined", Channel]);
+ { atomic, ok } ->
+ chan_broadcast(Channel, { sendline, ["IN", Channel, Uid | Message]
})
+ end.
+
+haver_mainloop() ->
+ V = receive
+ { line, [ "OPEN", Channel ] } ->
+ case haver_open(Channel) of
+ ok -> haver_sendline(["OPEN", Channel]);
+ collision -> haver_sendline(["FAIL", "OPEN", "exists.channel",
Channel])
+ end, ok;
+ { line, [ "JOIN", Channel ] } ->
+ case haver_join(Channel) of
+ ok -> ok;
+ notfound -> haver_sendline(["FAIL", "JOIN", "unknown.channel",
Channel]);
+ collision -> haver_sendline(["FAIL", "JOIN", "already.joined",
Channel])
+ end, ok;
+ { line, [ "PART", Channel ] } ->
+ case haver_part(Channel) of
+ ok -> haver_sendline(["PART", Channel, get(uid)]);
+ notfound -> haver_sendline(["FAIL", "PART", "not.joined",
Channel])
+ end, ok;
+ { line, [ "TO", Uid | Message ] } ->
+ 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 ] } ->
+ haver_sendchan(Channel, Message);
+ { line, [ "LIST", Channel, Type ] } -> haver_list(Channel, Type), ok;
+ { line, [ "POKE" | L ] } -> haver_sendline(["OUCH" | L]);
+ { line, [ "BYE", Detail ] } ->
+ haver_sendline(["BYE", Detail]),
+ gen_tcp:close(get(socket)),
+ exit({ quit, "bye", Detail});
+ { line, [ L | T ] } ->
+ io:format("unmatched line: ~p~n", [L|T]),
+ haver_sendline(["FAIL", L, "unknown.cmd"]),
+ ok;
+ { line, L } ->
+ exit({err_line, L});
+ { sendline, L } -> haver_sendline(L), ok;
+ { privmsg, From, Message } ->
+ haver_sendline(["FROM", From | Message]),
+ ok;
+ Other -> io:format("unknown msg: ~p~n", Other), exit(Other)
+ end,
+ haver_mainloop().
Deleted: trunk/erlang/server/line_encode.erl
===================================================================
--- trunk/erlang/server/line_encode.erl 2005-12-09 20:52:40 UTC (rev 945)
+++ trunk/erlang/server/line_encode.erl 2005-12-09 21:42:22 UTC (rev 946)
@@ -1,29 +0,0 @@
--module(line_encode).
--export([decode/1, encode/1]).
-
-escape([]) -> [];
-escape([$\t|T]) -> [$\e, $t | escape(T)];
-escape([$\n|T]) -> [$\e, $n | escape(T)];
-escape([$\r|T]) -> [$\e, $r | escape(T)];
-escape([$\e|T]) -> [$\e, $e | escape(T)];
-escape([H|T]) -> [H | escape(T)].
-
-unescape([]) -> [];
-unescape([$\e, $t | T]) -> [$\t | unescape(T)];
-unescape([$\e, $e | T]) -> [$\e | unescape(T)];
-unescape([$\e, $r | T]) -> [$\r | unescape(T)];
-unescape([$\e, $n | T]) -> [$\n | unescape(T)];
-unescape([H|T]) -> [H | unescape(T)].
-
-decode([]) -> [];
-decode(L) ->
- { Pre, Post } = lists:splitwith(fun(C) -> C =/= $\t end, L),
- case Post of
- [] -> [Pre];
- [_|L2] ->
- ArgsAfter = decode(L2),
- [Pre|ArgsAfter]
- end.
-
-encode([]) -> [];
-encode([H|T]) -> lists:append(H, [$\t|encode(T)]).
Added: trunk/erlang/server/schema.hrl
===================================================================
--- trunk/erlang/server/schema.hrl 2005-12-09 20:52:40 UTC (rev 945)
+++ trunk/erlang/server/schema.hrl 2005-12-09 21:42:22 UTC (rev 946)
@@ -0,0 +1,4 @@
+-record(users, {name,
+ pid}).
+-record(channels, {name, notused}).
+-record(chanjoin, {channel, user}).
Modified: trunk/erlang/server/server.erl
===================================================================
--- trunk/erlang/server/server.erl 2005-12-09 20:52:40 UTC (rev 945)
+++ trunk/erlang/server/server.erl 2005-12-09 21:42:22 UTC (rev 946)
@@ -1,14 +1,11 @@
-module(server).
--export([cstart/1, start/1, stop/0, worker/2, haver_entry/2, setup_schema/0,
+-export([cstart/1, start/1, stop/0, worker/2, setup_schema/0,
clear_transients/0]).
+-import(haver_worker, []).
+-import(haver_protocol, [encode/1, decode/1]).
-include_lib("mnemosyne/include/mnemosyne.hrl").
+-include("schema.hrl").
--record(users, {name,
- pid}).
--record(channels, {name, notused}).
--record(chanjoin, {channel, user}).
-
-
setup_schema() ->
mnesia:delete_table(users),
mnesia:create_table(users, [
@@ -41,7 +38,11 @@
spawn_loop(LSock).
init_tables() ->
- mnesia:wait_for_tables([users, chanjoin, channels], 5 * 1000).
+ io:format("Waiting for table init...", []),
+ case mnesia:wait_for_tables([users, chanjoin, channels], 5 * 1000) of
+ ok -> io:format("Table init done.~n", []), ok;
+ R -> io:format("Table init failed: ~p~n", [R]), R
+ end.
start(Port) ->
case whereis(haver) of
@@ -58,7 +59,14 @@
end.
stop() -> haver ! shutdown.
-reap_child(Pid) ->
+match_reason({quit, Type, Detail}) -> {Type, Detail};
+match_reason({socket_closed, _}) -> {"closed", ""};
+match_reason(R) ->
+ io:format("Unknown quit-reason: ~p~n", [R]),
+ { "error", io_lib:format("~w", [R]) }.
+
+reap_child(Pid, Reason) ->
+ {Type, Detail} = match_reason(Reason),
F = fun() ->
Q = query
[ E || E <- table(users), E.pid = Pid ]
@@ -66,24 +74,33 @@
R = mnemosyne:eval(Q),
case R of
[ E ] ->
+ mnesia:delete_object(E),
Name = E#users.name,
+ SQ = query
+ [ U.pid || J <- table(chanjoin), J.user = Name,
+ J2 <- table(chanjoin), J.channel = J2.channel,
+ U <- table(users), U.name = J2.user
+ ] end,
+ SR = mnemosyne:eval(SQ),
+ io:format("SR=~p~n", [SR]),
Q2 = query
[ J || J <- table(chanjoin), J.user = Name ]
end,
R2 = mnemosyne:eval(Q2),
lists:map(fun(J) -> mnesia:delete_object(J) end, R2),
- mnesia:delete_object(E),
- ok;
- _ -> ok
+ { ok, Name, SR };
+ _ -> { nouser }
end end,
R = mnesia:transaction(F),
case R of
- { atomic, ok } -> { ok };
+ { atomic, { ok, Name, L } } ->
+ lists:map(fun(P) -> P ! { sendline, [ "QUIT", Name, Type, Detail ]
} end, L),
+ { ok };
+ { atomic, { nouser } } -> { ok };
_ -> io:format("Warning: reap failed for pid ~p~n: ~p~n", [Pid,R]),
{ nok, R }
end.
-
spawn_loop(LSock) ->
process_flag(trap_exit, true),
receive
@@ -93,7 +110,7 @@
shutdown -> ok;
{ 'EXIT', Pid, Reason } ->
io:format("Child ~p~n died with ~p~n", [Pid, Reason]),
- reap_child(Pid),
+ reap_child(Pid, Reason),
spawn_loop(LSock);
Other ->
io:format("Unknown message: ~p~n", Other)
@@ -105,7 +122,7 @@
{ok, Socket} ->
Parent ! next_worker,
peer_entry(Parent, Socket),
- reap_child(self()),
+ reap_child(self(), returned),
gen_tcp:close(Socket);
{error, Reason} ->
Parent ! next_worker,
@@ -113,7 +130,7 @@
end.
peer_entry(Parent, Socket) ->
- Pid = spawn_link(?MODULE, haver_entry, [Parent, Socket]),
+ Pid = spawn_link(haver_worker, entry, [Parent, Socket]),
peer(Socket, "", Pid).
peer(Socket, Buf, Pid) ->
@@ -133,253 +150,8 @@
case lists:splitwith(fun(C) -> not ((C == $\r) or (C == $\n)) end, Buf) of
{ _, [] } -> Buf;
{ L, Rem} ->
- Pid ! {line, line_encode:decode(L) },
+ Pid ! {line, haver_protocol:decode(L) },
Rem2 = lists:dropwhile(fun(C) -> ((C == $\r) or (C == $\n)) end,
Rem),
Rem2
end.
-haver_sendline(Line) ->
- Socket = get(socket),
- gen_tcp:send(Socket, line_encode:encode(Line)),
- gen_tcp:send(Socket, "\r\n").
-
-haver_entry(Parent, Socket) ->
- link(Parent),
- put(socket, Socket),
- receive
- { line, [ "HAVER", Vers | _ ] } ->
- haver_sendline(["HAVER", "localhost", "Erver/0.0"]),
- haver_login();
- { line, _ } ->
- gen_tcp:close(Socket),
- exit(rude_peer);
- Foo -> io:format("haver_entry unknown msg ~p~n", [Foo])
- end.
-
-handle_ident(Uid) ->
- F = fun() ->
- Q = query
- [ E || E <- table(users), E.name = Uid ]
- end,
- R = mnemosyne:eval(Q),
- io:format("~p~n", [R]),
- case R of
- [] -> mnesia:write(#users{name = Uid, pid = self()}),
- ok;
- _ -> collision
- end
- end,
- R = mnesia:transaction(F),
- case R of
- { atomic, ok } ->
- haver_sendline(["HELLO", Uid]),
- put(uid, Uid),
- haver_mainloop();
- { atomic, collision } ->
- haver_sendline(["FAIL", "IDENT", "exists.user", Uid]),
- haver_login();
- Wtf -> io:format("wtf: ~p~n", [Wtf])
- end.
-
-haver_login() ->
- receive
- { line, [ "IDENT", Uid ] } -> handle_ident(Uid);
- { line, _ } ->
- haver_sendline(["DIE", "ihateyou"]),
- gen_tcp:close(get(socket)),
- exit(rudepeer);
- M -> exit({unexpected, M})
- end.
-
-find_user(Uid) ->
- T = fun() ->
- Q = query
- [ U.pid || U <- table(users), U.name = Uid ]
- end,
- mnemosyne:eval(Q)
- end,
- R = mnesia:transaction(T),
- case R of
- { atomic, [Pid] } -> Pid;
- { atomic, [] } -> notfound;
- { atomic, Wtf } ->
- io:format("find_user wtf: ~p~n", [Wtf]),
- notfound;
- _ ->
- io:format("find_user err: ~p~n", [R]),
- notfound
- end.
-
-haver_open(Channel) ->
- T = fun() ->
- Q = query
- [ C || C <- table(channels), C.name = Channel ]
- end,
- R = mnemosyne:eval(Q),
- case R of
- [] -> mnesia:write(#channels{name = Channel, notused = Channel}),
- ok;
- _ -> collision
- end end,
- R = mnesia:transaction(T),
- case R of
- { atomic, Result } -> Result
- end.
-
-chan_broadcast(Channel, Message) ->
- T = fun() ->
- Q = query
- [ U.pid
- || U <- table(users),
- J <- table(chanjoin),
- U.name = J.user,
- J.channel = Channel
- ] end,
- mnemosyne:eval(Q)
- end,
- R = mnesia:transaction(T),
- case R of
- { atomic, L } -> lists:map(fun(P) -> P ! Message end, L);
- _ -> io:format("err, bad mnesia txn response ~p~n", [R]),
- exit({mnesia_fail, R})
- end.
-
-haver_join(Channel) ->
- Uid = get(uid),
- T = fun() ->
- case mnesia:read({channels, Channel}) of
- [] -> mnesia:abort(notfound);
- _ -> ok
- end,
- Q = query
- [ J || J <- table(chanjoin), J.user = Uid, J.channel = Channel ]
- end,
- case mnemosyne:eval(Q) of
- [_]->
- mnesia:abort(already_joined);
- [] ->
- mnesia:write(#chanjoin{channel = Channel, user = Uid}),
- ok
- end end,
- R = mnesia:transaction(T),
- case R of
- { aborted, already_joined } -> collision;
- { aborted, notfound } -> notfound;
- { atomic, ok } ->
- chan_broadcast(Channel, { sendline, [ "JOIN", Channel, Uid ] }),
- ok
- end.
-
-haver_part(Channel) ->
- Uid = get(uid),
- T = fun() ->
- Q = query
- [ J || J <- table(chanjoin), J.user = Uid, J.channel = Channel ]
- end,
- case mnemosyne:eval(Q) of
- [] -> notfound;
- [R]->
- io:format("R=~p~n", [R]),
- mnesia:delete_object(R), ok
- end end,
- R = mnesia:transaction(T),
- case R of
- { atomic, notfound } -> notfound;
- { atomic, ok } ->
- chan_broadcast(Channel, { sendline, [ "PART", Channel, Uid ] }),
- ok
- end.
-
-haver_getlist("&lobby", "channel") -> { ok, mnesia:dirty_all_keys(channels) };
-haver_getlist("&lobby", "user") -> { ok, mnesia:dirty_all_keys(users) };
-haver_getlist(Channel, "user") ->
- T = fun() ->
- case mnesia:read({channels, Channel}) of
- [] -> mnesia:abort(notfound);
- _ -> ok
- end,
- Records = mnesia:read({chanjoin, Channel}),
- Uids = lists:map(fun({chanjoin, _, Uid}) -> Uid end, Records),
- { ok, Uids }
- end,
- case mnesia:transaction(T) of
- { aborted, notfound } -> notfound;
- { atomic, {ok, Uids} } -> { ok, Uids };
- Other ->
- io:format("getlist(~p~n, \"user\") unexpected: ~p~n", [Channel,
Other]),
- exit(Other)
- end;
-haver_getlist(_, "channel") -> { ok, [] };
-haver_getlist(_, Type) -> {badtype, Type}.
-
-haver_list(C, T) ->
- R = haver_getlist(C, T),
- case R of
- { ok, Members } -> haver_sendline(["LIST", C, T | Members ]);
- notfound -> haver_sendline(["FAIL", "LIST", "unknown.channel", C]);
- badtype -> haver_sendline(["FAIL", "LIST", "unknown.type", C])
- end.
-
-haver_sendchan(Channel, Message) ->
- Uid = get(uid),
- T = fun() ->
- Q = query
- [ J || J <- table(chanjoin),
- J.user = Uid,
- J.channel = Channel
- ] end,
- case mnemosyne:eval(Q) of
- [] -> notjoined;
- _ -> ok
- end end,
- R = mnesia:transaction(T),
- case R of
- { atomic, notjoined } ->
- haver_sendline(["FAIL", "IN", "not.joined", Channel]);
- { atomic, ok } ->
- chan_broadcast(Channel, { sendline, ["IN", Channel, Uid | Message]
})
- end.
-
-haver_mainloop() ->
- V = receive
- { line, [ "OPEN", Channel ] } ->
- case haver_open(Channel) of
- ok -> haver_sendline(["OPEN", Channel]);
- collision -> haver_sendline(["FAIL", "OPEN", "exists.channel",
Channel])
- end, ok;
- { line, [ "JOIN", Channel ] } ->
- case haver_join(Channel) of
- ok -> ok;
- notfound -> haver_sendline(["FAIL", "JOIN", "unknown.channel",
Channel]);
- collision -> haver_sendline(["FAIL", "JOIN", "already.joined",
Channel])
- end, ok;
- { line, [ "PART", Channel ] } ->
- case haver_part(Channel) of
- ok -> haver_sendline(["PART", Channel, get(uid)]);
- notfound -> haver_sendline(["FAIL", "PART", "not.joined",
Channel])
- end, ok;
- { line, [ "TO", Uid | Message ] } ->
- 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 ] } ->
- haver_sendchan(Channel, Message);
- { line, [ "LIST", Channel, Type ] } -> haver_list(Channel, Type), ok;
- { line, [ "POKE" | L ] } -> haver_sendline(["OUCH" | L]);
- { line, ["DIE"]} ->
- [] = [1]; % XXX
- { line, [ L | T ] } ->
- io:format("unmatched line: ~p~n", [L|T]),
- haver_sendline(["FAIL", L, "unknown.cmd"]),
- ok;
- { line, L } ->
- exit({err_line, L});
- { sendline, L } -> haver_sendline(L), ok;
- { privmsg, From, Message } ->
- haver_sendline(["FROM", From | Message]),
- ok;
- Other -> io:format("unknown msg: ~p~n", Other), ok
- end,
- haver_mainloop().