]> granicus.if.org Git - ejabberd/commitdiff
Added port and password connection parameters (thanks to Anton Podavalov)(EJAB-339)
authorBadlop <badlop@process-one.net>
Fri, 7 Aug 2009 14:43:44 +0000 (14:43 +0000)
committerBadlop <badlop@process-one.net>
Fri, 7 Aug 2009 14:43:44 +0000 (14:43 +0000)
SVN Revision: 2446

src/mod_irc/mod_irc.erl
src/mod_irc/mod_irc_connection.erl

index f73ebde892c138c393f8cfb565229b0068c0906b..a55c9c76abeba439d84be52bb46983b83f8fe0a8 100644 (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
index 5c983852b8bc57e7fc7e356913e2767493f60374..d1031415840dcec1933c764428c22783b3ab92cc 100644 (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 = ""}).
 %%%----------------------------------------------------------------------
 %%% 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,