--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : acl.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(acl).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-export([start/0, add/2, match_rule/2, match_acl/2]).
+
+-include("ejabberd.hrl").
+
+start() ->
+ ets:new(acls, [bag, named_table, public]).
+
+
+add(ACLName, ACLData) ->
+ ets:insert(acls, {ACLName, ACLData}).
+
+match_rule(Rule, JID) ->
+ ACLs = ejabberd_config:get_option(Rule),
+ match_acls(ACLs, JID).
+
+match_acls([], _) ->
+ deny;
+match_acls([{Access, ACL} | ACLs], JID) ->
+ case match_acl(ACL, JID) of
+ true ->
+ Access;
+ _ ->
+ match_acls(ACLs, JID)
+ end.
+
+match_acl(ACL, JID) ->
+ {User, Server, Resource} = jlib:jid_tolower(JID),
+ lists:any(fun({_, Spec}) ->
+ case Spec of
+ all ->
+ true;
+ {user, U} ->
+ (U == User) and (?MYNAME == Server);
+ {user, U, S} ->
+ (U == User) and (S == Server);
+ {server, S} ->
+ S == Server
+ end
+ end, ets:lookup(acls, ACL)).
% $Id$
+{acl, admin, {user, "aleksey"}}.
+{acl, admin, {user, "ermine"}}.
+{acl, jabberorg, {server, "jabber.org"}}.
+{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
+
+{disco_admin, [{allow, admin},
+ {deny, all}]}.
{host, "e.localhost"}.
init() ->
register(ejabberd, self()),
- erlang:system_flag(fullsweep_after, 0),
+ %erlang:system_flag(fullsweep_after, 0),
error_logger:logfile({open, ?ERROR_LOG_PATH}),
randoms:start(),
ok = erl_ddll:load_driver(".", expat_erl),
db_init(),
sha:start(),
translate:start(),
+ acl:start(),
ejabberd_config:start(),
ejabberd_auth:start(),
ejabberd_router:start(),
%%% Id : $Id$
%%%----------------------------------------------------------------------
+-define(VERSION, "0.0.1-alpha").
+
%-define(ejabberd_debug, true).
%-define(DBGFSM, true).
load_file(File) ->
case file:consult(File) of
{ok, Terms} ->
- lists:foreach(fun({Opt, Val}) ->
- ets:insert(ejabberd_config, {Opt, Val})
- end, Terms);
+ lists:foreach(fun process_term/1, Terms);
{error, Reason} ->
exit(file:format_error(Reason))
end.
+process_term(Term) ->
+ case Term of
+ {acl, ACLName, ACLData} ->
+ acl:add(ACLName, ACLData);
+ {Opt, Val} ->
+ ets:insert(ejabberd_config, {Opt, Val})
+ end.
+
get_option(Opt) ->
case ets:lookup(ejabberd_config, Opt) of
mod_offline:start(),
mod_echo:start(),
mod_private:start(),
+ mod_time:start(),
+ mod_version:start(),
ok.
init() ->
"message" ->
ok;
"presence" ->
- % TODO
ok;
_ ->
- % DROP
ok
end;
{"", _, _} ->
do_route(From, To, Packet) ->
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", [From, To, Packet]),
{DstNode, DstDomain, DstResourse} = To,
- case mnesia:dirty_read({local_route, DstDomain}) of
+ LDstDomain = jlib:tolower(DstDomain),
+ case mnesia:dirty_read({local_route, LDstDomain}) of
[] ->
- case mnesia:dirty_read({route, DstDomain}) of
+ case mnesia:dirty_read({route, LDstDomain}) of
[] ->
ejabberd_s2s ! {route, From, To, Packet};
[R] ->
set_presence/3,
unset_presence/2,
dirty_get_sessions_list/0,
+ dirty_get_my_sessions_list/0,
register_iq_handler/3]).
-include_lib("mnemosyne/include/mnemosyne.hrl").
ok
end;
_ ->
- UR = {User, Resource},
- Sess = mnesia:dirty_read({session, UR}),
+ LUR = {jlib:tolower(User), Resource},
+ Sess = mnesia:dirty_read({session, LUR}),
case Sess of
[] ->
if
?DEBUG("packet droped~n", [])
end;
[Ses] ->
- case mnesia:dirty_read({mysession, UR}) of
+ case mnesia:dirty_read({mysession, LUR}) of
[] ->
Node = Ses#session.node,
?DEBUG("sending to node ~p~n", [Node]),
dirty_get_sessions_list() ->
mnesia:dirty_all_keys(session).
+dirty_get_my_sessions_list() ->
+ mnesia:dirty_all_keys(mysession).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
is_iq_request_type/1,
iq_to_xml/1,
parse_xdata_submit/1,
+ timestamp_to_iso/1,
timestamp_to_xml/1]).
-include("namespaces.hrl").
parse_xdata_values(Els, Res).
+timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) ->
+ lists:flatten(
+ io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w",
+ [Year, Month, Day, Hour, Minute, Second])).
+
timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) ->
{xmlelement, "x",
[{"xmlns", ?NS_DELAY},
io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w",
[Year, Month, Day, Hour, Minute, Second]))}],
[]}.
+
[{"code", "405"}],
[{xmlcdata, "Not Allowed"}]}]};
get ->
- case string:tokens(xml:get_tag_attr_s("node", SubEl), "/") of
- [] ->
- Domains =
- lists:map(fun domain_to_xml/1,
- ejabberd_router:dirty_get_all_routes()),
- {iq, ID, result, XMLNS,
- [{xmlelement,
- "query",
- [{"xmlns", ?NS_DISCO_ITEMS}],
- Domains ++
- [{xmlelement, "item",
- [{"jid", jlib:jid_to_string(To)},
- {"name", translate:translate(Lang, "Online Users")},
- {"node", "online users"}], []},
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(To)},
- {"name", translate:translate(Lang, "All Users")},
- {"node", "all users"}], []},
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(To)},
- {"name", translate:translate(
- Lang, "Outgoing S2S connections")},
- {"node", "outgoing s2s"}], []}
- ]}]};
- ["online users"] ->
- {iq, ID, result, XMLNS,
- [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- get_online_users()
- }]};
- ["all users"] ->
- {iq, ID, result, XMLNS,
- [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- get_all_users()
- }]};
- ["outgoing s2s"] ->
- {iq, ID, result, XMLNS,
- [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- get_outgoing_s2s(Lang)
- }]};
- ["outgoing s2s", Host] ->
+ Node = string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
+
+ case get_local_items(Node, jlib:jid_to_string(To), Lang) of
+ {result, Res} ->
{iq, ID, result, XMLNS,
[{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- get_outgoing_s2s(Lang, Host)
+ Res
}]};
- _ ->
+ {error, Code, Desc} ->
{iq, ID, error, XMLNS,
[SubEl, {xmlelement, "error",
- [{"code", "501"}],
- [{xmlcdata, "Not Implemented"}]}]}
+ [{"code", Code}],
+ [{xmlcdata, Desc}]}]}
end
end.
["online users"] -> ?EMPTY_INFO_RESULT;
["all users"] -> ?EMPTY_INFO_RESULT;
["outgoing s2s" | _] -> ?EMPTY_INFO_RESULT;
+ ["running nodes"] -> ?EMPTY_INFO_RESULT;
+ ["stopped nodes"] -> ?EMPTY_INFO_RESULT;
+ ["running nodes", ENode] ->
+ {iq, ID, result, XMLNS, [{xmlelement,
+ "query",
+ [{"xmlns", ?NS_DISCO_INFO}],
+ [{xmlelement, "identity",
+ [{"category", "ejabberd"},
+ {"type", "node"},
+ {"name", ENode}], []},
+ feature_to_xml({?NS_STATS})
+ ]
+ }]};
_ ->
{iq, ID, error, XMLNS,
[SubEl, {xmlelement, "error",
{xmlelement, "item", [{"jid", Domain}], []}.
+-define(TOP_NODE(Name, Node),
+ {xmlelement, "item",
+ [{"jid", Server},
+ {"name", translate:translate(Lang, Name)},
+ {"node", Node}], []}).
+
+
+get_local_items([], Server, Lang) ->
+ Domains =
+ lists:map(fun domain_to_xml/1,
+ ejabberd_router:dirty_get_all_routes()),
+ {result,
+ Domains ++
+ [?TOP_NODE("Online Users", "online users"),
+ ?TOP_NODE("All Users", "all users"),
+ ?TOP_NODE("Outgoing S2S connections", "outgoing s2s"),
+ ?TOP_NODE("Running Nodes", "running nodes"),
+ ?TOP_NODE("Stopped Nodes", "stopped nodes")
+ ]};
+
+get_local_items(["online users"], Server, Lang) ->
+ {result, get_online_users()};
+
+get_local_items(["all users"], Server, Lang) ->
+ {result, get_all_users()};
+
+get_local_items(["outgoing s2s"], Server, Lang) ->
+ {result, get_outgoing_s2s(Lang)};
+
+get_local_items(["running nodes"], Server, Lang) ->
+ {result, get_running_nodes(Lang)};
+
+get_local_items(["stopped nodes"], Server, Lang) ->
+ {result, get_stopped_nodes(Lang)};
+
+get_local_items(["running nodes", _], Server, Lang) ->
+ {result, []};
+
+get_local_items(_, _, _) ->
+ {error, "501", "Not Implemented"}.
+
+
+
+
+
get_online_users() ->
case catch ejabberd_sm:dirty_get_sessions_list() of
{'EXIT', Reason} ->
end.
+get_running_nodes(Lang) ->
+ case catch mnesia:system_info(running_db_nodes) of
+ {'EXIT', Reason} ->
+ [];
+ DBNodes ->
+ lists:map(
+ fun(N) ->
+ S = atom_to_list(N),
+ {xmlelement, "item",
+ [{"jid", ?MYNAME},
+ {"node", "running nodes/" ++ S},
+ {"name", S}],
+ []}
+ end, lists:sort(DBNodes))
+ end.
+
+get_stopped_nodes(Lang) ->
+ case catch (lists:usort(mnesia:system_info(db_nodes) ++
+ mnesia:system_info(extra_db_nodes)) --
+ mnesia:system_info(running_db_nodes)) of
+ {'EXIT', Reason} ->
+ [];
+ DBNodes ->
+ lists:map(
+ fun(N) ->
+ S = atom_to_list(N),
+ {xmlelement, "item",
+ [{"jid", ?MYNAME},
+ {"node", "stopped nodes/" ++ S},
+ {"name", S}],
+ []}
+ end, lists:sort(DBNodes))
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
process_sm_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
{User, _, _} = To,
case Type of
Node = string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
Names = get_names(Els, []),
- {T, Res} = get_local_stats(Node, Names),
- case T of
- result ->
- {iq, ID, result, ?NS_STATS, Res};
- error ->
- {iq, ID, error, ?NS_STATS, [SubEl ++ Res]}
+ case get_local_stats(Node, Names) 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
-
- %case Node of
- %[] ->
- % {iq, ID, result, XMLNS,
- % [{xmlelement,
- % "query",
- % [{"xmlns", ?NS_STATS}],
- % [{xmlelement, "stat", [{"name", "time/uptime"}], []},
- % {xmlelement, "stat", [{"name", "time/cputime"}], []}
- % ]}]};
- %["online users"] ->
- % {iq, ID, result, XMLNS,
- % [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- % get_online_users()
- % }]};
- %["all users"] ->
- % {iq, ID, result, XMLNS,
- % [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- % get_all_users()
- % }]};
- %["outgoing s2s"] ->
- % {iq, ID, result, XMLNS,
- % [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- % get_outgoing_s2s(Lang)
- % }]};
- %["outgoing s2s", Host] ->
- % {iq, ID, result, XMLNS,
- % [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}],
- % get_outgoing_s2s(Lang, Host)
- % }]};
- %_ ->
- % {iq, ID, error, XMLNS,
- % [SubEl, {xmlelement, "error",
- % [{"code", "501"}],
- % [{xmlcdata, "Not Implemented"}]}]}
- %end
end.
get_names(Els, Res).
+-define(STAT(Name), {xmlelement, "stat", [{"name", Name}], []}).
+
get_local_stats([], []) ->
{result,
- [{xmlelement,
- "query",
- [{"xmlns", ?NS_STATS}],
- [{xmlelement, "stat", [{"name", "time/uptime"}], []},
- {xmlelement, "stat", [{"name", "time/cputime"}], []},
- {xmlelement, "stat", [{"name", "users/online"}], []},
- {xmlelement, "stat", [{"name", "users/total"}], []}
- ]}]};
+ [?STAT("users/online"),
+ ?STAT("users/total")
+ ]};
+
get_local_stats([], Names) ->
+ {result, lists:map(fun(Name) -> get_local_stat([], Name) end, Names)};
+
+get_local_stats(["running nodes", _], []) ->
{result,
- [{xmlelement,
- "query",
- [{"xmlns", ?NS_STATS}],
- lists:map(fun(Name) -> get_local_stat([], Name) end, Names)
- }]};
+ [?STAT("time/uptime"),
+ ?STAT("time/cputime"),
+ ?STAT("users/online")
+ ]};
+
+get_local_stats(["running nodes", ENode], Names) ->
+ case search_running_node(ENode) of
+ false ->
+ {error, "404", "Not Found"};
+ Node ->
+ {result,
+ lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}
+ end;
+
get_local_stats(_, _) ->
- {error,
- [{xmlelement, "error",
- [{"code", "501"}],
- [{xmlcdata, "Not Implemented"}]}]}.
+ {error, "501", "Not Implemented"}.
+
--define(STAT(Val, Unit),
+-define(STATVAL(Val, Unit),
{xmlelement, "stat",
[{"name", Name},
{"units", Unit},
[{xmlcdata, Desc}]}]}).
-get_local_stat([], Name) when Name == "time/uptime" ->
- ?STAT(io_lib:format("~.3f", [element(1, statistics(wall_clock))/1000]),
- "seconds");
-get_local_stat([], Name) when Name == "time/cputime" ->
- ?STAT(io_lib:format("~.3f", [element(1, statistics(runtime))/1000]),
- "seconds");
+%get_local_stat([], Name) when Name == "time/uptime" ->
+% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(wall_clock))/1000]),
+% "seconds");
+%get_local_stat([], Name) when Name == "time/cputime" ->
+% ?STATVAL(io_lib:format("~.3f", [element(1, statistics(runtime))/1000]),
+% "seconds");
get_local_stat([], Name) when Name == "users/online" ->
case catch ejabberd_sm:dirty_get_sessions_list() of
{'EXIT', Reason} ->
?STATERR("500", "Internal Server Error");
Users ->
- ?STAT(integer_to_list(length(Users)), "users")
+ ?STATVAL(integer_to_list(length(Users)), "users")
end;
get_local_stat([], Name) when Name == "users/total" ->
case catch ejabberd_auth:dirty_get_registered_users() of
{'EXIT', Reason} ->
?STATERR("500", "Internal Server Error");
Users ->
- ?STAT(integer_to_list(length(Users)), "users")
+ ?STATVAL(integer_to_list(length(Users)), "users")
end;
get_local_stat(_, Name) ->
?STATERR("404", "Not Found").
+
+
+
+get_node_stat(Node, Name) when Name == "time/uptime" ->
+ case catch rpc:call(Node, erlang, statistics, [wall_clock]) of
+ {badrpc, Reason} ->
+ ?STATERR("500", "Internal Server Error");
+ CPUTime ->
+ ?STATVAL(
+ io_lib:format("~.3f", [element(1, CPUTime)/1000]), "seconds")
+ end;
+
+get_node_stat(Node, Name) when Name == "time/cputime" ->
+ case catch rpc:call(Node, erlang, statistics, [runtime]) of
+ {badrpc, Reason} ->
+ ?STATERR("500", "Internal Server Error");
+ RunTime ->
+ ?STATVAL(
+ io_lib:format("~.3f", [element(1, RunTime)/1000]), "seconds")
+ end;
+
+get_node_stat(Node, Name) when Name == "users/online" ->
+ case catch rpc:call(Node, ejabberd_sm, dirty_get_my_sessions_list, []) of
+ {badrpc, Reason} ->
+ ?STATERR("500", "Internal Server Error");
+ Users ->
+ ?STATVAL(integer_to_list(length(Users)), "users")
+ end;
+get_node_stat(_, Name) ->
+ ?STATERR("404", "Not Found").
+
+
+search_running_node(SNode) ->
+ search_running_node(SNode, mnesia:system_info(running_db_nodes)).
+
+search_running_node(_, []) ->
+ false;
+search_running_node(SNode, [Node | Nodes]) ->
+ case atom_to_list(Node) of
+ SNode ->
+ Node;
+ _ ->
+ search_running_node(SNode, Nodes)
+ end.
+
--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : mod_time.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(mod_time).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-export([start/0,
+ process_local_iq/3]).
+
+-include("ejabberd.hrl").
+-include("namespaces.hrl").
+
+
+
+
+start() ->
+ ejabberd_local:register_iq_handler(?NS_TIME,
+ ?MODULE, process_local_iq).
+
+
+
+process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
+ case Type of
+ set ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", "405"}],
+ [{xmlcdata, "Not Allowed"}]}]};
+ get ->
+ UTC = jlib:timestamp_to_iso(calendar:universal_time()),
+ {iq, ID, result, XMLNS,
+ [{xmlelement, "query",
+ [{"xmlns", ?NS_TIME}],
+ [{xmlelement, "utc", [],
+ [{xmlcdata, UTC}]}
+ ]}]}
+ end.
+
+
--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : mod_version.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(mod_version).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-export([start/0,
+ process_local_iq/3]).
+
+-include("ejabberd.hrl").
+-include("namespaces.hrl").
+
+
+
+
+start() ->
+ ejabberd_local:register_iq_handler(?NS_VERSION,
+ ?MODULE, process_local_iq).
+
+
+
+process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) ->
+ case Type of
+ set ->
+ {iq, ID, error, XMLNS,
+ [SubEl, {xmlelement, "error",
+ [{"code", "405"}],
+ [{xmlcdata, "Not Allowed"}]}]};
+ get ->
+ OSType = case os:type() of
+ {Osfamily, Osname} ->
+ atom_to_list(Osfamily) ++ "/" ++
+ atom_to_list(Osname);
+ Osfamily ->
+ atom_to_list(Osfamily)
+ end,
+ OSVersion = case os:version() of
+ {Major, Minor, Release} ->
+ lists:flatten(
+ io_lib:format("~w.~w.~w",
+ [Major, Minor, Release]));
+ VersionString ->
+ VersionString
+ end,
+ OS = OSType ++ " " ++ OSVersion,
+ {iq, ID, result, XMLNS,
+ [{xmlelement, "query",
+ [{"xmlns", ?NS_VERSION}],
+ [{xmlelement, "name", [],
+ [{xmlcdata, "ejabberd"}]},
+ {xmlelement, "version", [],
+ [{xmlcdata, ?VERSION}]},
+ {xmlelement, "os", [],
+ [{xmlcdata, OS}]}
+ ]}]}
+ end.
+
+
%%%----------------------------------------------------------------------
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
--define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
--define(NS_VCARD, "vcard-temp").
--define(NS_AUTH, "jabber:iq:auth").
--define(NS_REGISTER, "jabber:iq:register").
--define(NS_SEARCH, "jabber:iq:search").
--define(NS_ROSTER, "jabber:iq:roster").
--define(NS_PRIVATE, "jabber:iq:private").
--define(NS_XDATA, "jabber:x:data").
--define(NS_DELAY, "jabber:x:delay").
--define(NS_EVENT, "jabber:x:event").
--define(NS_STATS, "http://jabber.org/protocol/stats").
+-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
+-define(NS_VCARD, "vcard-temp").
+-define(NS_AUTH, "jabber:iq:auth").
+-define(NS_REGISTER, "jabber:iq:register").
+-define(NS_SEARCH, "jabber:iq:search").
+-define(NS_ROSTER, "jabber:iq:roster").
+-define(NS_PRIVATE, "jabber:iq:private").
+-define(NS_VERSION, "jabber:iq:version").
+-define(NS_TIME, "jabber:iq:time").
+-define(NS_XDATA, "jabber:x:data").
+-define(NS_DELAY, "jabber:x:delay").
+-define(NS_EVENT, "jabber:x:event").
+-define(NS_STATS, "http://jabber.org/protocol/stats").