diff --git a/ircd/src/ejabberd_ircd.erl b/ircd/src/ejabberd_ircd.erl index 21482e5..555ad86 100644 --- a/ircd/src/ejabberd_ircd.erl +++ b/ircd/src/ejabberd_ircd.erl @@ -84,7 +84,7 @@ socket_type() -> %% {stop, StopReason} %%---------------------------------------------------------------------- init([{SockMod, Socket}, Opts]) -> - iconv:start(), + %iconv:start(), Access = case lists:keysearch(access, 1, Opts) of {value, {_, A}} -> A; _ -> all @@ -134,7 +134,8 @@ init([{SockMod, Socket}, Opts]) -> }}. handle_info({tcp, _Socket, Line}, StateName, StateData) -> - DecodedLine = iconv:convert(StateData#state.encoding, "utf-8", Line), + %DecodedLine = iconv:convert(StateData#state.encoding, "utf-8", Line), + DecodedLine = Line, Parsed = parse_line(DecodedLine), ?MODULE:StateName({line, Parsed}, StateData); handle_info({tcp_closed, _}, _StateName, StateData) -> @@ -163,8 +164,8 @@ terminate(_Reason, _StateName, #state{socket = Socket, sockmod = SockMod, none -> ok; _ -> - Packet = {xmlelement, "presence", - [{"type", "unavailable"}], []}, + Packet = {xmlel, <<"presence">>, + [{<<"type">>, <<"unavailable">>}], []}, FromJID = user_jid(State), ?DICT:map(fun(ChannelJID, _ChannelData) -> ejabberd_router:route(FromJID, ChannelJID, Packet) @@ -186,8 +187,10 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) -> Nick = hd(Params), Pass = State#state.pass, Server = State#state.host, + ?DEBUG("user=~p server=~p", [Nick, Server]), - JID = jlib:make_jid(Nick, Server, "irc"), + JID = jlib:make_jid(list_to_binary(Nick), Server, <<"irc">>), + ?DEBUG("JID=~p", [JID]), case JID of error -> ?DEBUG("invalid nick '~p'", [Nick]), @@ -200,7 +203,7 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) -> send_reply('ERR_NICKCOLLISION', [Nick, "Nickname collision"], State), {next_state, wait_for_nick, State}; allow -> - case ejabberd_auth:check_password(Nick, Server, Pass) of + case ejabberd_auth:check_password(list_to_binary(Nick), Server, list_to_binary(Pass)) of false -> ?DEBUG("auth failed for '~p'", [Nick]), send_reply('ERR_NICKCOLLISION', [Nick, "Authentication failed"], State), @@ -209,15 +212,15 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) -> ?DEBUG("good nickname '~p'", [Nick]), SID = {now(), self()}, ejabberd_sm:open_session( - SID, Nick, Server, "irc", peerip(gen_tcp, State#state.socket)), - ejabberd_sm:set_presence(SID, Nick, Server, "irc", + SID, list_to_binary(Nick), Server, <<"irc">>, peerip(gen_tcp, State#state.socket)), + ejabberd_sm:set_presence(SID, list_to_binary(Nick), Server, <<"irc">>, 3, "undefined", [{'ip', peerip(gen_tcp, State#state.socket)}, {'conn','c2s'}, {'state',"+"}]), send_text_command("", "001", [Nick, "IRC interface of ejabberd server "++Server], State), - send_reply('RPL_MOTDSTART', [Nick, "- "++Server++" Message of the day - "], State), - send_reply('RPL_MOTD', [Nick, "- This is the IRC interface of the ejabberd server "++Server++"."], State), - send_reply('RPL_MOTD', [Nick, "- Your full JID is "++Nick++"@"++Server++"/irc."], State), - send_reply('RPL_MOTD', [Nick, "- Channel #whatever corresponds to MUC room whatever@"++State#state.muc_host++"."], State), + send_reply('RPL_MOTDSTART', [Nick, "- "++binary_to_list(Server)++" Message of the day - "], State), + send_reply('RPL_MOTD', [Nick, "- This is the IRC interface of the ejabberd server "++binary_to_list(Server)++"."], State), + send_reply('RPL_MOTD', [Nick, "- Your full JID is "++Nick++"@"++binary_to_list(Server)++"/irc."], State), + send_reply('RPL_MOTD', [Nick, "- Channel #whatever corresponds to MUC room whatever@"++binary_to_list(State#state.muc_host)++"."], State), send_reply('RPL_MOTD', [Nick, "- This IRC interface is quite immature. You will probably find bugs."], State), send_reply('RPL_MOTD', [Nick, "- Have a good time!"], State), send_reply('RPL_ENDOFMOTD', [Nick, "End of /MOTD command"], State), @@ -244,6 +247,7 @@ wait_for_cmd({line, #line{command = "USER", params = [_Username, _Hostname, _Ser %% Yeah, like we care. {next_state, wait_for_cmd, State}; wait_for_cmd({line, #line{command = "JOIN", params = Params}}, State) -> + ?DEBUG("received JOIN ~p", [Params]), {ChannelsString, KeysString} = case Params of [C, K] -> @@ -253,7 +257,9 @@ wait_for_cmd({line, #line{command = "JOIN", params = Params}}, State) -> end, Channels = string:tokens(ChannelsString, ","), Keys = string:tokens(KeysString, ","), + ?DEBUG("joining channels ~p", [Channels]), NewState = join_channels(Channels, Keys, State), + ?DEBUG("joined channels ~p", [Channels]), {next_state, wait_for_cmd, NewState}; %% USERHOST command @@ -294,18 +300,18 @@ wait_for_cmd({line, #line{command = "PRIVMSG", params = [To, Text]}}, State) -> fun(Rcpt) -> case Rcpt of [$# | Roomname] -> - Packet = {xmlelement, "message", - [{"type", "groupchat"}], - [{xmlelement, "body", [], + Packet = {xmlel, <<"message">>, + [{<<"type">>, <<"groupchat">>}], + [{xmlel, <<"body">>, [], filter_cdata(translate_action(Text))}]}, ToJID = channel_to_jid(Roomname, State), ejabberd_router:route(FromJID, ToJID, Packet); _ -> case string:tokens(Rcpt, "#") of [Nick, Channel] -> - Packet = {xmlelement, "message", - [{"type", "chat"}], - [{xmlelement, "body", [], + Packet = {xmlel, <<"message">>, + [{<<"type">>, <<"chat">>}], + [{xmlel, <<"body">>, [], filter_cdata(translate_action(Text))}]}, ToJID = channel_nick_to_jid(Nick, Channel, State), ejabberd_router:route(FromJID, ToJID, Packet); @@ -357,9 +363,9 @@ wait_for_cmd({line, #line{command = "TOPIC", params = Params}}, State) -> end; [Channel, NewTopic] -> Packet = - {xmlelement, "message", - [{"type", "groupchat"}], - [{xmlelement, "subject", [], filter_cdata(NewTopic)}]}, + {xmlel, <<"message">>, + [{<<"type">>, <<"groupchat">>}], + [{xmlel, <<"subject">>, [], filter_cdata(NewTopic)}]}, FromJID = user_jid(State), ToJID = channel_to_jid(Channel, State), ejabberd_router:route(FromJID, ToJID, Packet) @@ -411,10 +417,11 @@ wait_for_cmd({line, #line{command = Unknown, params = Params} = Line}, State) -> Unknown ++ "/" ++ integer_to_list(length(Params))], State), {next_state, wait_for_cmd, State}; -wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, State) -> +wait_for_cmd({route, From, _To, {xmlel, <<"presence">>, Attrs, Els} = El}, State) -> + ?DEBUG("Received a Presence ~p ~p ~p", [From, _To, El]), Type = xml:get_attr_s("type", Attrs), FromRoom = jlib:jid_remove_resource(From), - FromNick = From#jid.resource, + FromNick = binary_to_list(From#jid.resource), Channel = jid_to_channel(From, State), MyNick = State#state.nick, @@ -422,24 +429,29 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat Joining = ?DICT:find(FromRoom, State#state.joining), Joined = ?DICT:find(FromRoom, State#state.joined), + ?DEBUG("JoinState ~p ~p ~p", [Joining, Joined, Type]), case {Joining, Joined, Type} of - {{ok, BufferedNicks}, _, ""} -> + {{ok, BufferedNicks}, _, <<"">>} -> + ?DEBUG("BufferedNicks ~p", [BufferedNicks]), case BufferedNicks of [] -> %% If this is the first presence, tell the %% client that it's joining. - send_command(make_irc_sender(MyNick, FromRoom, State), - "JOIN", [Channel], State); + ?DEBUG("Sending Command ~p ~p", [IRCSender, Channel]), + send_command(IRCSender, "JOIN", [Channel], State), + ?DEBUG("Command Sent", []); _ -> ok end, + ?DEBUG("Getting NewRole", []), NewRole = case find_el("x", ?NS_MUC_USER, Els) of nothing -> ""; XMucEl -> xml:get_path_s(XMucEl, [{elem, "item"}, {attr, "role"}]) end, + ?DEBUG("NewRole ~p", [NewRole]), NewBufferedNicks = [{FromNick, NewRole} | BufferedNicks], ?DEBUG("~s is present in ~s. we now have ~p.", [FromNick, Channel, NewBufferedNicks]), @@ -479,14 +491,14 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat State#state{joining = NewJoining} end, {next_state, wait_for_cmd, NewState}; - {{ok, _BufferedNicks}, _, "error"} -> + {{ok, _BufferedNicks}, _, <<"error">>} -> NewState = case FromNick of MyNick -> %% we couldn't join the room {ReplyCode, ErrorDescription} = case xml:get_subtag(El, "error") of - {xmlelement, _, _, _} = ErrorEl -> + {xmlel, _, _, _} = ErrorEl -> {ErrorName, ErrorText} = parse_error(ErrorEl), {case ErrorName of "forbidden" -> 'ERR_INVITEONLYCHAN'; @@ -511,7 +523,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat end, {next_state, wait_for_cmd, NewState}; %% Presence in a channel we have already joined - {_, {ok, _}, ""} -> + {_, {ok, _}, <<"">>} -> %% Someone enters send_command(IRCSender, "JOIN", [Channel], State), {next_state, wait_for_cmd, State}; @@ -524,29 +536,37 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat {next_state, wait_for_cmd, State} end; -wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State) -> - Type = xml:get_attr_s("type", Attrs), +wait_for_cmd({route, From, _To, {xmlel, <<"message">>, Attrs, Els} = El}, State) -> + ?DEBUG("Got a Message! ~p ~p ~p", [From, _To, El]), + Type = xml:get_attr_s(<<"type">>, Attrs), case Type of - "groupchat" -> + <<"groupchat">> -> + ?DEBUG("It's a groupchat", []), ChannelJID = jlib:jid_remove_resource(From), case ?DICT:find(ChannelJID, State#state.joined) of {ok, #channel{} = ChannelData} -> FromChannel = jid_to_channel(From, State), - FromNick = From#jid.resource, - Subject = xml:get_path_s(El, [{elem, "subject"}, cdata]), - Body = xml:get_path_s(El, [{elem, "body"}, cdata]), - XDelay = lists:any(fun({xmlelement, "x", XAttrs, _}) -> - xml:get_attr_s("xmlns", XAttrs) == ?NS_DELAY; + FromNick = binary_to_list(From#jid.resource), + Subject = xml:get_path_s(El, [{elem, <<"subject">>}, cdata]), + Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]), + ?DEBUG("Message Data ~p ~p", [Subject, Body]), + XDelay = lists:any(fun({xmlel, <<"x">>, XAttrs, _}) -> + xml:get_attr_s(<<"xmlns">>, XAttrs) == ?NS_DELAY; (_) -> false end, Els), + ?DEBUG("XDelay ~p", [XDelay]), if - Subject /= "" -> + Subject /= <<"">> -> + ?DEBUG("Cleaning Subject!", []), CleanSubject = lists:map(fun($\n) -> $\ ; (C) -> C - end, Subject), - send_text_command(make_irc_sender(From, State), + end, binary_to_list(Subject)), + ?DEBUG("CleanSubject ~p", [CleanSubject]), + IRCSender = make_irc_sender(From, State), + ?DEBUG("IRCSender ~p", [IRCSender]), + send_text_command(IRCSender, "TOPIC", [FromChannel, CleanSubject], State), NewChannelData = ChannelData#channel{topic = CleanSubject}, NewState = State#state{joined = ?DICT:store(jlib:jid_remove_resource(From), NewChannelData, State#state.joined)}, @@ -554,9 +574,11 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State not XDelay, FromNick == State#state.nick -> %% there is no message echo in IRC. %% we let the backlog through, though. + ?DEBUG("Don't care about it", []), {next_state, wait_for_cmd, State}; true -> - BodyLines = string:tokens(Body, "\n"), + ?DEBUG("Send it to someone!", []), + BodyLines = string:tokens(binary_to_list(Body), "\n"), lists:foreach( fun(Line) -> Line1 = @@ -576,7 +598,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State [jlib:jid_to_string(ChannelJID)]), {next_state, wait_for_cmd, State} end; - "error" -> + <<"error">> -> MucHost = State#state.muc_host, ErrorFrom = case From of @@ -595,7 +617,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State %% I think this should cover all possible combinations of %% XMPP and non-XMPP error messages... ErrorText = - error_to_string(xml:get_subtag(El, "error")), + error_to_string(xml:get_subtag(El, <<"error">>)), send_text_command("", "NOTICE", [State#state.nick, "Message to "++ErrorFrom++" bounced: "++ ErrorText], State), @@ -604,8 +626,8 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State ChannelJID = jlib:jid_remove_resource(From), case ?DICT:find(ChannelJID, State#state.joined) of {ok, #channel{}} -> - FromNick = From#jid.lresource++jid_to_channel(From, State), - Body = xml:get_path_s(El, [{elem, "body"}, cdata]), + FromNick = binary_to_list(From#jid.lresource)++jid_to_channel(From, State), + Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]), BodyLines = string:tokens(Body, "\n"), lists:foreach( fun(Line) -> @@ -636,30 +658,36 @@ join_channels(Channels, [], State) -> join_channels([Channel | Channels], [Key | Keys], #state{nick = Nick} = State) -> Packet = - {xmlelement, "presence", [], - [{xmlelement, "x", [{"xmlns", ?NS_MUC}], + {xmlel, <<"presence">>, [], + [{xmlel, <<"x">>, [{<<"xmlns">>, ?NS_MUC}], case Key of none -> []; _ -> - [{xmlelement, "password", [], filter_cdata(Key)}] + [{xmlel, <<"password">>, [], filter_cdata(Key)}] end}]}, + ?DEBUG("joining channel nick=~p channel=~p state=~p", [Nick, Channel, State]), From = user_jid(State), + ?DEBUG("1 ~p", [From]), To = channel_nick_to_jid(Nick, Channel, State), + ?DEBUG("2 ~p", [To]), Room = jlib:jid_remove_resource(To), + ?DEBUG("3 ~p", [Room]), ejabberd_router:route(From, To, Packet), + ?DEBUG("4", []), NewState = State#state{joining = ?DICT:store(Room, [], State#state.joining)}, + ?DEBUG("5 ~p", [NewState]), join_channels(Channels, Keys, NewState). part_channels([], State, _Message) -> State; part_channels([Channel | Channels], State, Message) -> Packet = - {xmlelement, "presence", - [{"type", "unavailable"}], + {xmlel, <<"presence">>, + [{<<"type">>, <<"unavailable">>}], case Message of nothing -> []; - _ -> [{xmlelement, "status", [], + _ -> [{xmlel, <<"status">>, [], [{xmlcdata, Message}]}] end}, From = user_jid(State), @@ -704,7 +732,8 @@ upcase([C|String]) -> send_line(Line, #state{sockmod = SockMod, socket = Socket, encoding = Encoding}) -> ?DEBUG("sending ~s", [Line]), gen_tcp = SockMod, - EncodedLine = iconv:convert("utf-8", Encoding, Line), + %EncodedLine = iconv:convert("utf-8", Encoding, Line), + EncodedLine = Line, ok = gen_tcp:send(Socket, [EncodedLine, 13, 10]). send_command(Sender, Command, Params, State) -> @@ -716,9 +745,10 @@ send_text_command(Sender, Command, Params, State) -> send_command(Sender, Command, Params, State, true). send_command(Sender, Command, Params, State, AlwaysQuote) -> + ?DEBUG("SendCommand ~p ~p ~p", [Sender, Command, Params]), Prefix = case Sender of "" -> - [$: | State#state.host]; + [$: | binary_to_list(State#state.host)]; _ -> [$: | Sender] end, @@ -784,7 +814,7 @@ make_param_string([Param | Params], AlwaysQuote) -> " " ++ Param ++ make_param_string(Params, AlwaysQuote) end. -find_el(Name, NS, [{xmlelement, N, Attrs, _} = El|Els]) -> +find_el(Name, NS, [{xmlel, N, Attrs, _} = El|Els]) -> XMLNS = xml:get_attr_s("xmlns", Attrs), case {Name, NS} of {N, XMLNS} -> @@ -801,7 +831,7 @@ channel_to_jid(Channel, #state{muc_host = MucHost, channels_to_jids = ChannelsToJids}) -> case ?DICT:find(Channel, ChannelsToJids) of {ok, RoomJID} -> RoomJID; - _ -> jlib:make_jid(Channel, MucHost, "") + _ -> jlib:make_jid(list_to_binary(Channel), MucHost, <<"">>) end. channel_nick_to_jid(Nick, [$#|Channel], State) -> @@ -809,28 +839,28 @@ channel_nick_to_jid(Nick, [$#|Channel], State) -> channel_nick_to_jid(Nick, Channel, #state{muc_host = MucHost, channels_to_jids = ChannelsToJids}) -> case ?DICT:find(Channel, ChannelsToJids) of - {ok, RoomJID} -> jlib:jid_replace_resource(RoomJID, Nick); - _ -> jlib:make_jid(Channel, MucHost, Nick) + {ok, RoomJID} -> jlib:jid_replace_resource(RoomJID, list_to_binary(Nick)); + _ -> jlib:make_jid(list_to_binary(Channel), MucHost, list_to_binary(Nick)) end. jid_to_channel(#jid{user = Room} = RoomJID, #state{jids_to_channels = JidsToChannels}) -> case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of - {ok, Channel} -> [$#|Channel]; - _ -> [$#|Room] + {ok, Channel} -> [$#|binary_to_list(Channel)]; + _ -> [$#|binary_to_list(Room)] end. make_irc_sender(Nick, #jid{luser = Room} = RoomJID, #state{jids_to_channels = JidsToChannels}) -> case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of - {ok, Channel} -> Nick++"!"++Nick++"@"++Channel; - _ -> Nick++"!"++Nick++"@"++Room + {ok, Channel} -> Nick++"!"++Nick++"@"++binary_to_list(Channel); + _ -> Nick++"!"++Nick++"@"++binary_to_list(Room) end. make_irc_sender(#jid{lresource = Nick} = JID, State) -> - make_irc_sender(Nick, JID, State). + make_irc_sender(binary_to_list(Nick), JID, State). user_jid(#state{nick = Nick, host = Host}) -> - jlib:make_jid(Nick, Host, "irc"). + jlib:make_jid(list_to_binary(Nick), Host, <<"irc">>). filter_cdata(Msg) -> [{xmlcdata, filter_message(Msg)}]. @@ -857,25 +887,25 @@ translate_action(Msg) -> Msg end. -parse_error({xmlelement, "error", _ErrorAttrs, ErrorEls} = ErrorEl) -> +parse_error({xmlel, "error", _ErrorAttrs, ErrorEls} = ErrorEl) -> ErrorTextEl = xml:get_subtag(ErrorEl, "text"), ErrorName = case ErrorEls -- [ErrorTextEl] of - [{xmlelement, ErrorReason, _, _}] -> + [{xmlel, ErrorReason, _, _}] -> ErrorReason; _ -> "unknown error" end, ErrorText = case ErrorTextEl of - {xmlelement, _, _, _} -> + {xmlel, _, _, _} -> xml:get_tag_cdata(ErrorTextEl); _ -> nothing end, {ErrorName, ErrorText}. -error_to_string({xmlelement, "error", _ErrorAttrs, _ErrorEls} = ErrorEl) -> +error_to_string({xmlel, "error", _ErrorAttrs, _ErrorEls} = ErrorEl) -> case parse_error(ErrorEl) of {ErrorName, ErrorText} when is_list(ErrorText) -> ErrorName ++ ": " ++ ErrorText;