privmsg between users; better cleanup on quit
This commit is contained in:
parent
5e2384b855
commit
4dc454e9a2
|
@ -1,11 +1,11 @@
|
||||||
open! Import
|
open! Import
|
||||||
|
module User = Router.User
|
||||||
|
|
||||||
type t = {
|
type t = {
|
||||||
addr : sockaddr;
|
addr : sockaddr;
|
||||||
user : Router.user;
|
user : User.t;
|
||||||
outbox : Irc.Msg.t Lwt_stream.t;
|
outbox : Irc.Msg.t Lwt_stream.t;
|
||||||
push_outbox : (Irc.Msg.t option -> unit);
|
push_outbox : (Irc.Msg.t option -> unit);
|
||||||
quit : unit Lwt_condition.t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let make ~(router : Router.t) ~(addr : sockaddr) : t =
|
let make ~(router : Router.t) ~(addr : sockaddr) : t =
|
||||||
|
@ -13,25 +13,22 @@ let make ~(router : Router.t) ~(addr : sockaddr) : t =
|
||||||
| ADDR_INET (ia, _) -> Unix.string_of_inet_addr ia
|
| ADDR_INET (ia, _) -> Unix.string_of_inet_addr ia
|
||||||
| ADDR_UNIX path -> path
|
| ADDR_UNIX path -> path
|
||||||
in
|
in
|
||||||
let user = Router.User.make router ~hostname in
|
let user = User.make router ~hostname in
|
||||||
let outbox, push_outbox = Lwt_stream.create () in
|
let outbox, push_outbox = Lwt_stream.create () in
|
||||||
{
|
{ addr; user; outbox; push_outbox }
|
||||||
addr; user; outbox; push_outbox;
|
|
||||||
quit = Lwt_condition.create ();
|
|
||||||
}
|
|
||||||
|
|
||||||
let quitting t = Lwt_condition.wait t.quit
|
let outbox t = Lwt_stream.choose [t.outbox; User.inbox t.user]
|
||||||
let outbox t = t.outbox
|
|
||||||
let send t msg = try t.push_outbox (Some msg) with Lwt_stream.Closed -> ()
|
let send t msg = try t.push_outbox (Some msg) with Lwt_stream.Closed -> ()
|
||||||
|
let close t = try t.push_outbox None with Lwt_stream.Closed -> ()
|
||||||
|
|
||||||
let cleanup t =
|
let cleanup t =
|
||||||
t.push_outbox None;
|
close t;
|
||||||
Router.User.cleanup t.user
|
User.cleanup t.user
|
||||||
|
|
||||||
(* message handling *)
|
(* message handling *)
|
||||||
|
|
||||||
let require_registered t f =
|
let require_registered t f =
|
||||||
if Router.User.is_registered t.user then
|
if User.is_registered t.user then
|
||||||
f (Option.get t.user.userinfo)
|
f (Option.get t.user.userinfo)
|
||||||
else
|
else
|
||||||
`notregistered
|
`notregistered
|
||||||
|
@ -41,18 +38,21 @@ let require_registered t f =
|
||||||
let attempt_to_register t =
|
let attempt_to_register t =
|
||||||
match t.user.nick, t.user.userinfo with
|
match t.user.nick, t.user.userinfo with
|
||||||
| Some nick, Some _userinfo ->
|
| Some nick, Some _userinfo ->
|
||||||
begin match Router.User.set_nick t.user nick with
|
begin match User.set_nick t.user nick with
|
||||||
| `nick_in_use -> `nicknameinuse nick
|
| `nick_in_use -> `nicknameinuse nick
|
||||||
| `nick_set ->
|
| `nick_set ->
|
||||||
Logs.debug (fun m -> m "init mode: +%a" Irc.Mode.pp t.user.mode);
|
Logs.debug (fun m -> m "init mode: +%a" Irc.Mode.pp t.user.mode);
|
||||||
|
User.send t.user
|
||||||
|
(Irc.Msg.make "MODE" [nick; Fmt.str "+%a" Irc.Mode.pp t.user.mode]
|
||||||
|
~prefix:(User_prefix (nick, None, None)));
|
||||||
(* TODO: set initial mode *)
|
(* TODO: set initial mode *)
|
||||||
`welcome
|
`welcome
|
||||||
end
|
end
|
||||||
| _, _ -> `ok
|
| _, _ -> `ok
|
||||||
|
|
||||||
let on_msg_nick t nick =
|
let on_msg_nick t nick =
|
||||||
if Router.User.is_registered t.user then
|
if User.is_registered t.user then
|
||||||
match Router.User.set_nick t.user nick with
|
match User.set_nick t.user nick with
|
||||||
| `nick_in_use -> `nicknameinuse nick
|
| `nick_in_use -> `nicknameinuse nick
|
||||||
| `nick_set -> `ok
|
| `nick_set -> `ok
|
||||||
else begin
|
else begin
|
||||||
|
@ -61,10 +61,10 @@ let on_msg_nick t nick =
|
||||||
end
|
end
|
||||||
|
|
||||||
let on_msg_user t username modestr realname =
|
let on_msg_user t username modestr realname =
|
||||||
if Router.User.is_registered t.user then
|
if User.is_registered t.user then
|
||||||
`alreadyregistered
|
`alreadyregistered
|
||||||
else begin
|
else begin
|
||||||
(* NB: +iw is automatically set, so it's impossible to actually affect the initial
|
(* NB: "+iw" is automatically set, so it's impossible to actually affect the initial
|
||||||
mode with the parameter to USER *)
|
mode with the parameter to USER *)
|
||||||
ignore modestr;
|
ignore modestr;
|
||||||
t.user.userinfo <- Some { username; realname };
|
t.user.userinfo <- Some { username; realname };
|
||||||
|
@ -73,13 +73,10 @@ let on_msg_user t username modestr realname =
|
||||||
|
|
||||||
(* > messages and channels *)
|
(* > messages and channels *)
|
||||||
|
|
||||||
let on_msg_privmsg t tgt msg _userinfo =
|
let on_msg_privmsg t tgt msg _ =
|
||||||
match Router.find_user t.user.router tgt with
|
match Router.find_user t.user.router tgt with
|
||||||
| None -> `nosuchnick tgt
|
| None -> `nosuchnick tgt
|
||||||
| Some _ ->
|
| Some dst -> Router.privmsg t.user (`user dst) msg; `ok
|
||||||
let _ = msg in
|
|
||||||
(* TODO: send messages *)
|
|
||||||
`tryagain
|
|
||||||
|
|
||||||
let on_msg_privmsg t tgt msg = require_registered t (on_msg_privmsg t tgt msg)
|
let on_msg_privmsg t tgt msg = require_registered t (on_msg_privmsg t tgt msg)
|
||||||
|
|
||||||
|
@ -88,8 +85,8 @@ let on_msg_privmsg t tgt msg = require_registered t (on_msg_privmsg t tgt msg)
|
||||||
let on_msg_quit t why =
|
let on_msg_quit t why =
|
||||||
let why = String.concat " " why in
|
let why = String.concat " " why in
|
||||||
Logs.debug (fun m -> m "%a: quit: %S" pp_sockaddr t.addr why);
|
Logs.debug (fun m -> m "%a: quit: %S" pp_sockaddr t.addr why);
|
||||||
Lwt_condition.broadcast t.quit ();
|
(* broadcast "quit" message *)
|
||||||
`ok
|
`quit
|
||||||
|
|
||||||
(* message sending *)
|
(* message sending *)
|
||||||
|
|
||||||
|
@ -105,13 +102,13 @@ let srv_motd_lines = [
|
||||||
|
|
||||||
let rpl t cmd params =
|
let rpl t cmd params =
|
||||||
let prefix = Irc.Msg.Server_prefix srv_host in
|
let prefix = Irc.Msg.Server_prefix srv_host in
|
||||||
let target = Router.User.nick t.user in
|
let target = User.nick t.user in
|
||||||
send t (Irc.Msg.make ~prefix cmd (target :: params))
|
send t (Irc.Msg.make ~prefix cmd (target :: params))
|
||||||
|
|
||||||
let rpl_tryagain t cmd = rpl t "263" [cmd; "Please wait a while and try again."]
|
let rpl_tryagain t cmd = rpl t "263" [cmd; "Please wait a while and try again."]
|
||||||
|
|
||||||
let rpl_welcome t =
|
let rpl_welcome t =
|
||||||
let who = Irc.Msg.prefix_string (Router.User.prefix t.user) in
|
let who = Irc.Msg.prefix_string (User.prefix t.user) in
|
||||||
begin
|
begin
|
||||||
rpl t "001" [Fmt.str "Welcome to the tali IRC network %s" who];
|
rpl t "001" [Fmt.str "Welcome to the tali IRC network %s" who];
|
||||||
rpl t "002" [Fmt.str "Your host is %s, running version %s" srv_host srv_ver];
|
rpl t "002" [Fmt.str "Your host is %s, running version %s" srv_host srv_ver];
|
||||||
|
@ -155,6 +152,7 @@ let on_msg t (msg : Irc.Msg.t) : unit =
|
||||||
in
|
in
|
||||||
match result with
|
match result with
|
||||||
| `ok -> ()
|
| `ok -> ()
|
||||||
|
| `quit -> close t
|
||||||
| `welcome -> rpl_welcome t; rpl_motd t
|
| `welcome -> rpl_welcome t; rpl_motd t
|
||||||
| `tryagain -> rpl_tryagain t msg.command
|
| `tryagain -> rpl_tryagain t msg.command
|
||||||
| `alreadyregistered -> err_alreadyregistered t
|
| `alreadyregistered -> err_alreadyregistered t
|
||||||
|
|
|
@ -63,15 +63,14 @@ let handle_client (router : Router.t) (conn_fd : fd) (conn_addr : sockaddr) =
|
||||||
in
|
in
|
||||||
let rd = Lwt_stream.iter (Connection.on_msg conn) (reader conn_fd) in
|
let rd = Lwt_stream.iter (Connection.on_msg conn) (reader conn_fd) in
|
||||||
let wr = writer conn_fd (Connection.outbox conn) in
|
let wr = writer conn_fd (Connection.outbox conn) in
|
||||||
let qt = Connection.quitting conn in
|
|
||||||
Lwt.finalize
|
Lwt.finalize
|
||||||
(fun () ->
|
(fun () ->
|
||||||
Logs.info (fun m -> m "new connection %a" pp_sockaddr conn_addr);
|
Logs.info (fun m -> m "new connection %a" pp_sockaddr conn_addr);
|
||||||
Lwt.choose [rd; wr; qt])
|
Lwt.choose [rd; wr])
|
||||||
(fun () ->
|
(fun () ->
|
||||||
Logs.info (fun m -> m "connection closed %a" pp_sockaddr conn_addr);
|
|
||||||
Connection.cleanup conn;
|
Connection.cleanup conn;
|
||||||
Lwt_unix.close conn_fd)
|
Lwt_unix.close conn_fd >|= fun () ->
|
||||||
|
Logs.info (fun m -> m "connection closed %a" pp_sockaddr conn_addr))
|
||||||
|
|
||||||
type config = {
|
type config = {
|
||||||
port : int;
|
port : int;
|
||||||
|
|
|
@ -2,11 +2,6 @@ type nick_key = Nick_key of string [@@unboxed]
|
||||||
let nick_key n = Nick_key (String.lowercase_ascii n) (* TODO: "scandinavian" lowercase *)
|
let nick_key n = Nick_key (String.lowercase_ascii n) (* TODO: "scandinavian" lowercase *)
|
||||||
let unset = Nick_key ""
|
let unset = Nick_key ""
|
||||||
|
|
||||||
(* TODO: notifications *)
|
|
||||||
(* type notif = *)
|
|
||||||
(* | Mode of Irc.nick * Irc.nick * Irc.Mode.t *)
|
|
||||||
(* | Nick of Irc.Msg.prefix * Irc.nick *)
|
|
||||||
|
|
||||||
type t = {
|
type t = {
|
||||||
users : (nick_key, user) Hashtbl.t
|
users : (nick_key, user) Hashtbl.t
|
||||||
(* TODO: channels *)
|
(* TODO: channels *)
|
||||||
|
@ -19,8 +14,8 @@ and user = {
|
||||||
mutable nick : Irc.nick option;
|
mutable nick : Irc.nick option;
|
||||||
mutable userinfo : Irc.userinfo option;
|
mutable userinfo : Irc.userinfo option;
|
||||||
mutable mode : Irc.Mode.t;
|
mutable mode : Irc.Mode.t;
|
||||||
(* inbox : notif Lwt_stream.t; *)
|
inbox : Irc.Msg.t Lwt_stream.t;
|
||||||
(* push_inbox : (notif option -> unit); *)
|
push_inbox : (Irc.Msg.t option -> unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
let make () =
|
let make () =
|
||||||
|
@ -33,7 +28,7 @@ module User = struct
|
||||||
type t = user
|
type t = user
|
||||||
|
|
||||||
let make router ~hostname =
|
let make router ~hostname =
|
||||||
(* let inbox, push_inbox = Lwt_stream.create () in *)
|
let inbox, push_inbox = Lwt_stream.create () in
|
||||||
{
|
{
|
||||||
router;
|
router;
|
||||||
hostname;
|
hostname;
|
||||||
|
@ -41,11 +36,14 @@ module User = struct
|
||||||
nick = None;
|
nick = None;
|
||||||
userinfo = None;
|
userinfo = None;
|
||||||
mode = Irc.Mode.of_string "iw";
|
mode = Irc.Mode.of_string "iw";
|
||||||
(* inbox; push_inbox; *)
|
inbox; push_inbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nick t = Option.value t.nick ~default:"*"
|
let inbox t = t.inbox
|
||||||
|
let send t msg = try t.push_inbox (Some msg) with Lwt_stream.Closed -> ()
|
||||||
|
let close t = try t.push_inbox None with Lwt_stream.Closed -> ()
|
||||||
|
|
||||||
|
let nick t = Option.value t.nick ~default:"*"
|
||||||
let prefix t : Irc.Msg.prefix =
|
let prefix t : Irc.Msg.prefix =
|
||||||
match t.nick with
|
match t.nick with
|
||||||
| None -> No_prefix
|
| None -> No_prefix
|
||||||
|
@ -69,7 +67,15 @@ module User = struct
|
||||||
`nick_set
|
`nick_set
|
||||||
end
|
end
|
||||||
|
|
||||||
let cleanup user =
|
let cleanup t =
|
||||||
unset_nick user
|
(* TODO: notify other users of quit *)
|
||||||
(* user.push_inbox None *)
|
close t;
|
||||||
|
unset_nick t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let privmsg src dst txt =
|
||||||
|
match dst with
|
||||||
|
| `user dst ->
|
||||||
|
let prefix = User.prefix src in
|
||||||
|
let msg = Irc.Msg.make "PRIVMSG" [User.nick dst; txt] ~prefix in
|
||||||
|
User.send dst msg
|
||||||
|
|
Loading…
Reference in New Issue