diff --git a/TODO b/TODO index 248c97fb1..af37fc296 100644 --- a/TODO +++ b/TODO @@ -3,6 +3,7 @@ admin interface node management backup management S2S timeouts +rewrite S2S key validation iq:browse(?) SVR DNS records karma diff --git a/src/acl.erl b/src/acl.erl index b9c231475..375f69490 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -15,14 +15,15 @@ -include("ejabberd.hrl"). start() -> - ets:new(acls, [bag, named_table, public]). + ets:new(acls, [bag, named_table, public]), + ok. add(ACLName, ACLData) -> ets:insert(acls, {ACLName, ACLData}). match_rule(Rule, JID) -> - case ejabberd_config:get_option(Rule) of + case ejabberd_config:get_global_option({access, Rule}) of undefined -> deny; ACLs -> diff --git a/src/ejabberd.cfg b/src/ejabberd.cfg index 6d3d6521d..464a1f7a1 100644 --- a/src/ejabberd.cfg +++ b/src/ejabberd.cfg @@ -9,10 +9,10 @@ {acl, jabberorg, {server, "jabber.org"}}. {acl, aleksey, {user, "aleksey", "jabber.ru"}}. -{disco_admin, [{allow, admin}, - {deny, all}]}. +{access, disco_admin, [{allow, admin}, + {deny, all}]}. -{configure, [{allow, admin}]}. +{access, configure, [{allow, admin}]}. {host, "e.localhost"}. diff --git a/src/ejabberd.erl b/src/ejabberd.erl index e02773594..0e4f50954 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -45,6 +45,11 @@ loop(Port) -> end. db_init() -> - mnesia:create_schema([node()]), + case mnesia:system_info(extra_db_nodes) of + [] -> + mnesia:create_schema([node()]); + _ -> + ok + end, mnesia:start(), - mnesia:wait_for_tables(mnesia:system_info(tables), infinity). + mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity). diff --git a/src/ejabberd.hrl b/src/ejabberd.hrl index 44adc0db4..5e453fd5b 100644 --- a/src/ejabberd.hrl +++ b/src/ejabberd.hrl @@ -24,7 +24,7 @@ %-define(MYNAME,"e.localhost"). --define(MYNAME, ejabberd_config:get_option(host)). +-define(MYNAME, ejabberd_config:get_global_option(host)). -define(MSGS_DIR, "msgs"). -define(CONFIG_PATH, "ejabberd.cfg"). diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 7e0aa4284..809d3dfd8 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -22,6 +22,7 @@ check_password/4, try_register/2, dirty_get_registered_users/0, + get_password_s/1, is_user_exists/1]). %% gen_server callbacks @@ -163,6 +164,15 @@ try_register(User, Password) -> dirty_get_registered_users() -> mnesia:dirty_all_keys(passwd). +get_password_s(User) -> + LUser = jlib:tolower(User), + case catch mnesia:dirty_read(passwd, LUser) of + [#passwd{password = Password}] -> + Password; + _ -> + [] + end. + is_user_exists(User) -> LUser = jlib:tolower(User), F = fun() -> diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 1bae8a9fd..40dc97bf2 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -10,12 +10,26 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([start/0, load_file/1, get_option/1]). +-export([start/0, load_file/1, + add_global_option/2, add_local_option/2, + get_global_option/1, get_local_option/1]). -include("ejabberd.hrl"). +-record(config, {key, value}). +-record(local_config, {key, value}). + start() -> - ets:new(ejabberd_config, [named_table, public]), + %ets:new(ejabberd_config, [named_table, public]), + mnesia:create_table(config, + [{disc_copies, [node()]}, + {attributes, record_info(fields, config)}]), + mnesia:add_table_copy(config, node(), ram_copies), + mnesia:create_table(local_config, + [{disc_copies, [node()]}, + {local_content, true}, + {attributes, record_info(fields, local_config)}]), + mnesia:add_table_copy(local_config, node(), ram_copies), load_file(?CONFIG_PATH). @@ -31,14 +45,50 @@ process_term(Term) -> case Term of {acl, ACLName, ACLData} -> acl:add(ACLName, ACLData); + {access, RuleName, Rules} -> + add_global_option({access, RuleName}, Rules); {Opt, Val} -> - ets:insert(ejabberd_config, {Opt, Val}) + add_option(Opt, Val) end. +add_option(Opt, Val) -> + Table = case Opt of + host -> + config; + _ -> + local_config + end, + case Table of + config -> + add_global_option(Opt, Val); + local_config -> + add_local_option(Opt, Val) + end. -get_option(Opt) -> - case ets:lookup(ejabberd_config, Opt) of - [{_, Val}] -> +add_global_option(Opt, Val) -> + mnesia:transaction(fun() -> + mnesia:write(#config{key = Opt, + value = Val}) + end). + +add_local_option(Opt, Val) -> + mnesia:transaction(fun() -> + mnesia:write(#local_config{key = Opt, + value = Val}) + end). + + +get_global_option(Opt) -> + case ets:lookup(config, Opt) of + [#config{value = Val}] -> + Val; + _ -> + undefined + end. + +get_local_option(Opt) -> + case ets:lookup(local_config, Opt) of + [#local_config{value = Val}] -> Val; _ -> undefined diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index c09733515..9e6957c47 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -17,7 +17,7 @@ start() -> init(_) -> - case ejabberd_config:get_option(listen) of + case ejabberd_config:get_local_option(listen) of undefined -> ignore; Ls -> diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 0ef1a93e2..975990263 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -41,6 +41,7 @@ init() -> {local_content, true}, {attributes, record_info(fields, local_route)}]), + mnesia:add_table_copy(local_route, node(), ram_copies), loop(). loop() -> diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index d834dea5c..3a2d19a58 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -20,7 +20,7 @@ -include("ejabberd.hrl"). -record(s2s, {fromto, node, key}). --record(mys2s, {fromto, pid}). +-record(local_s2s, {fromto, pid}). start() -> @@ -31,10 +31,11 @@ init() -> mnesia:create_table(s2s,[{ram_copies, [node()]}, {attributes, record_info(fields, s2s)}]), mnesia:add_table_index(session, node), - mnesia:create_table(mys2s, + mnesia:create_table(local_s2s, [{ram_copies, [node()]}, {local_content, true}, - {attributes, record_info(fields, mys2s)}]), + {attributes, record_info(fields, local_s2s)}]), + mnesia:add_table_copy(local_s2s, node(), ram_copies), mnesia:subscribe(system), loop(). @@ -70,7 +71,7 @@ loop() -> remove_connection(FromTo) -> F = fun() -> - mnesia:delete({mys2s, FromTo}), + mnesia:delete({local_s2s, FromTo}), mnesia:delete({s2s, FromTo}) end, mnesia:transaction(F). @@ -133,8 +134,8 @@ try_register(FromTo) -> mnesia:write(#s2s{fromto = FromTo, node = node(), key = Key}), - mnesia:write(#mys2s{fromto = FromTo, - pid = self()}), + mnesia:write(#local_s2s{fromto = FromTo, + pid = self()}), {key, Key}; _ -> false @@ -159,7 +160,7 @@ do_route(From, To, Packet) -> FromTo = {MyServer, Server}, Key = randoms:get_string(), F = fun() -> - case mnesia:read({mys2s, FromTo}) of + case mnesia:read({local_s2s, FromTo}) of [] -> case mnesia:read({s2s, FromTo}) of [Er] -> @@ -172,7 +173,7 @@ do_route(From, To, Packet) -> new end; [El] -> - {local, El#mys2s.pid} + {local, El#local_s2s.pid} end end, case mnesia:transaction(F) of @@ -192,8 +193,10 @@ do_route(From, To, Packet) -> {atomic, new} -> ?DEBUG("starting new s2s connection~n", []), Pid = ejabberd_s2s_out:start(MyServer, Server, {new, Key}), - mnesia:transaction(fun() -> mnesia:write(#mys2s{fromto = FromTo, - pid = Pid}) end), + mnesia:transaction(fun() -> + mnesia:write(#local_s2s{fromto = FromTo, + pid = Pid}) + end), {xmlelement, Name, Attrs, Els} = Packet, NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From), jlib:jid_to_string(To), diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index cbb695bea..9e74177ea 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -22,7 +22,7 @@ -include("ejabberd.hrl"). -record(session, {ur, user, node}). --record(mysession, {ur, pid}). +-record(local_session, {ur, pid}). -record(presence, {ur, user, priority}). start() -> @@ -34,10 +34,11 @@ init() -> {attributes, record_info(fields, session)}]), mnesia:add_table_index(session, user), mnesia:add_table_index(session, node), - mnesia:create_table(mysession, + mnesia:create_table(local_session, [{ram_copies, [node()]}, {local_content, true}, - {attributes, record_info(fields, mysession)}]), + {attributes, record_info(fields, local_session)}]), + mnesia:add_table_copy(local_session, node(), ram_copies), mnesia:create_table(presence, [{ram_copies, [node()]}, {attributes, record_info(fields, presence)}]), @@ -111,15 +112,15 @@ replace_my_connection(User, Resource) -> LUser = jlib:tolower(User), F = fun() -> UR = {LUser, Resource}, - Es = mnesia:read({mysession, UR}), - mnesia:delete({mysession, UR}), + Es = mnesia:read({local_session, UR}), + mnesia:delete({local_session, UR}), Es end, case mnesia:transaction(F) of {atomic, Rs} -> lists:foreach( fun(R) -> - R#mysession.pid ! replaced + R#local_session.pid ! replaced end, Rs); _ -> false @@ -129,7 +130,7 @@ remove_connection(User, Resource) -> LUser = jlib:tolower(User), F = fun() -> UR = {LUser, Resource}, - mnesia:delete({mysession, UR}), + mnesia:delete({local_session, UR}), mnesia:delete({session, UR}) end, mnesia:transaction(F). @@ -138,15 +139,15 @@ replace_and_register_my_connection(User, Resource, Pid) -> LUser = jlib:tolower(User), F = fun() -> UR = {LUser, Resource}, - Es = mnesia:read({mysession, UR}), - mnesia:write(#mysession{ur = UR, pid = Pid}), + Es = mnesia:read({local_session, UR}), + mnesia:write(#local_session{ur = UR, pid = Pid}), Es end, case mnesia:transaction(F) of {atomic, Rs} -> lists:foreach( fun(R) -> - R#mysession.pid ! replaced + R#local_session.pid ! replaced end, Rs); _ -> false @@ -262,13 +263,13 @@ do_route(From, To, Packet) -> ?DEBUG("packet droped~n", []) end; [Ses] -> - case mnesia:dirty_read({mysession, LUR}) of + case mnesia:dirty_read({local_session, LUR}) of [] -> Node = Ses#session.node, ?DEBUG("sending to node ~p~n", [Node]), {ejabberd_sm, Node} ! {route, From, To, Packet}; [El] -> - Pid = El#mysession.pid, + Pid = El#local_session.pid, ?DEBUG("sending to process ~p~n", [Pid]), Pid ! {route, From, To, Packet} end @@ -341,7 +342,7 @@ dirty_get_sessions_list() -> mnesia:dirty_all_keys(session). dirty_get_my_sessions_list() -> - mnesia:dirty_all_keys(mysession). + mnesia:dirty_all_keys(local_session). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/mod_configure.erl b/src/mod_configure.erl index b72f180b5..5ae67513f 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -11,7 +11,8 @@ -vsn('$Revision$ '). -export([start/0, - process_local_iq/3]). + process_local_iq/3, + process_sm_iq/3]). -include("ejabberd.hrl"). -include("namespaces.hrl"). @@ -19,7 +20,10 @@ start() -> ejabberd_local:register_iq_handler(?NS_XDATA, - ?MODULE, process_local_iq). + ?MODULE, process_local_iq), + ejabberd_sm:register_iq_handler(?NS_XDATA, + ?MODULE, process_sm_iq), + ok. process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> @@ -92,6 +96,12 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> {"label", translate:translate(Lang, Label)}, {"var", Var}], []}). +-define(XFIELD(Type, Label, Var, Val), + {xmlelement, "field", [{"type", Type}, + {"label", translate:translate(Lang, Label)}, + {"var", Var}], + [{xmlelement, "value", [], [{xmlcdata, Val}]}]}). + -define(TABLEFIELD(Table, Val), {xmlelement, "field", [{"type", "list-single"}, {"label", atom_to_list(Table)}, @@ -105,7 +115,8 @@ process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> "RAM and disc copy")}], [{xmlelement, "value", [], [{xmlcdata, "disc_copies"}]}]}, {xmlelement, "option", [{"label", - translate:translate(Lang, "Disk copy")}], + translate:translate(Lang, + "Disc only copy")}], [{xmlelement, "value", [], [{xmlcdata, "disc_only_copies"}]}]}, {xmlelement, "option", [{"label", translate:translate(Lang, "Remote copy")}], @@ -123,6 +134,7 @@ get_form(["running nodes", ENode, "DB"], Lang) -> {badrpc, Reason} -> {error, "500", "Internal Server Error"}; Tables -> + STables = lists:sort(Tables), {result, [{xmlelement, "title", [], [{xmlcdata, translate:translate( @@ -142,11 +154,27 @@ get_form(["running nodes", ENode, "DB"], Lang) -> Type -> ?TABLEFIELD(Table, Type) end - end, Tables) + end, STables) ]} end end; +get_form(["config", "hostname"], Lang) -> + {result, [{xmlelement, "title", [], + [{xmlcdata, + translate:translate( + Lang, "DB Tables Configuration")}]}, + {xmlelement, "instructions", [], + [{xmlcdata, + translate:translate( + Lang, "Choose host name")}]}, + {xmlelement, "field", [{"type", "text-single"}, + {"label", + translate:translate(Lang, "Host name")}, + {"var", "hostname"}], + [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]} + ]}; + get_form(_, Lang) -> {error, "503", "Service Unavailable"}. @@ -159,7 +187,7 @@ set_form(["running nodes", ENode, "DB"], Lang, XData) -> Node -> lists:foreach( fun({SVar, SVals}) -> - % We believe that this allowed only for good peoples + % We believe that this is allowed only for good peoples Table = list_to_atom(SVar), Type = case SVals of ["unknown"] -> unknown; @@ -186,6 +214,19 @@ set_form(["running nodes", ENode, "DB"], Lang, XData) -> {result, []} end; +set_form(["config", "hostname"], Lang, XData) -> + case lists:keysearch("hostname", 1, XData) of + false -> + {error, "406", "Not Acceptable"}; + {value, {_, [""]}} -> + {error, "406", "Not Acceptable"}; + {value, {_, [NewName]}} -> + ejabberd_config:add_global_option(hostname, NewName), + {result, []}; + _ -> + {error, "406", "Not Acceptable"} + end; + set_form(_, Lang, XData) -> {error, "503", "Service Unavailable"}. @@ -204,4 +245,97 @@ search_running_node(SNode, [Node | Nodes]) -> search_running_node(SNode, Nodes) end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case acl:match_rule(configure, From) of + deny -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + allow -> + {User, _, _} = To, + 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_sm_form( + User, 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_sm_form(User, 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 + end. + + +get_sm_form(User, [], Lang) -> + {result, [{xmlelement, "title", [], + [{xmlcdata, + translate:translate( + Lang, "Administration of " ++ User)}]}, + %{xmlelement, "instructions", [], + % [{xmlcdata, + % translate:translate( + % Lang, "Choose host name")}]}, + ?XFIELD("text-private", "Password", "password", + ejabberd_auth:get_password_s(User)) + %{xmlelement, "field", [{"type", "text-single"}, + % {"label", + % translate:translate(Lang, "Host name")}, + % {"var", "hostname"}], + % [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]} + ]}; + +get_sm_form(_, _, Lang) -> + {error, "503", "Service Unavailable"}. + + +set_sm_form(_, _, Lang, XData) -> + {error, "503", "Service Unavailable"}. diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 9bf9c6dcf..b0574d31a 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -90,6 +90,7 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> {"name", "ejabberd"}], []}] ++ Features }]}; + ["config"] -> ?EMPTY_INFO_RESULT; ["online users"] -> ?EMPTY_INFO_RESULT; ["all users"] -> ?EMPTY_INFO_RESULT; ["outgoing s2s" | _] -> ?EMPTY_INFO_RESULT; @@ -98,7 +99,7 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> ["running nodes", ENode] -> {iq, ID, result, XMLNS, [{xmlelement, "query", - [{"xmlns", ?NS_DISCO_INFO}], + [{"xmlns", XMLNS}], [{xmlelement, "identity", [{"category", "ejabberd"}, {"type", "node"}, @@ -109,10 +110,14 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> ["running nodes", ENode, "DB"] -> {iq, ID, result, XMLNS, [{xmlelement, "query", - [{"xmlns", ?NS_DISCO_INFO}], + [{"xmlns", XMLNS}], [feature_to_xml({?NS_XDATA}) ] }]}; + ["config", _] -> + {iq, ID, result, XMLNS, + [{xmlelement, "query", [{"xmlns", XMLNS}], + [feature_to_xml({?NS_XDATA})]}]}; _ -> {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", @@ -141,13 +146,24 @@ get_local_items([], Server, Lang) -> lists:map(fun domain_to_xml/1, ejabberd_router:dirty_get_all_routes()), {result, - Domains ++ - [?NODE("Online Users", "online users"), - ?NODE("All Users", "all users"), - ?NODE("Outgoing S2S connections", "outgoing s2s"), - ?NODE("Running Nodes", "running nodes"), - ?NODE("Stopped Nodes", "stopped nodes") - ]}; + Domains ++ + [?NODE("Configuration", "config"), + ?NODE("Online Users", "online users"), + ?NODE("All Users", "all users"), + ?NODE("Outgoing S2S connections", "outgoing s2s"), + ?NODE("Running Nodes", "running nodes"), + ?NODE("Stopped Nodes", "stopped nodes") + ]}; + +get_local_items(["config"], Server, Lang) -> + {result, + [?NODE("Host Name", "config/hostname"), + ?NODE("ACLs", "config/acls"), + ?NODE("Access Rules", "config/access") + ]}; + +get_local_items(["config", _], Server, Lang) -> + {result, []}; get_local_items(["online users"], Server, Lang) -> {result, get_online_users()}; @@ -310,7 +326,10 @@ process_sm_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> [{xmlcdata, "Not Allowed"}]}]}; get -> case xml:get_tag_attr_s("node", SubEl) of - "" -> ?EMPTY_INFO_RESULT; + "" -> + {iq, ID, result, XMLNS, + [{xmlelement, "query", [{"xmlns", XMLNS}], + [feature_to_xml({?NS_XDATA})]}]}; _ -> {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error",