From 2444ac841fad00133f231e9667ab427c4ce4d39e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 5 Oct 2010 01:44:25 +0200 Subject: Receiving messages works again (kinda) --- src/core/CMakeLists.txt | 2 + src/core/ephraim_account.erl | 2 + src/core/ephraim_conn.erl | 2 - src/core/ephraim_conv.erl | 51 ++++++++++++++++++---- src/core/ephraim_conv_handler.erl | 71 ++++++++++++++++++++++++++++++ src/core/ephraim_conv_man.erl | 12 ++++++ src/core/ephraim_gui.erl | 2 +- src/core/ephraim_roster.erl | 2 + src/gui/Conversation.vala | 2 +- src/gui/CoreConnector.vala | 90 ++++++++++++++++++++++----------------- src/gui/Ephraim.vala | 22 +++++----- 11 files changed, 194 insertions(+), 64 deletions(-) create mode 100644 src/core/ephraim_conv_handler.erl create mode 100644 src/core/ephraim_conv_man.erl diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9baaeee..97c7d4e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,6 +6,8 @@ erl_target(ephraim-core ephraim_config.erl ephraim_conn.erl ephraim_conv.erl + ephraim_conv_handler.erl + ephraim_conv_man.erl ephraim_gui.erl ephraim_roster.erl ephraim_roster_handler.erl diff --git a/src/core/ephraim_account.erl b/src/core/ephraim_account.erl index d79e118..8024d68 100644 --- a/src/core/ephraim_account.erl +++ b/src/core/ephraim_account.erl @@ -9,6 +9,8 @@ start_link() -> {ok, EventManager} = supervisor:start_child(Pid, {event_man, {gen_event, start_link, []}, permanent, 1000, worker, [gen_event]}), ephraim_account_event_proxy:start(EventManager), + {ok, _} = supervisor:start_child(Pid, {conv_man, {ephraim_conv_man, start_link, [EventManager]}, permanent, 1000, supervisor, [ephraim_conv_man]}), + {ok, Connection} = supervisor:start_child(Pid, {connection, {ephraim_conn, start_link, [EventManager]}, permanent, 10000, worker, [ephraim_conn]}), {ok, _} = supervisor:start_child(Pid, {roster, {ephraim_roster, start_link, [EventManager, Connection]}, permanent, 1000, worker, [ephraim_roster]}), {ok, Pid}. diff --git a/src/core/ephraim_conn.erl b/src/core/ephraim_conn.erl index f4e14e0..fa9344d 100644 --- a/src/core/ephraim_conn.erl +++ b/src/core/ephraim_conn.erl @@ -37,8 +37,6 @@ session(Session) -> exmpp_session:login(Session), io:format("Getting profile...~n"), exmpp_session:send_packet(Session, exmpp_iq:get('jabber:client', #xmlel{ns='vcard-temp',name='vCard'})), - io:format("Getting roster...~n"), - exmpp_session:send_packet(Session, exmpp_client_roster:get_roster("foo")), io:format("Setting presence...~n"), exmpp_session:send_packet(Session, exmpp_presence:set_status(exmpp_presence:available(), "Foo/Test\\Bar")), io:format("Ok.~n"). diff --git a/src/core/ephraim_conv.erl b/src/core/ephraim_conv.erl index 3342bc9..d9431b2 100644 --- a/src/core/ephraim_conv.erl +++ b/src/core/ephraim_conv.erl @@ -1,18 +1,51 @@ -module(ephraim_conv). --compile([debug_info, export_all]). +-behaviour(gen_server). + +-export([start_link/2]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([received_message/4]). -record(conv_state, { - my_jid :: binary(), - jid :: binary() + conv_ref :: reference(), + event_manager :: pid(), + my_jid :: binary(), + jid :: binary() }). --spec init(binary()) -> ok. -init(JID) -> - io:format("Starting a conversation with ~p~n", [JID]), - +-spec start_link(pid(), binary()) -> ok. +start_link(EventManager, JID) -> + ConvRef = make_ref(), + io:format("Starting conversation ~p with ~p~n", [ConvRef, JID]), + + gen_event:notify(EventManager, {view_update, {conversation, ConvRef, {new, JID}}}), + {jid,MyJID} = ephraim_config:get(jid), - loop(#conv_state{my_jid=list_to_binary(MyJID),jid=JID}), - io:format("Stopping a conversation with ~p~n", [JID]). + gen_server:start_link(?MODULE, #conv_state{conv_ref=ConvRef,event_manager=EventManager,my_jid=list_to_binary(MyJID),jid=JID}, []). + +init(State) -> + process_flag(trap_exit, true), + {ok, State}. + +received_message(Conv, From, Type, Body) -> + gen_server:cast(Conv, {received_message, From, Type, Body}). + +handle_call(_Msg, _From, State) -> + {noreply, State}. + +handle_cast({received_message, From, Type, Body}, State) -> + gen_event:notify(State#conv_state.event_manager, {view_update, {conversation, State#conv_state.conv_ref, {message, From, Type, Body}}}), + {noreply, State}; +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Msg, State) -> + {ok, State}. +terminate(_Reason, State) -> + io:format("Stopping conversation ~p with ~p~n", [State#conv_state.conv_ref, State#conv_state.jid]), + ok. +code_change(_OldVersion, State, _Extra) -> + {ok, State}. + -spec loop(#conv_state{}) -> ok. loop(State) -> diff --git a/src/core/ephraim_conv_handler.erl b/src/core/ephraim_conv_handler.erl new file mode 100644 index 0000000..e525d99 --- /dev/null +++ b/src/core/ephraim_conv_handler.erl @@ -0,0 +1,71 @@ +-module(ephraim_conv_handler). +-include_lib("exmpp/include/exmpp_client.hrl"). +-behaviour(gen_event). +-export([start/2]). +-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). + +-record(conv_handler_state, { + supervisor :: pid(), + event_manager :: pid(), + convs = dict:new() :: dict() + }). + +start(Supervisor, EventManager) -> + gen_event:add_handler(EventManager, ?MODULE, {Supervisor, EventManager}). + +init({Supervisor, EventManager}) -> + State = #conv_handler_state{supervisor=Supervisor,event_manager=EventManager}, + {ok, State}. + +-spec get_conv(#conv_handler_state{},exmpp_jid:jid()) -> {#conv_handler_state{},pid()|undefined}. +get_conv(State, JID) -> + {Node, Domain, Resource} = exmpp_jid:to_lower(exmpp_jid:parse(JID)), + + case Resource of + undefined -> + {State, undefined}; + _ -> + Key = list_to_binary([Node, <<"@">>, Domain, <<"/">>, Resource]), + + case dict:find(Key, State#conv_handler_state.convs) of + {ok, Conv} -> + {State, Conv}; + error -> + {ok, Conv} = supervisor:start_child(State#conv_handler_state.supervisor, + {Key, {ephraim_conv, start_link, [State#conv_handler_state.event_manager, JID]}, transient, 1000, worker, [ephraim_conv]}), + Dict = dict:store(Key, Conv, State#conv_handler_state.convs), + {State#conv_handler_state{convs=Dict}, Conv} + end + end. + +handle_event({received_packet, #received_packet{packet_type=message, raw_packet=Packet}}, State) -> + From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>), + Type = exmpp_message:get_type(Packet), + Body = exmpp_message:get_body(Packet), + NewState = if + Body =/= undefined -> + {State2, Conv} = get_conv(State, From), + if + Conv =/= undefined -> + ephraim_conv:received_message(Conv, From, Type, Body); + true -> + ok + end, + State2; + true -> + io:format("Received strange message from ~p:~n~p~n", [From, Packet]), + State + end, + {ok, NewState}; + +handle_event(_Event, State) -> + {ok, State}. + +handle_call(_Msg, State) -> + {noreply, State}. +handle_info(_Msg, State) -> + {ok, State}. +terminate(_Reason, _State) -> + ok. +code_change(_OldVersion, State, _Extra) -> + {ok, State}. diff --git a/src/core/ephraim_conv_man.erl b/src/core/ephraim_conv_man.erl new file mode 100644 index 0000000..617ccc4 --- /dev/null +++ b/src/core/ephraim_conv_man.erl @@ -0,0 +1,12 @@ +-module(ephraim_conv_man). +-behaviour(supervisor). +-export([start_link/1]). +-export([init/1]). + +start_link(EventManager) -> + {ok, Pid} = supervisor:start_link(?MODULE, []), + ephraim_conv_handler:start(Pid, EventManager), + {ok, Pid}. + +init(_Args) -> + {ok, {{one_for_one, 0, 1}, []}}. diff --git a/src/core/ephraim_gui.erl b/src/core/ephraim_gui.erl index 92ab521..7bf4b74 100644 --- a/src/core/ephraim_gui.erl +++ b/src/core/ephraim_gui.erl @@ -30,7 +30,7 @@ handle_info({Port, {data, Data}}, Port) -> init:stop(), remove_handler; _ -> - ephraim:notify({view_event, Msg}), + ephraim:notify({view_request, Msg}), {ok, Port} end; diff --git a/src/core/ephraim_roster.erl b/src/core/ephraim_roster.erl index f111f38..605d019 100644 --- a/src/core/ephraim_roster.erl +++ b/src/core/ephraim_roster.erl @@ -40,6 +40,8 @@ init({EventManager, Connection}) -> process_flag(trap_exit, true), ephraim_roster_handler:start(EventManager, self()), + io:format("Getting roster...~n"), + ephraim_conn:send_packet(Connection, exmpp_client_roster:get_roster("foo")), {ok, #roster_state{event_manager=EventManager,connection=Connection,entries=dict:new()}}. -spec updateResource(#roster_state{}, binary(), integer(), atom(), atom(), binary()) -> #roster_state{}. diff --git a/src/gui/Conversation.vala b/src/gui/Conversation.vala index 7c8dc03..926ed3d 100644 --- a/src/gui/Conversation.vala +++ b/src/gui/Conversation.vala @@ -62,7 +62,7 @@ public class Conversation { conversations.remove(widget); } - public void chat_message(string type, string from, string message) { + public void chat_message(string from, string type, string message) { if(type != "chat") return; diff --git a/src/gui/CoreConnector.vala b/src/gui/CoreConnector.vala index 4c853ff..46c94f2 100644 --- a/src/gui/CoreConnector.vala +++ b/src/gui/CoreConnector.vala @@ -1,25 +1,27 @@ public class CoreConnector { - static Eva.Term match_gui_init; + static Eva.Term match_init_view; static Eva.Term match_roster_update; static Eva.Term match_resource_entry; static Eva.Term match_avatar; - static Eva.Term match_new_conversation; - static Eva.Term match_chat_message; + static Eva.Term match_conversation; + static Eva.Term match_conversation_new; + static Eva.Term match_conversation_message; static construct { - match_gui_init = Eva.parse("{global,{gui_init,Terms}}"); + match_init_view = Eva.parse("{global,{init_view,Terms}}"); match_roster_update = Eva.parse("{account,Account,{roster_update,JID,Name,Subscription,Groups,Resources,Avatar}}"); match_resource_entry = Eva.parse("{Name,{resource_entry,Priority,Show,Status}}"); match_avatar = Eva.parse("{avatar,Data}"); - match_new_conversation = Eva.parse("{new_conversation,JID}"); - match_chat_message = Eva.parse("{chat_message,JID,Type,From,Body}"); + match_conversation = Eva.parse("{account,Account,{conversation,Ref,Message}}"); + match_conversation_new = Eva.parse("{new,JID}"); + match_conversation_message = Eva.parse("{message,From,Type,Body}"); } Eva.PacketHandler con; public signal void update_contact(Contact contact); - public signal void new_conversation(string jid); - public signal void chat_message(string jid, string from, string type, string message); + public signal void new_conversation(Eva.Ref conv_ref, string jid); + public signal void chat_message(Eva.Ref conv_ref, string from, string type, string message); public void start() { con = new Eva.PacketHandler(new UnixInputStream(3, true), new UnixOutputStream(4, true), 4); @@ -43,7 +45,7 @@ public class CoreConnector { private void handle_term(Eva.Term term) { Gee.Map match; - if((match = term.match(match_gui_init)) != null) { + if((match = term.match(match_init_view)) != null) { Eva.Cons terms = match["Terms"] as Eva.Cons; while(terms != null) { @@ -175,46 +177,56 @@ public class CoreConnector { } update_contact(contact); } - else if((match = term.match(match_new_conversation)) != null) { - Eva.Binary jid_term = match["JID"] as Eva.Binary; - if(jid_term == null) { + else if((match = term.match(match_conversation)) != null) { + Eva.Ref conv_ref = match["Ref"] as Eva.Ref; + if(conv_ref == null) { warn_if_reached(); return; } - string jid = from_utf8(jid_term); - - new_conversation(jid); - } - else if((match = term.match(match_chat_message)) != null) { - Eva.Binary jid_term = match["JID"] as Eva.Binary; - if(jid_term == null) { + Eva.Term message_term = match["Message"]; + if(message_term == null) { warn_if_reached(); return; } - string jid = from_utf8(jid_term); - - Eva.Atom type_term = match["Type"] as Eva.Atom; - if(type_term == null) { - warn_if_reached(); - return; + + Gee.Map cmatch; + if((cmatch = message_term.match(match_conversation_new)) != null) { + Eva.Binary jid_term = cmatch["JID"] as Eva.Binary; + if(jid_term == null) { + warn_if_reached(); + return; + } + + string jid = from_utf8(jid_term); + new_conversation(conv_ref, jid); } - string type = type_term.value; + else if((cmatch = message_term.match(match_conversation_message)) != null) { + Eva.Binary from_term = cmatch["From"] as Eva.Binary; + if(from_term == null) { + warn_if_reached(); + return; + } + string from = from_utf8(from_term); - Eva.Binary from_term = match["From"] as Eva.Binary; - if(from_term == null) { - warn_if_reached(); - return; - } - string from = from_utf8(from_term); + Eva.Atom type_term = cmatch["Type"] as Eva.Atom; + if(type_term == null) { + warn_if_reached(); + return; + } + string type = type_term.value; - Eva.Binary body_term = match["Body"] as Eva.Binary; - if(body_term == null) { - warn_if_reached(); - return; - } - string body = from_utf8(body_term); + Eva.Binary body_term = cmatch["Body"] as Eva.Binary; + if(body_term == null) { + warn_if_reached(); + return; + } + string body = from_utf8(body_term); - chat_message(jid, type, from, body); + chat_message(conv_ref, from, type, body); + } + else { + stdout.printf("Received unhandled term: %s\n", term.to_string()); + } } else { stdout.printf("Received unhandled term: %s\n", term.to_string()); diff --git a/src/gui/Ephraim.vala b/src/gui/Ephraim.vala index 5801966..fb5e77f 100644 --- a/src/gui/Ephraim.vala +++ b/src/gui/Ephraim.vala @@ -17,7 +17,7 @@ public class Ephraim { unowned Gtk.TreeView rosterView = builder.get_object("Roster") as Gtk.TreeView; - Gee.TreeMap conversations = new Gee.TreeMap(); + Gee.TreeMap conversations = new Gee.TreeMap(); unowned Gtk.Notebook conversationNotebook = builder.get_object("Conversations") as Gtk.Notebook; ContactList roster = new ContactList(rosterView); @@ -28,24 +28,22 @@ public class Ephraim { coreconn.update_contact.connect(roster.update_contact); - coreconn.new_conversation.connect((jid) => { + coreconn.new_conversation.connect((conv_ref, jid) => { Contact contact = roster.get_contact(jid); - if(contact != null) { - conversations[jid] = new Conversation(conversationNotebook, jid, contact.display_string); - } - else { - conversations[jid] = new Conversation(conversationNotebook, jid, null); - } + Conversation conv = new Conversation(conversationNotebook, jid, contact != null ? contact.display_string : null); - conversations[jid].send_message.connect((type, message) => coreconn.send_message(jid, type, message)); + conversations[conv_ref] = conv; + conv.send_message.connect((type, message) => coreconn.send_message(jid, type, message)); }); - coreconn.chat_message.connect((jid, type, from, message) => { - if(!(jid in conversations.keys)) + coreconn.chat_message.connect((conv_ref, from, type, message) => { + if(!(conv_ref in conversations.keys)) { + warn_if_reached(); return; + } - conversations[jid].chat_message(type, from, message); + conversations[conv_ref].chat_message(from, type, message); }); //if(!coreconn.start()) -- cgit v1.2.3