aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2003-02-23 20:13:39 +0000
committerAlexey Shchepin <alexey@process-one.net>2003-02-23 20:13:39 +0000
commit499a1117dc78c1fc8f4cff0549eceebae7d6aa91 (patch)
treedc953bfcb3e3a235282546f2ff84ba83363bd601
parent* src/mod_configure.erl: Backup management support (diff)
* src/mod_irc/: Added configuration interface
* src/mod_configure.erl: Use jabber:iq:data instead of jabber:x:data * src/mod_disco.erl: Likewise SVN Revision: 82
-rw-r--r--ChangeLog8
-rw-r--r--src/mod_configure.erl10
-rw-r--r--src/mod_disco.erl12
-rw-r--r--src/mod_irc/mod_irc.erl245
-rw-r--r--src/mod_irc/mod_irc_connection.erl64
-rw-r--r--src/namespaces.hrl1
6 files changed, 281 insertions, 59 deletions
diff --git a/ChangeLog b/ChangeLog
index fe8799390..546685820 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2003-02-23 Alexey Shchepin <alexey@sevcom.net>
+
+ * src/mod_irc/: Added configuration interface
+
+ * src/mod_configure.erl: Use jabber:iq:data instead of
+ jabber:x:data
+ * src/mod_disco.erl: Likewise
+
2003-02-22 Alexey Shchepin <alexey@sevcom.net>
* src/mod_configure.erl: Backup management support
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 05381cbcc..248c9e3b6 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : mod_configure.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
-%%% Purpose : Support for online configuration of ejabberd via x:data
+%%% Purpose : Support for online configuration of ejabberd
%%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
@@ -23,15 +23,15 @@
start(Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_XDATA,
+ gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_IQDATA,
?MODULE, process_local_iq, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_XDATA,
+ gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_IQDATA,
?MODULE, process_sm_iq, IQDisc),
ok.
stop() ->
- gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_XDATA),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_XDATA).
+ gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_IQDATA),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_IQDATA).
process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 2ce645d63..ec8887276 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -125,28 +125,28 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
{iq, ID, result, XMLNS, [{xmlelement,
"query",
[{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})
+ [feature_to_xml({?NS_IQDATA})
]
}]};
["running nodes", ENode, "modules"] -> ?EMPTY_INFO_RESULT;
["running nodes", ENode, "modules", _] ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})]}]};
+ [feature_to_xml({?NS_IQDATA})]}]};
["running nodes", ENode, "backup"] -> ?EMPTY_INFO_RESULT;
["running nodes", ENode, "backup", _] ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})]}]};
+ [feature_to_xml({?NS_IQDATA})]}]};
["running nodes", ENode, "import"] -> ?EMPTY_INFO_RESULT;
["running nodes", ENode, "import", _] ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})]}]};
+ [feature_to_xml({?NS_IQDATA})]}]};
["config", _] ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})]}]};
+ [feature_to_xml({?NS_IQDATA})]}]};
_ ->
{iq, ID, error, XMLNS,
[SubEl, {xmlelement, "error",
@@ -443,7 +443,7 @@ process_sm_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
"" ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", XMLNS}],
- [feature_to_xml({?NS_XDATA})]}]};
+ [feature_to_xml({?NS_IQDATA})]}]};
_ ->
{iq, ID, error, XMLNS,
[SubEl, {xmlelement, "error",
diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl
index a3c4d0225..bd3e69854 100644
--- a/src/mod_irc/mod_irc.erl
+++ b/src/mod_irc/mod_irc.erl
@@ -12,15 +12,22 @@
-behaviour(gen_mod).
--export([start/1, init/1, stop/0, closed_conection/2]).
+-export([start/1, init/1, stop/0, closed_conection/2,
+ get_user_and_encoding/2]).
-include("ejabberd.hrl").
-include("namespaces.hrl").
+-define(DEFAULT_IRC_ENCODING, "koi8-r").
+
-record(irc_connection, {userserver, pid}).
+-record(irc_custom, {userserver, data}).
start(Opts) ->
iconv:start(),
+ mnesia:create_table(irc_custom,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, irc_custom)}]),
Host = gen_mod:get_opt(host, Opts, "irc." ++ ?MYNAME),
register(ejabberd_mod_irc, spawn(?MODULE, init, [Host])).
@@ -60,15 +67,12 @@ do_route(Host, From, To, Packet) ->
Res = {iq, ID, result, XMLNS,
[{xmlelement, "query",
[{"xmlns", XMLNS}],
- [{xmlelement, "identity",
- [{"category", "conference"},
- {"type", "irc"},
- {"name", "ejabberd"}], []},
- {xmlelement, "feature",
- [{"var", ?NS_MUC}], []}]}]},
+ iq_disco()}]},
ejabberd_router:route(To,
From,
jlib:iq_to_xml(Res));
+ {iq, ID, Type, ?NS_IQDATA = XMLNS, SubEl} ->
+ iq_data(From, To, ID, XMLNS, Type, SubEl);
_ ->
Err = jlib:make_error_reply(
Packet, "503", "Service Unavailable"),
@@ -85,8 +89,11 @@ do_route(Host, From, To, Packet) ->
case ets:lookup(irc_connection, {From, Server}) of
[] ->
io:format("open new connection~n"),
+ {Username, Encoding} = get_user_and_encoding(
+ From, Server),
{ok, Pid} = mod_irc_connection:start(
- From, Host, Server),
+ From, Host, Server,
+ Username, Encoding),
ets:insert(
irc_connection,
#irc_connection{userserver = {From, Server},
@@ -137,3 +144,225 @@ stop() ->
closed_conection(From, Server) ->
ets:delete(irc_connection, {From, Server}).
+
+iq_disco() ->
+ [{xmlelement, "identity",
+ [{"category", "conference"},
+ {"type", "irc"},
+ {"name", "ejabberd/mod_irc"}], []},
+ {xmlelement, "feature",
+ [{"var", ?NS_MUC}], []},
+ {xmlelement, "feature",
+ [{"var", ?NS_IQDATA}], []}].
+
+
+
+iq_data(From, To, ID, XMLNS, Type, SubEl) ->
+ case catch process_iq_data(From, To, ID, XMLNS, Type, SubEl) of
+ {'EXIT', Reason} ->
+ ?ERROR_MSG("~p", [Reason]);
+ ResIQ ->
+ if
+ ResIQ /= ignore ->
+ ejabberd_router:route(To, From,
+ jlib:iq_to_xml(ResIQ));
+ true ->
+ ok
+ end
+ end.
+
+
+process_iq_data(From, To, ID, XMLNS, Type, SubEl) ->
+ Lang = xml:get_tag_attr_s("xml:lang", SubEl),
+ case Type of
+ set ->
+ case xml:get_tag_attr_s("type", SubEl) of
+ "cancel" ->
+ {iq, ID, result, XMLNS,
+ [{xmlelement, "query", [{"xmlns", XMLNS}], []}]};
+ "submit" ->
+ XData = jlib:parse_xdata_submit(SubEl),
+ case XData of
+ invalid ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", "400"}],
+ [{xmlcdata, "Bad Request"}]}]};
+ _ ->
+ Node =
+ string:tokens(
+ xml:get_tag_attr_s("node", SubEl),
+ "/"),
+ case set_form(From, Node, Lang, XData) of
+ {result, Res} ->
+ {iq, ID, result, XMLNS,
+ [{xmlelement, "query",
+ [{"xmlns", XMLNS}],
+ Res
+ }]};
+ {error, Code, Desc} ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", Code}],
+ [{xmlcdata, Desc}]}]}
+ end
+ end;
+ _ ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", "405"}],
+ [{xmlcdata, "Not Allowed"}]}]}
+ end;
+ get ->
+ Node =
+ string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
+ case get_form(From, Node, Lang) of
+ {result, Res} ->
+ {iq, ID, result, XMLNS,
+ [{xmlelement, "query", [{"xmlns", XMLNS}],
+ Res
+ }]};
+ {error, Code, Desc} ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", Code}],
+ [{xmlcdata, Desc}]}]}
+ end
+ end.
+
+
+
+get_form(From, [], Lang) ->
+ {User, Server, _} = From,
+ {LUser, LServer, _} = jlib:jid_tolower(From),
+ Customs =
+ case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
+ {'EXIT', Reason} ->
+ {error, "500", "Internal Server Error"};
+ [] ->
+ {User, []};
+ [#irc_custom{data = Data}] ->
+ {xml:get_attr_s(username, Data),
+ xml:get_attr_s(encodings, Data)}
+ end,
+ case Customs of
+ {error, _, _} ->
+ Customs;
+ {Username, Encodings} ->
+ {result,
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ User ++ "@" ++ Server ++ " " ++
+ translate:translate(Lang, "Configuration")}]},
+ %{xmlelement, "instructions", [],
+ % [{xmlcdata,
+ % translate:translate(
+ % Lang, "")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "IRC Username")},
+ {"var", "username"}],
+ [{xmlelement, "value", [], [{xmlcdata, Username}]}]},
+ {xmlelement, "field", [{"type", "fixed"}],
+ [{xmlelement, "value", [],
+ [{xmlcdata,
+ lists:flatten(
+ io_lib:format(
+ translate:translate(
+ Lang,
+ "If you want to specify different encodings "
+ "for IRC servers, fill this list with values "
+ "in format '{\"irc server\", \"encoding\"}'. "
+ "By default this service use \"~s\" encoding."),
+ [?DEFAULT_IRC_ENCODING]))}]}]},
+ {xmlelement, "field", [{"type", "fixed"}],
+ [{xmlelement, "value", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang,
+ "Example: [{\"irc.lucky.net\", \"koi8-r\"}, "
+ "{\"vendetta.fef.net\", \"iso8859-1\"}]."
+ )}]}]},
+ {xmlelement, "field", [{"type", "text-multi"},
+ {"label",
+ translate:translate(Lang, "Encodings")},
+ {"var", "encodings"}],
+ lists:map(
+ fun(S) ->
+ {xmlelement, "value", [], [{xmlcdata, S}]}
+ end,
+ string:tokens(
+ lists:flatten(
+ io_lib:format("~p.", [Encodings])),
+ "\n"))
+ }
+ ]}
+ end;
+
+
+get_form(_, _, Lang) ->
+ {error, "503", "Service Unavailable"}.
+
+
+
+
+set_form(From, [], Lang, XData) ->
+ {LUser, LServer, _} = jlib:jid_tolower(From),
+ case {lists:keysearch("username", 1, XData),
+ lists:keysearch("encodings", 1, XData)} of
+ {{value, {_, [Username]}}, {value, {_, Strings}}} ->
+ EncString = lists:foldl(fun(S, Res) ->
+ Res ++ S ++ "\n"
+ end, "", Strings),
+ case erl_scan:string(EncString) of
+ {ok, Tokens, _} ->
+ case erl_parse:parse_term(Tokens) of
+ {ok, Encodings} ->
+ case mnesia:transaction(
+ fun() ->
+ mnesia:write(
+ #irc_custom{userserver =
+ {LUser, LServer},
+ data =
+ [{username,
+ Username},
+ {encodings,
+ Encodings}]})
+ end) of
+ {atomic, _} ->
+ {result, []};
+ _ ->
+ {error, "406", "Not Acceptable"}
+ end;
+ _ ->
+ {error, "406", "Not Acceptable"}
+ end;
+ _ ->
+ {error, "406", "Not Acceptable"}
+ end;
+ _ ->
+ {error, "406", "Not Acceptable"}
+ end;
+
+
+set_form(_, _, Lang, XData) ->
+ {error, "503", "Service Unavailable"}.
+
+
+get_user_and_encoding(From, IRCServer) ->
+ {User, Server, _} = From,
+ {LUser, LServer, _} = jlib:jid_tolower(From),
+ case catch mnesia:dirty_read({irc_custom, {LUser, LServer}}) of
+ {'EXIT', Reason} ->
+ {User, ?DEFAULT_IRC_ENCODING};
+ [] ->
+ {User, ?DEFAULT_IRC_ENCODING};
+ [#irc_custom{data = Data}] ->
+ {xml:get_attr_s(username, Data),
+ case xml:get_attr_s(IRCServer, xml:get_attr_s(encodings, Data)) of
+ "" -> ?DEFAULT_IRC_ENCODING;
+ E -> E
+ end}
+ end.
+
diff --git a/src/mod_irc/mod_irc_connection.erl b/src/mod_irc/mod_irc_connection.erl
index 8e9c88d27..ed1a46e73 100644
--- a/src/mod_irc/mod_irc_connection.erl
+++ b/src/mod_irc/mod_irc_connection.erl
@@ -13,7 +13,7 @@
-behaviour(gen_fsm).
%% External exports
--export([start/3, receiver/2, route_chan/4, route_nick/3]).
+-export([start/5, receiver/2, route_chan/4, route_nick/3]).
%% gen_fsm callbacks
-export([init/1,
@@ -31,13 +31,11 @@
-define(SETS, gb_sets).
--record(state, {socket, receiver, queue,
+-record(state, {socket, encoding, receiver, queue,
user, myname, server, nick,
channels = dict:new(),
inbuf = "", outbuf = ""}).
--define(IRC_ENCODING, "koi8-r").
-
-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -49,8 +47,8 @@
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
-start(From, Host, Server) ->
- gen_fsm:start(?MODULE, [From, Host, Server], ?FSMOPTS).
+start(From, Host, Server, Username, Encoding) ->
+ gen_fsm:start(?MODULE, [From, Host, Server, Username, Encoding], ?FSMOPTS).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
@@ -63,12 +61,12 @@ start(From, Host, Server) ->
%% ignore |
%% {stop, StopReason}
%%----------------------------------------------------------------------
-init([From, Host, Server]) ->
+init([From, Host, Server, Username, Encoding]) ->
gen_fsm:send_event(self(), init),
- {Nick, _, _} = From,
{ok, open_socket, #state{queue = queue:new(),
+ encoding = Encoding,
user = From,
- nick = Nick,
+ nick = Username,
myname = Host,
server = Server}}.
@@ -84,13 +82,10 @@ open_socket(init, StateData) ->
?DEBUG("connecting to ~s:~p~n", [Addr, Port]),
case gen_tcp:connect(Addr, Port, [binary, {packet, 0}]) of
{ok, Socket} ->
- % TODO: send nick, etc...
- %send_text(Socket, io_lib:format(?STREAM_HEADER,
- % [StateData#state.server])),
- %send_queue(StateData#state.socket, StateData#state.queue),
- send_text(Socket, io_lib:format("NICK ~s\r\n",
- [StateData#state.nick])),
- send_text(Socket,
+ NewStateData = StateData#state{socket = Socket},
+ send_text(NewStateData,
+ io_lib:format("NICK ~s\r\n", [StateData#state.nick])),
+ send_text(NewStateData,
io_lib:format(
"USER ~s ~s ~s :~s\r\n",
[StateData#state.nick,
@@ -98,7 +93,7 @@ open_socket(init, StateData) ->
StateData#state.myname,
StateData#state.nick])),
{next_state, wait_for_registration,
- StateData#state{socket = Socket}};
+ NewStateData};
{error, Reason} ->
?DEBUG("connect return ~p~n", [Reason]),
Text = case Reason of
@@ -164,7 +159,7 @@ code_change(OldVsn, StateName, StateData, Extra) ->
-define(SEND(S),
if
StateName == stream_established ->
- send_text(StateData#state.socket, S),
+ send_text(StateData, S),
StateData;
true ->
StateData#state{outbuf = StateData#state.outbuf ++ S}
@@ -297,7 +292,7 @@ handle_info({route_nick, Nick, Packet}, StateName, StateData) ->
handle_info({ircstring, [$P, $I, $N, $G, $ | ID]}, StateName, StateData) ->
- send_text(StateData#state.socket, "PONG " ++ ID ++ "\r\n"),
+ send_text(StateData, "PONG " ++ ID ++ "\r\n"),
{next_state, StateName, StateData};
handle_info({ircstring, [$: | String]}, StateName, StateData) ->
@@ -343,7 +338,7 @@ handle_info({ircstring, [$: | String]}, StateName, StateData) ->
"" ->
NewStateData;
Data ->
- send_text(NewStateData#state.socket, Data),
+ send_text(NewStateData, Data),
NewStateData#state{outbuf = ""}
end,
{next_state, stream_established, NewStateData1};
@@ -360,22 +355,13 @@ handle_info({ircstring, String}, StateName, StateData) ->
handle_info({send_text, Text}, StateName, StateData) ->
- send_text(StateData#state.socket, Text),
+ send_text(StateData, Text),
{next_state, StateName, StateData};
-handle_info({send_element, El}, StateName, StateData) ->
- case StateName of
- stream_established ->
- send_element(StateData#state.socket, El),
- {next_state, StateName, StateData};
- _ ->
- Q = queue:in(El, StateData#state.queue),
- {next_state, StateName, StateData#state{queue = Q}}
- end;
handle_info({tcp, Socket, Data}, StateName, StateData) ->
Buf = StateData#state.inbuf ++ binary_to_list(Data),
{ok, Strings} = regexp:split([C || C <- Buf, C /= $\r], "\n"),
io:format("strings=~p~n", [Strings]),
- NewBuf = process_lines(Strings),
+ NewBuf = process_lines(StateData#state.encoding, Strings),
{next_state, StateName, StateData#state{inbuf = NewBuf}};
handle_info({tcp_closed, Socket}, StateName, StateData) ->
gen_fsm:send_event(self(), closed),
@@ -430,13 +416,11 @@ receiver(Socket, C2SPid, XMLStreamPid) ->
ok
end.
-send_text(Socket, Text) ->
- CText = iconv:convert("utf-8", ?IRC_ENCODING, lists:flatten(Text)),
+send_text(#state{socket = Socket, encoding = Encoding}, Text) ->
+ CText = iconv:convert("utf-8", Encoding, lists:flatten(Text)),
%io:format("IRC OUTu: ~s~nIRC OUTk: ~s~n", [Text, CText]),
gen_tcp:send(Socket, CText).
-send_element(Socket, El) ->
- send_text(Socket, xml:element_to_string(El)).
%send_queue(Socket, Q) ->
% case queue:out(Q) of
@@ -474,11 +458,11 @@ route_nick(Pid, Nick, Packet) ->
Pid ! {route_nick, Nick, Packet}.
-process_lines([S]) ->
+process_lines(Encoding, [S]) ->
S;
-process_lines([S | Ss]) ->
- self() ! {ircstring, iconv:convert(?IRC_ENCODING, "utf-8", S)},
- process_lines(Ss).
+process_lines(Encoding, [S | Ss]) ->
+ self() ! {ircstring, iconv:convert(Encoding, "utf-8", S)},
+ process_lines(Encoding, Ss).
process_channel_list(StateData, Items) ->
process_channel_list_find_chan(StateData, Items).
@@ -580,7 +564,7 @@ process_privmsg(StateData, Nick, From, String) ->
process_version(StateData, Nick, From) ->
[FromUser | _] = string:tokens(From, "!"),
send_text(
- StateData#state.socket,
+ StateData,
io_lib:format("NOTICE ~s :\001VERSION "
"ejabberd IRC transport ~s (c) Alexey Shchepin"
"\001\r\n",
diff --git a/src/namespaces.hrl b/src/namespaces.hrl
index e26b9750e..397ee7826 100644
--- a/src/namespaces.hrl
+++ b/src/namespaces.hrl
@@ -16,6 +16,7 @@
-define(NS_VERSION, "jabber:iq:version").
-define(NS_TIME, "jabber:iq:time").
-define(NS_XDATA, "jabber:x:data").
+-define(NS_IQDATA, "jabber:iq:data").
-define(NS_DELAY, "jabber:x:delay").
-define(NS_EVENT, "jabber:x:event").
-define(NS_STATS, "http://jabber.org/protocol/stats").