Added port and password connection parameters (thanks to Anton Podavalov)(EJAB-339)

SVN Revision: 2446
This commit is contained in:
Badlop 2009-08-07 14:43:44 +00:00
parent dd2be928af
commit 0e8dd70ed5
2 changed files with 154 additions and 72 deletions

View File

@ -35,7 +35,7 @@
start/2,
stop/1,
closed_connection/3,
get_user_and_encoding/3]).
get_connection_params/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@ -46,6 +46,7 @@
-include("adhoc.hrl").
-define(DEFAULT_IRC_ENCODING, "iso8859-1").
-define(DEFAULT_IRC_PORT, 6667).
-define(POSSIBLE_ENCODINGS, ["koi8-r", "iso8859-1", "iso8859-2", "utf-8", "utf-8+latin-1"]).
-record(irc_connection, {jid_server_host, pid}).
@ -328,7 +329,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
case ets:lookup(irc_connection, {From, Server, Host}) of
[] ->
?DEBUG("open new connection~n", []),
{Username, Encoding} = get_user_and_encoding(
{Username, Encoding, Port, Password} = get_connection_params(
Host, From, Server),
ConnectionUsername =
case Packet of
@ -347,7 +348,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
end,
{ok, Pid} = mod_irc_connection:start(
From, Host, ServerHost, Server,
ConnectionUsername, Encoding),
ConnectionUsername, Encoding, Port, Password),
ets:insert(
irc_connection,
#irc_connection{jid_server_host = {From, Server, Host},
@ -436,7 +437,7 @@ command_items(Host, Lang) ->
commands() ->
[{"join", "Join channel", fun adhoc_join/3},
{"register", "Configure username and encoding", fun adhoc_register/3}].
{"register", "Configure username, encoding, port and password", fun adhoc_register/3}].
process_register(Host, From, To, #iq{} = IQ) ->
case catch process_irc_register(Host, From, To, IQ) of
@ -542,12 +543,12 @@ get_form(Host, From, [], Lang) ->
{User, []};
[#irc_custom{data = Data}] ->
{xml:get_attr_s(username, Data),
xml:get_attr_s(encodings, Data)}
xml:get_attr_s(connections_params, Data)}
end,
case Customs of
{error, _Error} ->
Customs;
{Username, Encodings} ->
{Username, ConnectionsParams} ->
{result,
[{xmlelement, "instructions", [],
[{xmlcdata,
@ -565,7 +566,7 @@ get_form(Host, From, [], Lang) ->
[{xmlcdata,
translate:translate(
Lang,
"Enter username and encodings you wish to use for "
"Enter username, encodings, ports and passwords you wish to use for "
"connecting to IRC servers")}]},
{xmlelement, "field", [{"type", "text-single"},
{"label",
@ -580,30 +581,32 @@ get_form(Host, From, [], Lang) ->
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]))}]}]},
"If you want to specify different ports, "
"passwords, encodings for IRC servers, fill "
"this list with values in format "
"'{\"irc server\", \"encoding\", port, \"password\"}'. "
"By default this service use \"~s\" encoding, port ~p, "
"empty password."),
[?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT]))}]}]},
{xmlelement, "field", [{"type", "fixed"}],
[{xmlelement, "value", [],
[{xmlcdata,
translate:translate(
Lang,
"Example: [{\"irc.lucky.net\", \"koi8-r\"}, "
"{\"vendetta.fef.net\", \"iso8859-1\"}]."
"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, "
"{\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."
)}]}]},
{xmlelement, "field", [{"type", "text-multi"},
{"label",
translate:translate(Lang, "Encodings")},
{"var", "encodings"}],
translate:translate(Lang, "Connections parameters")},
{"var", "connections_params"}],
lists:map(
fun(S) ->
{xmlelement, "value", [], [{xmlcdata, S}]}
end,
string:tokens(
lists:flatten(
io_lib:format("~p.", [Encodings])),
io_lib:format("~p.", [ConnectionsParams])),
"\n"))
}
]}]}
@ -619,7 +622,7 @@ set_form(Host, From, [], _Lang, XData) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
US = {LUser, LServer},
case {lists:keysearch("username", 1, XData),
lists:keysearch("encodings", 1, XData)} of
lists:keysearch("connections_params", 1, XData)} of
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
EncString = lists:foldl(fun(S, Res) ->
Res ++ S ++ "\n"
@ -627,7 +630,7 @@ set_form(Host, From, [], _Lang, XData) ->
case erl_scan:string(EncString) of
{ok, Tokens, _} ->
case erl_parse:parse_term(Tokens) of
{ok, Encodings} ->
{ok, ConnectionsParams} ->
case mnesia:transaction(
fun() ->
mnesia:write(
@ -636,8 +639,8 @@ set_form(Host, From, [], _Lang, XData) ->
data =
[{username,
Username},
{encodings,
Encodings}]})
{connections_params,
ConnectionsParams}]})
end) of
{atomic, _} ->
{result, []};
@ -659,22 +662,38 @@ set_form(_Host, _, _, _Lang, _XData) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
get_user_and_encoding(Host, From, IRCServer) ->
get_connection_params(Host, From, IRCServer) ->
#jid{user = User, server = _Server,
luser = LUser, lserver = LServer} = From,
US = {LUser, LServer},
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
{'EXIT', _Reason} ->
{User, ?DEFAULT_IRC_ENCODING};
{User, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""};
[] ->
{User, ?DEFAULT_IRC_ENCODING};
{User, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""};
[#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.
Username = xml:get_attr_s(username, Data),
{NewUsername, NewEncoding, NewPort, NewPassword} =
case lists:keysearch(IRCServer, 1, xml:get_attr_s(connections_params, Data)) of
{value, {_, Encoding, Port, Password}} ->
{Username, Encoding, Port, Password};
{value, {_, Encoding, Port}} ->
{Username, Encoding, Port, ""};
{value, {_, Encoding}} ->
{Username, Encoding, ?DEFAULT_IRC_PORT, ""};
_ ->
{Username, ?DEFAULT_IRC_ENCODING, ?DEFAULT_IRC_PORT, ""}
end,
{NewUsername,
NewEncoding,
if
NewPort >= 0 andalso NewPort =< 65535 ->
NewPort;
true ->
?DEFAULT_IRC_PORT
end,
NewPassword}
end.
adhoc_join(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
adhoc:produce_response(Request,
@ -766,13 +785,13 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
{'EXIT', _Reason} ->
Username = User,
Encodings = [];
ConnectionsParams = [];
[] ->
Username = User,
Encodings = [];
ConnectionsParams = [];
[#irc_custom{data = Data}] ->
Username = xml:get_attr_s(username, Data),
Encodings = xml:get_attr_s(encodings, Data)
ConnectionsParams = xml:get_attr_s(connections_params, Data)
end,
Error = false;
true ->
@ -780,7 +799,7 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
invalid ->
Error = {error, ?ERR_BAD_REQUEST},
Username = false,
Encodings = false;
ConnectionsParams = false;
Fields ->
Username = case lists:keysearch("username", 1, Fields) of
{value, {"username", U}} ->
@ -788,7 +807,7 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
_ ->
User
end,
Encodings = parse_encodings(Fields),
ConnectionsParams = parse_connections_params(Fields),
Error = false
end
end,
@ -804,8 +823,8 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
data =
[{username,
Username},
{encodings,
Encodings}]})
{connections_params,
ConnectionsParams}]})
end) of
{atomic, _} ->
adhoc:produce_response(Request, #adhoc_response{status = completed});
@ -813,14 +832,14 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
{error, ?ERR_INTERNAL_SERVER_ERROR}
end;
true ->
Form = generate_adhoc_register_form(Lang, Username, Encodings),
Form = generate_adhoc_register_form(Lang, Username, ConnectionsParams),
adhoc:produce_response(Request,
#adhoc_response{status = executing,
elements = [Form],
actions = ["next", "complete"]})
end.
generate_adhoc_register_form(Lang, Username, Encodings) ->
generate_adhoc_register_form(Lang, Username, ConnectionsParams) ->
{xmlelement, "x",
[{"xmlns", ?NS_XDATA},
{"type", "form"}],
@ -838,24 +857,58 @@ generate_adhoc_register_form(Lang, Username, Encodings) ->
{"label", translate:translate(Lang, "IRC username")}],
[{xmlelement, "required", [], []},
{xmlelement, "value", [], [{xmlcdata, Username}]}]}] ++
generate_encoding_fields(Lang, Encodings, 1, [])}.
generate_connection_params_fields(Lang, ConnectionsParams, 1, [])}.
generate_encoding_fields(Lang, [], Number, Acc) ->
Field = generate_encoding_field(Lang, "", "", Number),
generate_connection_params_fields(Lang, [], Number, Acc) ->
Field = generate_connection_params_field(Lang, "", "", 0, "", Number),
lists:reverse(Field ++ Acc);
generate_encoding_fields(Lang, [{Server, Encoding} | Encodings], Number, Acc) ->
Field = generate_encoding_field(Lang, Server, Encoding, Number),
generate_encoding_fields(Lang, Encodings, Number + 1, Field ++ Acc).
generate_connection_params_fields(Lang, [ConnectionParams | ConnectionsParams], Number, Acc) ->
case ConnectionParams of
{Server, Encoding, Port, Password} ->
Field = generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number),
generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
{Server, Encoding, Port} ->
Field = generate_connection_params_field(Lang, Server, Encoding, Port, [], Number),
generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
{Server, Encoding} ->
Field = generate_connection_params_field(Lang, Server, Encoding, [], [], Number),
generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
_ ->
[]
end.
generate_encoding_field(Lang, Server, Encoding, Number) ->
generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number) ->
EncodingUsed = case Encoding of
[] ->
?DEFAULT_IRC_ENCODING;
_ ->
Encoding
end,
PortUsed = if
Port >= 0 andalso Port =< 65535 ->
Port;
true ->
?DEFAULT_IRC_PORT
end,
PasswordUsed = case Password of
[] ->
"";
_ ->
Password
end,
%% Fields are in reverse order, as they will be reversed again later.
[{xmlelement, "field",
[{"var", "password" ++ io_lib:format("~b", [Number])},
{"type", "text-single"},
{"label", io_lib:format(translate:translate(Lang, "Password ~b"), [Number])}],
[{xmlelement, "value", [], [{xmlcdata, PasswordUsed}]}]},
{xmlelement, "field",
[{"var", "port" ++ io_lib:format("~b", [Number])},
{"type", "text-single"},
{"label", io_lib:format(translate:translate(Lang, "Port ~b"), [Number])}],
[{xmlelement, "value", [], [{xmlcdata, PortUsed}]}]},
{xmlelement, "field",
[{"var", "encoding" ++ io_lib:format("~b", [Number])},
{"type", "list-single"},
{"label", io_lib:format(translate:translate(Lang, "Encoding for server ~b"), [Number])}],
@ -870,8 +923,8 @@ generate_encoding_field(Lang, Server, Encoding, Number) ->
{"label", io_lib:format(translate:translate(Lang, "Server ~b"), [Number])}],
[{xmlelement, "value", [], [{xmlcdata, Server}]}]}].
parse_encodings(Fields) ->
%% Find all fields staring with serverN and encodingN, for any values
parse_connections_params(Fields) ->
%% Find all fields staring with serverN, encodingN, portN and passwordN for any values
%% of N, and generate lists of {"N", Value}.
Servers = lists:sort(
[{lists:nthtail(6, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
@ -879,25 +932,46 @@ parse_encodings(Fields) ->
Encodings = lists:sort(
[{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
lists:prefix("encoding", Var)]),
Ports = lists:sort(
[{lists:nthtail(4, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
lists:prefix("port", Var)]),
Passwords = lists:sort(
[{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
lists:prefix("password", Var)]),
%% Now sort the lists, and find the corresponding pairs.
parse_encodings(Servers, Encodings).
parse_encodings([{ServerN, Server} | Servers], [{EncodingN, Encoding} | Encodings]) ->
%% Try to match pairs of servers and encodings, no matter what fields
%% the client might have left out.
if ServerN == EncodingN ->
[{Server, Encoding} | parse_encodings(Servers, Encodings)];
ServerN < EncodingN ->
parse_encodings(Servers, [{EncodingN, Encoding} | Encodings]);
ServerN > EncodingN ->
parse_encodings([{ServerN, Server} | Servers], Encodings)
end;
parse_encodings([], _) ->
parse_connections_params(Servers, Encodings, Ports, Passwords).
retrieve_connections_params(ConnectionParams, ServerN) ->
case ConnectionParams of
[{ConnectionParamN, ConnectionParam} | ConnectionParamsTail] ->
if
ServerN == ConnectionParamN ->
{ConnectionParam, ConnectionParamsTail};
ServerN < ConnectionParamN ->
{[], [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail]};
ServerN > ConnectionParamN ->
{[], ConnectionParamsTail}
end;
_ ->
{[], []}
end.
parse_connections_params([], _, _, _) ->
[];
parse_encodings(_, []) ->
[].
parse_connections_params(_, [], [], []) ->
[];
parse_connections_params([{ServerN, Server} | Servers], Encodings, Ports, Passwords) ->
%% Try to match matches of servers, ports, passwords and encodings, no matter what fields
%% the client might have left out.
{NewEncoding, NewEncodings} = retrieve_connections_params(Encodings, ServerN),
{NewPort, NewPorts} = retrieve_connections_params(Ports, ServerN),
{NewPassword, NewPasswords} = retrieve_connections_params(Passwords, ServerN),
[{Server, NewEncoding, NewPort, NewPassword} | parse_connections_params(Servers, NewEncodings, NewPorts, NewPasswords)].
update_table(Host) ->
Fields = record_info(fields, irc_custom),
case mnesia:table_info(irc_custom, attributes) of

View File

@ -30,7 +30,7 @@
-behaviour(gen_fsm).
%% External exports
-export([start_link/5, start/6, route_chan/4, route_nick/3]).
-export([start_link/7, start/8, route_chan/4, route_nick/3]).
%% gen_fsm callbacks
-export([init/1,
@ -48,8 +48,8 @@
-define(SETS, gb_sets).
-record(state, {socket, encoding, queue,
user, host, server, nick,
-record(state, {socket, encoding, port, password,
queue, user, host, server, nick,
channels = dict:new(),
nickchannel,
inbuf = "", outbuf = ""}).
@ -65,13 +65,13 @@
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(From, Host, ServerHost, Server, Username, Encoding) ->
start(From, Host, ServerHost, Server, Username, Encoding, Port, Password) ->
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_irc_sup),
supervisor:start_child(
Supervisor, [From, Host, Server, Username, Encoding]).
Supervisor, [From, Host, Server, Username, Encoding, Port, Password]).
start_link(From, Host, Server, Username, Encoding) ->
gen_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding],
start_link(From, Host, Server, Username, Encoding, Port, Password) ->
gen_fsm:start_link(?MODULE, [From, Host, Server, Username, Encoding, Port, Password],
?FSMOPTS).
%%%----------------------------------------------------------------------
@ -85,10 +85,12 @@ start_link(From, Host, Server, Username, Encoding) ->
%% ignore |
%% {stop, StopReason}
%%----------------------------------------------------------------------
init([From, Host, Server, Username, Encoding]) ->
init([From, Host, Server, Username, Encoding, Port, Password]) ->
gen_fsm:send_event(self(), init),
{ok, open_socket, #state{queue = queue:new(),
encoding = Encoding,
port = Port,
password = Password,
user = From,
nick = Username,
host = Host,
@ -102,11 +104,17 @@ init([From, Host, Server, Username, Encoding]) ->
%%----------------------------------------------------------------------
open_socket(init, StateData) ->
Addr = StateData#state.server,
Port = 6667,
Port = StateData#state.port,
?DEBUG("connecting to ~s:~p~n", [Addr, Port]),
case gen_tcp:connect(Addr, Port, [binary, {packet, 0}]) of
{ok, Socket} ->
NewStateData = StateData#state{socket = Socket},
if
StateData#state.password /= "" ->
send_text(NewStateData,
io_lib:format("PASS ~s\r\n", [StateData#state.password]));
true -> true
end,
send_text(NewStateData,
io_lib:format("NICK ~s\r\n", [StateData#state.nick])),
send_text(NewStateData,