diff --git a/mod_admin_extra/src/mod_admin_extra.erl b/mod_admin_extra/src/mod_admin_extra.erl index 24eefa1..d71803b 100644 --- a/mod_admin_extra/src/mod_admin_extra.erl +++ b/mod_admin_extra/src/mod_admin_extra.erl @@ -219,22 +219,22 @@ commands() -> #ejabberd_commands{name = kick_session, tags = [session], desc = "Kick a user session", module = ?MODULE, function = kick_session, - args = [{user, string}, {host, string}, {resource, string}, {reason, string}], + args = [{user, binary}, {host, binary}, {resource, binary}, {reason, binary}], result = {res, rescode}}, #ejabberd_commands{name = status_num_host, tags = [session, stats], desc = "Number of logged users with this status in host", module = ?MODULE, function = status_num, - args = [{host, string}, {status, string}], + args = [{host, binary}, {status, binary}], result = {users, integer}}, #ejabberd_commands{name = status_num, tags = [session, stats], desc = "Number of logged users with this status", module = ?MODULE, function = status_num, - args = [{status, string}], + args = [{status, binary}], result = {users, integer}}, #ejabberd_commands{name = status_list_host, tags = [session], desc = "List of users logged in host with their statuses", module = ?MODULE, function = status_list, - args = [{host, string}, {status, string}], + args = [{host, binary}, {status, binary}], result = {users, {list, {userstatus, {tuple, [ {user, string}, @@ -247,7 +247,7 @@ commands() -> #ejabberd_commands{name = status_list, tags = [session], desc = "List of logged users with this status", module = ?MODULE, function = status_list, - args = [{status, string}], + args = [{status, binary}], result = {users, {list, {userstatus, {tuple, [ {user, string}, @@ -320,47 +320,47 @@ commands() -> desc = "Get content from a vCard field", longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, module = ?MODULE, function = get_vcard, - args = [{user, string}, {host, string}, {name, string}], + args = [{user, binary}, {host, binary}, {name, binary}], result = {content, string}}, #ejabberd_commands{name = get_vcard2, tags = [vcard], desc = "Get content from a vCard field", longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, module = ?MODULE, function = get_vcard, - args = [{user, string}, {host, string}, {name, string}, {subname, string}], + args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}], result = {content, string}}, #ejabberd_commands{name = get_vcard2_multi, tags = [vcard], desc = "Get multiple contents from a vCard field (requires exmpp installed)", longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, module = ?MODULE, function = get_vcard_multi, - args = [{user, string}, {host, string}, {name, string}, {subname, string}], + args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}], result = {contents, {list, string}}}, #ejabberd_commands{name = set_vcard, tags = [vcard], desc = "Set content in a vCard field", longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP, module = ?MODULE, function = set_vcard, - args = [{user, string}, {host, string}, {name, string}, {content, string}], + args = [{user, binary}, {host, binary}, {name, binary}, {content, binary}], result = {res, rescode}}, #ejabberd_commands{name = set_vcard2, tags = [vcard], desc = "Set content in a vCard subfield", longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, module = ?MODULE, function = set_vcard, - args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}], + args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {content, binary}], result = {res, rescode}}, #ejabberd_commands{name = set_vcard2_multi, tags = [vcard], desc = "Set multiple contents in a vCard subfield", longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP, module = ?MODULE, function = set_vcard, - args = [{user, string}, {host, string}, {name, string}, {subname, string}, {contents, {list, string}}], + args = [{user, binary}, {host, binary}, {name, binary}, {subname, binary}, {contents, {list, binary}}], result = {res, rescode}}, #ejabberd_commands{name = add_rosteritem, tags = [roster], desc = "Add an item to a user's roster (supports ODBC)", module = ?MODULE, function = add_rosteritem, - args = [{localuser, string}, {localserver, string}, - {user, string}, {server, string}, - {nick, string}, {group, string}, - {subs, string}], + args = [{localuser, binary}, {localserver, binary}, + {user, binary}, {server, binary}, + {nick, binary}, {group, binary}, + {subs, binary}], result = {res, rescode}}, %%{"", "subs= none, from, to or both"}, %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, @@ -368,8 +368,8 @@ commands() -> #ejabberd_commands{name = delete_rosteritem, tags = [roster], desc = "Delete an item from a user's roster (supports ODBC)", module = ?MODULE, function = delete_rosteritem, - args = [{localuser, string}, {localserver, string}, - {user, string}, {server, string}], + args = [{localuser, binary}, {localserver, binary}, + {user, binary}, {server, binary}], result = {res, rescode}}, #ejabberd_commands{name = process_rosteritems, tags = [roster], desc = "List or delete rosteritems that match filtering options", @@ -413,7 +413,7 @@ commands() -> #ejabberd_commands{name = get_roster, tags = [roster], desc = "Get roster of a local user", module = ?MODULE, function = get_roster, - args = [{user, string}, {host, string}], + args = [{user, binary}, {host, binary}], result = {contacts, {list, {contact, {tuple, [ {jid, string}, {nick, string}, @@ -809,20 +809,20 @@ kick_session(User, Server, Resource, ReasonText) -> kick_this_session(User, Server, Resource, Reason) -> ejabberd_router:route( - jlib:make_jid("", "", ""), + jlib:make_jid(<<>>, <<>>, <<>>), jlib:make_jid(User, Server, Resource), - {xmlelement, "broadcast", [], [{exit, Reason}]}). + {xmlel, <<"broadcast">>, [], [{exit, Reason}]}). status_num(Host, Status) -> length(get_status_list(Host, Status)). status_num(Status) -> - status_num("all", Status). + status_num(<<"all">>, Status). status_list(Host, Status) -> Res = get_status_list(Host, Status), [{U, S, R, P, St} || {U, S, R, P, St} <- Res]. status_list(Status) -> - status_list("all", Status). + status_list(<<"all">>, Status). get_status_list(Host, Status_required) -> @@ -831,7 +831,7 @@ get_status_list(Host, Status_required) -> %% Reformat the list Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions], Fhost = case Host of - "all" -> + <<"all">> -> %% All hosts are requested, so dont filter at all fun(_, _) -> true end; _ -> @@ -843,7 +843,7 @@ get_status_list(Host, Status_required) -> Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3], %% Filter by status Fstatus = case Status_required of - "all" -> + <<"all">> -> fun(_, _) -> true end; _ -> fun(A, B) -> A == B end @@ -882,18 +882,18 @@ dirty_get_sessions_list2() -> %% Make string more print-friendly stringize(String) -> %% Replace newline characters with other code - ejabberd_regexp:greplace(String, "\n", "\\n"). + ejabberd_regexp:greplace(String, <<"\n">>, <<"\\n">>). set_presence(User, Host, Resource, Type, Show, Status, Priority) -> Pid = ejabberd_sm:get_session_pid(User, Host, Resource), USR = User ++ "@" ++ Host ++ "/" ++ Resource, US = User ++ "@" ++ Host, Message = {route_xmlstreamelement, - {xmlelement, "presence", - [{"from", USR}, {"to", US}, {"type", Type}], - [{xmlelement, "show", [], [{xmlcdata, Show}]}, - {xmlelement, "status", [], [{xmlcdata, Status}]}, - {xmlelement, "priority", [], [{xmlcdata, Priority}]}]}}, + {xmlel, <<"presence">>, + [{<<"from">>, USR}, {<<"to">>, US}, {<<"type">>, Type}], + [{xmlel, <<"show">>, [], [{xmlcdata, Show}]}, + {xmlel, <<"status">>, [], [{xmlcdata, Status}]}, + {xmlel, <<"priority">>, [], [{xmlcdata, Priority}]}]}}, Pid ! Message. user_sessions_info(User, Host) -> @@ -929,14 +929,14 @@ user_sessions_info(User, Host) -> set_nickname(User, Host, Nickname) -> R = mod_vcard:process_sm_iq( - {jid, User, Host, "", User, Host, ""}, - {jid, User, Host, "", User, Host, ""}, - {iq, "", set, "", "en", - {xmlelement, "vCard", - [{"xmlns", "vcard-temp"}], [ - {xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]} - ] - }}), + {jid, User, Host, <<>>, User, Host, <<>>}, + {jid, User, Host, <<>>, User, Host, <<>>}, + {iq, <<>>, set, <<>>, <<"en">>, + {xmlel, <<"vCard">>, [ + {<<"xmlns">>, <<"vcard-temp">>}], [ + {xmlel, <<"NICKNAME">>, [], [{xmlcdata, Nickname}]} + ] + }}), case R of {iq, [], result, [], _L, []} -> ok; @@ -966,9 +966,9 @@ set_vcard(User, Host, Name, Subname, SomeContent) -> %% Internal vcard get_module_resource(Server) -> - case gen_mod:get_module_opt(Server, ?MODULE, module_resource, none) of - none -> atom_to_list(?MODULE); - R when is_list(R) -> R + case gen_mod:get_module_opt(Server, ?MODULE, module_resource, fun(A) -> A end, none) of + none -> list_to_binary(atom_to_list(?MODULE)); + R when is_binary(R) -> R end. get_vcard_content(User, Server, Data) -> @@ -1013,8 +1013,8 @@ get_subtag_exmpp(Xmlelement, Name) -> set_vcard_content(User, Server, Data, SomeContent) -> ContentList = case SomeContent of - [Char | _] when not is_list(Char) -> [SomeContent]; - [Char | _] when is_list(Char) -> SomeContent + [Bin | _] when is_binary(Bin) -> SomeContent; + Bin when is_binary(Bin) -> [SomeContent] end, [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}), JID = jlib:make_jid(User, Server, get_module_resource(Server)), @@ -1031,7 +1031,7 @@ set_vcard_content(User, Server, Data, SomeContent) -> end, %% Build new vcard - SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4}, + SubEl = {xmlel, <<"vCard">>, [{<<"xmlns">>,<<"vcard-temp">>}], A4}, IQ2 = #iq{type=set, sub_el = SubEl}, Module:Function(JID, JID, IQ2), @@ -1042,18 +1042,18 @@ update_vcard_els(Data, ContentList, Els1) -> [Data1 | Data2] = Data, NewEls = case Data2 of [] -> - [{xmlelement, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList]; + [{xmlel, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList]; [D2] -> OldEl = case lists:keysearch(Data1, 2, Els2) of {value, A} -> A; - false -> {xmlelement, Data1, [], []} + false -> {xmlel, Data1, [], []} end, - {xmlelement, _, _, ContentOld1} = OldEl, - Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]} || Content <- ContentList], + {xmlel, _, _, ContentOld1} = OldEl, + Content2 = [{xmlel, D2, [], [{xmlcdata,Content}]} || Content <- ContentList], ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2], ContentOld3 = lists:keysort(2, ContentOld2), ContentNew = lists:keymerge(2, Content2, ContentOld3), - [{xmlelement, Data1, [], ContentNew}] + [{xmlel, Data1, [], ContentNew}] end, Els3 = lists:keydelete(Data1, 2, Els2), lists:keymerge(2, NewEls, Els3). @@ -1064,7 +1064,7 @@ update_vcard_els(Data, ContentList, Els1) -> %%% add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> - case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of + case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs, []) of {atomic, ok} -> push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}), ok; @@ -1076,16 +1076,11 @@ add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) -> subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs). subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) -> - SubscriptionS = case is_atom(Subscription) of - true -> atom_to_list(Subscription); - false -> Subscription - end, - ItemEl = build_roster_item(User, Server, {add, Nick, SubscriptionS, Group}), - {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]), - M:set_items( + ItemEl = build_roster_item(User, Server, {add, Nick, Subscription, Group}), + mod_roster:set_items( LU, LS, - {xmlelement,"query", - [{"xmlns","jabber:iq:roster"}], + {xmlel, <<"query">>, + [{<<"xmlns">>, <<"jabber:iq:roster">>}], [ItemEl]}). delete_rosteritem(LocalUser, LocalServer, User, Server) -> @@ -1099,22 +1094,12 @@ delete_rosteritem(LocalUser, LocalServer, User, Server) -> unsubscribe(LU, LS, User, Server) -> ItemEl = build_roster_item(User, Server, remove), - {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]), - M:set_items( + mod_roster:set_items( LU, LS, - {xmlelement,"query", - [{"xmlns","jabber:iq:roster"}], + {xmlel, <<"query">>, + [{<<"xmlns">>, <<"jabber:iq:roster">>}], [ItemEl]}). -loaded_module(Domain,Options) -> - LoadedModules = gen_mod:loaded_modules(Domain), - case lists:filter(fun(Module) -> - lists:member(Module, LoadedModules) - end, Options) of - [M|_] -> {ok, M}; - [] -> {error,not_found} - end. - %% ----------------------------- %% Get Roster %% ----------------------------- @@ -1133,11 +1118,10 @@ make_roster_xmlrpc(Roster) -> Subs = atom_to_list(Item#roster.subscription), Ask = atom_to_list(Item#roster.ask), Groups = case Item#roster.groups of - [] -> [""]; + [] -> [<<>>]; Gs -> Gs end, - ItemsX = [{JIDS, Nick, Subs, Ask, Group} - || Group <- Groups], + ItemsX = [{JIDS, Nick, Subs, Ask, Group} || Group <- Groups], ItemsX ++ Res end, [], @@ -1150,7 +1134,7 @@ make_roster_xmlrpc(Roster) -> push_roster(File, User, Server) -> {ok, [Roster]} = file:consult(File), - subscribe_roster({User, Server, "", User}, Roster). + subscribe_roster({User, Server, <<>>, User}, Roster). push_roster_all(File) -> {ok, [Roster]} = file:consult(File), @@ -1171,7 +1155,7 @@ subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) - subscribe_roster({Name, Server, Group, Nick}, Roster); %% Subscribe Name2 to Name1 subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) -> - subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []), + subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, <<"both">>, []), subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). push_alltoall(S, G) -> @@ -1203,39 +1187,37 @@ push_roster_item(LU, LS, R, U, S, Action) -> ejabberd_router:route(LJID, LJID, ResIQ). build_roster_item(U, S, {add, Nick, Subs, Group}) -> - {xmlelement, "item", - [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))}, - {"name", Nick}, - {"subscription", Subs}], - [{xmlelement, "group", [], [{xmlcdata, Group}]}] + {xmlel, <<"item">>, + [{<<"jid">>, jlib:jid_to_string(jlib:make_jid(U, S, <<>>))}, + {<<"name">>, Nick}, + {<<"subscription">>, Subs}], + [{xmlel, <<"group">>, [], [{xmlcdata, Group}]}] }; build_roster_item(U, S, remove) -> - {xmlelement, "item", - [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))}, - {"subscription", "remove"}], + {xmlel, <<"item">>, + [{<<"jid">>, jlib:jid_to_string(jlib:make_jid(U, S, <<>>))}, + {<<"subscription">>, <<"remove">>}], [] }. build_iq_roster_push(Item) -> - {xmlelement, "iq", - [{"type", "set"}, {"id", "push"}], - [{xmlelement, "query", - [{"xmlns", ?NS_ROSTER}], + {xmlel, <<"iq">>, + [{<<"type">>, <<"set">>}, {<<"id">>, <<"push">>}], + [{xmlel, <<"query">>, + [{<<"xmlns">>, ?NS_ROSTER}], [Item] } ] }. build_broadcast(U, S, {add, _Nick, Subs, _Group}) -> - build_broadcast(U, S, list_to_atom(Subs)); + build_broadcast(U, S, list_to_atom(binary_to_list(Subs))); build_broadcast(U, S, remove) -> build_broadcast(U, S, none); -%% @spec (U::string(), S::string(), Subs::atom()) -> any() +%% @spec (U::binary(), S::binary(), Subs::atom()) -> any() %% Subs = both | from | to | none build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) -> - {xmlelement, "broadcast", [], - [{item, {U, S, ""}, SubsAtom}] - }. + {broadcast, {item, {U, S, <<>>}, SubsAtom}}. %%% %%% Last Activity @@ -1277,15 +1259,15 @@ set_last(User, Server, Timestamp, Status) -> %% Cluth private_get(Username, Host, Element, Ns) -> - From = jlib:make_jid(Username, Host, ""), - To = jlib:make_jid(Username, Host, ""), - IQ = {iq, "", get, ?NS_PRIVATE, "", - {xmlelement,"query", - [{"xmlns",?NS_PRIVATE}], - [{xmlelement, Element, [{"xmlns", Ns}], []}]}}, + From = jlib:make_jid(Username, Host, <<>>), + To = jlib:make_jid(Username, Host, <<>>), + IQ = {iq, <<>>, get, ?NS_PRIVATE, <<>>, + {xmlel, <<"query">>, + [{<<"xmlns">>,?NS_PRIVATE}], + [{xmlel, Element, [{<<"xmlns">>, Ns}], []}]}}, ResIq = mod_private:process_sm_iq(From, To, IQ), - [{xmlelement,"query", - [{"xmlns","jabber:iq:private"}], + [{xmlel, <<"query">>, + [{<<"xmlns">>, <<"jabber:iq:private">>}], [SubEl]}] = ResIq#iq.sub_el, xml:element_to_string(SubEl). @@ -1300,11 +1282,11 @@ private_set(Username, Host, ElementString) -> end. private_set2(Username, Host, Xml) -> - From = jlib:make_jid(Username, Host, ""), - To = jlib:make_jid(Username, Host, ""), - IQ = {iq, "", set, ?NS_PRIVATE, "", - {xmlelement,"query", - [{"xmlns",?NS_PRIVATE}], + From = jlib:make_jid(Username, Host, <<>>), + To = jlib:make_jid(Username, Host, <<>>), + IQ = {iq, <<>>, set, ?NS_PRIVATE, <<>>, + {xmlel, <<"query">>, + [{<<"xmlns">>, ?NS_PRIVATE}], [Xml]}}, mod_private:process_sm_iq(From, To, IQ), ok. @@ -1341,7 +1323,7 @@ srg_get_info(Group, Host) -> srg_get_members(Group, Host) -> Members = mod_shared_roster:get_group_explicit_users(Host,Group), - [jlib:jid_to_string(jlib:make_jid(MUser, MServer, <<"">>)) + [jlib:jid_to_string(jlib:make_jid(MUser, MServer, <<>>)) || {MUser, MServer} <- Members]. srg_user_add(User, Host, Group, GroupHost) -> @@ -1383,7 +1365,7 @@ send_packet_all_resources(FromJIDString, ToJIDString, Packet) -> ToUser = ToJID#jid.user, ToServer = ToJID#jid.server, case ToJID#jid.resource of - "" -> + <<>> -> send_packet_all_resources(FromJID, ToUser, ToServer, Packet); Res -> send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet) @@ -1392,7 +1374,7 @@ send_packet_all_resources(FromJIDString, ToJIDString, Packet) -> send_packet_all_resources(FromJID, ToUser, ToServer, Packet) -> case ejabberd_sm:get_user_resources(ToUser, ToServer) of [] -> - send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet); + send_packet_all_resources(FromJID, ToUser, ToServer, <<>>, Packet); ToResources -> lists:foreach( fun(ToResource) -> @@ -1438,7 +1420,7 @@ privacy_set(Username, Host, QueryS) -> From = jlib:string_to_jid(Username ++ "@" ++ Host), To = jlib:string_to_jid(Host), QueryEl = xml_stream:parse_element(QueryS), - StanzaEl = {xmlelement, "iq", [{"type", "set"}], [QueryEl]}, + StanzaEl = {xmlel, <<"iq">>, [{<<"type">>, <<"set">>}], [QueryEl]}, IQ = jlib:iq_query_info(StanzaEl), ejabberd_hooks:run_fold( privacy_iq_set, diff --git a/mod_muc_admin/src/mod_muc_admin.erl b/mod_muc_admin/src/mod_muc_admin.erl index bf05c81..fd7982d 100644 --- a/mod_muc_admin/src/mod_muc_admin.erl +++ b/mod_muc_admin/src/mod_muc_admin.erl @@ -171,9 +171,9 @@ muc_online_rooms(ServerHost) -> fun({_, {Roomname, Host}, _}, Results) -> case MUCHost of global -> - [Roomname, <<"@">>, Host | Results]; + [<> | Results]; Host -> - [Roomname, <<"@">>, Host | Results]; + [<> | Results]; _ -> Results end diff --git a/mod_rest/src/mod_rest.erl b/mod_rest/src/mod_rest.erl index fab4a59..152ee0b 100644 --- a/mod_rest/src/mod_rest.erl +++ b/mod_rest/src/mod_rest.erl @@ -46,11 +46,7 @@ start(_Host, _Opts) -> stop(_Host) -> ok. -process([], #request{method = 'POST', - data = Data, - host = Host, - ip = ClientIp - }) -> +process([], #request{method = 'POST', data = Data, host = Host, ip = ClientIp}) -> try {ClientAddress, _PortNumber} = ClientIp, check_member_option(Host, ClientAddress, allowed_ips), @@ -111,7 +107,7 @@ try_get_option(Host, OptionName, DefaultValue) -> true -> ok; _ -> throw({module_must_be_started_in_vhost, ?MODULE, Host}) end, - gen_mod:get_module_opt(Host, ?MODULE, OptionName, fun(I) when I -> I end, DefaultValue). + gen_mod:get_module_opt(Host, ?MODULE, OptionName, fun(I) -> I end, DefaultValue). get_option_access(Host) -> try_get_option(Host, access_commands, []). @@ -123,12 +119,31 @@ check_stanza(Stanza, _From, To, Host) -> check_member_option(Host, StanzaType, allowed_stanza_types), allowed. +check_member_option(Host, ClientIp, allowed_ips) -> + true = case try_get_option(Host, allowed_ips, all) of + all -> true; + AllowedValues -> + case lists:all(fun(El) -> is_binary(El) end, AllowedValues) of + true -> + AllowedIps = lists:map(fun(El) -> + binary_to_ip_tuple(El) + end, + AllowedValues), + lists:member(ClientIp, AllowedIps); + false -> + lists:member(ClientIp, AllowedValues) + end + end; check_member_option(Host, Element, Option) -> true = case try_get_option(Host, Option, all) of all -> true; AllowedValues -> lists:member(Element, AllowedValues) end. +binary_to_ip_tuple(IpAddress) when is_binary(IpAddress) -> + {ok, IpTuple} = inet_parse:address(binary_to_list(IpAddress)), + IpTuple. + post_request(Stanza, From, To) -> case ejabberd_router:route(From, To, Stanza) of ok -> {200, [], <<"Ok">>}; diff --git a/mod_s2s_log/src/mod_s2s_log.erl b/mod_s2s_log/src/mod_s2s_log.erl index de403d2..2dae0a0 100644 --- a/mod_s2s_log/src/mod_s2s_log.erl +++ b/mod_s2s_log/src/mod_s2s_log.erl @@ -44,19 +44,18 @@ -include("ejabberd.hrl"). -define(PROCNAME, ?MODULE). --define(DEFAULT_FILENAME, "s2s.log"). +-define(DEFAULT_FILENAME, <<"s2s.log">>). -define(FILE_OPTS, [append,raw]). -record(config, {filename=?DEFAULT_FILENAME, iodevice}). %% For now we only support one log file for all vhosts. -start(Host, Opts) -> +start(_Host, Opts) -> %% ejabberd starts modules sequentially so we assume no race %% condition is possible here case whereis(?PROCNAME) of undefined -> - ?DEBUG("Starting mod_s2s_log ~p ~p~n", [Host, Opts]), - Filename = gen_mod:get_opt(filename, Opts, ?DEFAULT_FILENAME), + Filename = gen_mod:get_opt(filename, Opts, fun(V) -> V end, ?DEFAULT_FILENAME), %% TODO: Both hooks will need Host parameter for vhost support ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 55), ejabberd_hooks:add(s2s_connect_hook, ?MODULE, s2s_connect, 55), @@ -67,7 +66,6 @@ start(Host, Opts) -> end. init(Config)-> - ?DEBUG("Starting mod_s2s_log ~p with config ~p~n", [?MODULE, Config]), {ok, IOD} = file:open(Config#config.filename, ?FILE_OPTS), loop(Config#config{iodevice=IOD}). @@ -79,7 +77,6 @@ loop(Config) -> {reopen_log} -> file:close(Config#config.iodevice), {ok, IOD} = file:open(Config#config.filename, ?FILE_OPTS), - ?INFO_MSG("Reopened s2s log file", []), loop(Config#config{iodevice = IOD}); stop -> file:close(Config#config.iodevice), diff --git a/mod_statsdx/src/mod_statsdx.erl b/mod_statsdx/src/mod_statsdx.erl index 196c1a6..3b5422c 100644 --- a/mod_statsdx/src/mod_statsdx.erl +++ b/mod_statsdx/src/mod_statsdx.erl @@ -29,9 +29,12 @@ -include("ejabberd.hrl"). -include("ejabberd_commands.hrl"). -include("jlib.hrl"). +-include("logger.hrl"). -include("mod_roster.hrl"). --include("web/ejabberd_http.hrl"). --include("web/ejabberd_web_admin.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). + +-define(XCTB(Name, Text), ?XCT(list_to_binary(Name), list_to_binary(Text))). -define(PROCNAME, ejabberd_mod_statsdx). @@ -42,7 +45,7 @@ %%%% Module control start(Host, Opts) -> - Hooks = gen_mod:get_opt(hooks, Opts, false), + Hooks = gen_mod:get_opt(hooks, Opts, fun(O) -> is_atom(O) end, false), %% Default value for the counters CD = case Hooks of true -> 0; @@ -75,7 +78,9 @@ stop(Host) -> %%%================================== %%%% Stats Server -table_name(server) -> gen_mod:get_module_proc("server", mod_statsdx); +%%% +++ TODO: why server and "server" +table_name(server) -> gen_mod:get_module_proc(<<"server">>, mod_statsdx); +table_name("server") -> gen_mod:get_module_proc(<<"server">>, mod_statsdx); table_name(Host) -> gen_mod:get_module_proc(Host, mod_statsdx). initialize_stats_server() -> @@ -831,7 +836,7 @@ update_counter_create(Table, Element, C) -> get_tag_cdata_subtag(E, T) -> E2 = xml:get_subtag(E, T), case E2 of - false -> "unknown"; + false -> <<"unknown">>; _ -> xml:get_tag_cdata(E2) end. @@ -908,7 +913,7 @@ get_client_os(Server) -> CO1 = ets:match(table_name(Server), {{client_os, Server, '$1', '$2'}, '$3'}), CO2 = lists:map( fun([Cl, Os, A3]) -> - {lists:flatten([atom_to_list(Cl), "/", atom_to_list(Os)]), A3} + {list_to_binary(lists:flatten([atom_to_list(Cl), "/", atom_to_list(Os)])), A3} end, CO1 ), @@ -918,7 +923,7 @@ get_client_conntype(Server) -> CO1 = ets:match(table_name(Server), {{client_conntype, Server, '$1', '$2'}, '$3'}), CO2 = lists:map( fun([Cl, Os, A3]) -> - {lists:flatten([atom_to_list(Cl), "/", atom_to_list(Os)]), A3} + {list_to_binary(lists:flatten([atom_to_list(Cl), "/", atom_to_list(Os)])), A3} end, CO1 ), @@ -958,73 +963,73 @@ localtime_to_string({{Y, Mo, D},{H, Mi, S}}) -> %%%% Web Admin Menu web_menu_main(Acc, Lang) -> - Acc ++ [{"statsdx", ?T("Statistics Dx")}]. + Acc ++ [{<<"statsdx">>, ?T(<<"Statistics Dx">>)}]. web_menu_node(Acc, _Node, Lang) -> - Acc ++ [{"statsdx", ?T("Statistics Dx")}]. + Acc ++ [{<<"statsdx">>, ?T(<<"Statistics Dx">>)}]. web_menu_host(Acc, _Host, Lang) -> - Acc ++ [{"statsdx", ?T("Statistics Dx")}]. + Acc ++ [{<<"statsdx">>, ?T(<<"Statistics Dx">>)}]. %%%================================== %%%% Web Admin Page -web_page_main(_, #request{path=["statsdx"], lang = Lang} = _Request) -> - Res = [?XC("h1", ?T("Statistics")++" Dx"), - ?XC("h3", "Accounts"), - ?XAE("table", [], - [?XE("tbody", [ +web_page_main(_, #request{path=[<<"statsdx">>], lang = Lang} = _Request) -> + Res = [?XC(<<"h1">>, <<(?T(<<"Statistics">>))/binary, " Dx">>), + ?XC(<<"h3">>, <<"Accounts">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "registeredusers") ]) ]), - ?XC("h3", "Roster"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Roster">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "totalrosteritems"), do_stat(global, Lang, "meanitemsinroster"), - ?XE("tr", - [?XE("td", [?CT("Top rosters")]), - ?XE("td", [ - ?ACT("top/roster/30", "30"), ?C(", "), - ?ACT("top/roster/100", "100"), ?C(", "), - ?ACT("top/roster/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?CT(<<"Top rosters">>)]), + ?XE(<<"td">>, [ + ?ACT(<<"top/roster/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/roster/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/roster/500">>, <<"500">>) ])] ) ]) ]), - ?XC("h3", "Users"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Users">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "onlineusers"), do_stat(global, Lang, "offlinemsg"), - ?XE("tr", - [?XE("td", [?CT("Top offline message queues") ]), - ?XE("td", [ - ?ACT("top/offlinemsg/30", "30"), ?C(", "), - ?ACT("top/offlinemsg/100", "100"), ?C(", "), - ?ACT("top/offlinemsg/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?CT(<<"Top offline message queues">>) ]), + ?XE(<<"td">>, [ + ?ACT(<<"top/offlinemsg/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/offlinemsg/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/offlinemsg/500">>, <<"500">>) ])] ), do_stat(global, Lang, "vcards"), - ?XE("tr", - [?XE("td", [?CT("Top vCard sizes") ]), - ?XE("td", [ - ?ACT("top/vcard/5", "5"), ?C(", "), - ?ACT("top/vcard/30", "30"), ?C(", "), - ?ACT("top/vcard/100", "100"), ?C(", "), - ?ACT("top/vcard/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?CT(<<"Top vCard sizes">>) ]), + ?XE(<<"td">>, [ + ?ACT(<<"top/vcard/5">>, <<"5">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/500">>, <<"500">>) ])] ) ]) ]), - ?XC("h3", "MUC"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"MUC">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "totalmucrooms"), do_stat(global, Lang, "permmucrooms"), do_stat(global, Lang, "regmucrooms") ]) ]), - ?XC("h3", "Pub/Sub"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Pub/Sub">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "regpubsubnodes") ]) ]), @@ -1039,87 +1044,90 @@ web_page_main(_, #request{path=["statsdx"], lang = Lang} = _Request) -> %% [?XE("tbody", [ %% ]) %% ]), - ?XC("h3", "Sessions: " ++ get_stat_n("client")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client", server) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("os")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("os"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "os", server) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("os")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary, "/", (get_stat_n("os"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client_os", server) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("conntype")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("conntype"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "conntype", server) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("conntype")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary, "/", (get_stat_n("conntype"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client_conntype", server) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("languages")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("languages"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "languages", server) ) ]) ], {stop, Res}; -web_page_main(_, #request{path=["statsdx", "top", Topic, Topnumber], q = _Q, lang = Lang} = _Request) -> - Res = [?XC("h1", ?T("Statistics")++" Dx"), +web_page_main(_, #request{path=[<<"statsdx">>, <<"top">>, Topic, Topnumber], q = _Q, lang = Lang} = _Request) -> + Res = [?XC(<<"h1">>, <<(?T(<<"Statistics">>))/binary, " Dx">>), case Topic of - "offlinemsg" -> ?XCT("h2", "Top offline message queues"); - "vcard" -> ?XCT("h2", "Top vCard sizes"); - "roster" -> ?XCT("h2", "Top rosters") + <<"offlinemsg">> -> ?XCT(<<"h2">>, <<"Top offline message queues">>); + <<"vcard">> -> ?XCT(<<"h2">>, <<"Top vCard sizes">>); + <<"roster">> -> ?XCT(<<"h2">>, <<"Top rosters">>) end, - ?XE("table", - [?XE("thead", [?XE("tr", - [?XE("td", [?CT("#")]), - ?XE("td", [?CT("Jabber ID")]), - ?XE("td", [?CT("Value")])] + ?XE(<<"table">>, + [?XE(<<"thead">>, [?XE(<<"tr">>, + [?XE(<<"td">>, [?CT(<<"#">>)]), + ?XE(<<"td">>, [?CT(<<"Jabber ID">>)]), + ?XE(<<"td">>, [?CT(<<"Value">>)])] )]), - ?XE("tbody", do_top_table(global, Lang, Topic, Topnumber, server)) + ?XE(<<"tbody">>, do_top_table(global, Lang, Topic, Topnumber, server)) ]) ], {stop, Res}; -web_page_main(_, #request{path=["statsdx" | FilterURL], q = Q, lang = Lang} = _Request) -> +web_page_main(_, #request{path=[<<"statsdx">> | FilterURL], q = Q, lang = Lang} = _Request) -> Filter = parse_url_filter(FilterURL), Sort_query = get_sort_query(Q), - Res = [?XC("h1", ?T("Statistics")++" Dx"), - ?XC("h2", "Sessions with: "++ io_lib:format("~p", [Filter])), - ?XE("table", + FilterS = io_lib:format("~p", [Filter]), + Res = [?XC(<<"h1">>, list_to_binary(?T("Statistics") ++ " Dx222")), + ?XC(<<"h2">>, list_to_binary("Sessions with: " ++ FilterS)), + ?XE(<<"table">>, [ - ?XE("thead", [?XE("tr", make_sessions_table_tr(Lang) )]), - ?XE("tbody", do_sessions_table(global, Lang, Filter, Sort_query, server)) + ?XE(<<"thead">>, [?XE(<<"tr">>, make_sessions_table_tr(Lang) )]), + ?XE(<<"tbody">>, do_sessions_table(global, Lang, Filter, Sort_query, server)) ]) ], {stop, Res}; web_page_main(Acc, _) -> Acc. -do_top_table(_Node, Lang, Topic, TopnumberString, Host) -> - List = get_top_users(Host, list_to_integer(TopnumberString), Topic), +do_top_table(_Node, Lang, Topic, TopnumberBin, Host) -> + List = get_top_users(Host, list_to_integer(binary_to_list(TopnumberBin)), Topic), %% get_top_users(Topnumber, "roster") {List2, _} = lists:mapfoldl( - fun({Value, User, Server}, Counter) -> + fun({Value, UserB, ServerB}, Counter) -> + User = binary_to_list(UserB), + Server = binary_to_list(ServerB), UserJID = User++"@"++Server, UserJIDUrl = "/admin/server/" ++ Server ++ "/user/" ++ User ++ "/", ValueString = integer_to_list(Value), ValueEl = case Topic of - "offlinemsg" -> {url, UserJIDUrl++"queue/", ValueString}; - "vcard" -> {url, UserJIDUrl++"vcard/", ValueString}; - "roster" -> {url, UserJIDUrl++"roster/", ValueString}; + <<"offlinemsg">> -> {url, UserJIDUrl++"queue/", ValueString}; + <<"vcard">> -> {url, UserJIDUrl++"vcard/", ValueString}; + <<"roster">> -> {url, UserJIDUrl++"roster/", ValueString}; _ -> ValueString end, {do_table_element(Counter, Lang, UserJID, {fixed_url, UserJIDUrl}, ValueEl), @@ -1145,23 +1153,23 @@ get_sort_query2(Q) -> false -> {ok, {reverse, abs(Integer)}} end. make_sessions_table_tr(Lang) -> - Titles = ["Jabber ID", - "Client ID", - "OS ID", - "Lang", - "Connection", - "Client", - "Version", - "OS"], + Titles = [<<"Jabber ID">>, + <<"Client ID">>, + <<"OS ID">>, + <<"Lang">>, + <<"Connection">>, + <<"Client">>, + <<"Version">>, + <<"OS">>], {Titles_TR, _} = lists:mapfoldl( fun(Title, Num_column) -> - NCS = integer_to_list(Num_column), - TD = ?XE("td", [?CT(Title), + NCS = list_to_binary(integer_to_list(Num_column)), + TD = ?XE(<<"td">>, [?CT(Title), ?BR, - ?ACT("?sort="++NCS, "<"), - ?C(" "), - ?ACT("?sort=-"++NCS, ">")]), + ?ACT(<<"?sort=", NCS/binary>>, <<"<">>), + ?C(<<" ">>), + ?ACT(<<"?sort=-", NCS/binary>>, <<">">>)]), {TD, Num_column+1} end, 1, @@ -1178,7 +1186,7 @@ parse_url_filter(_, Res) -> Res. -web_page_node(_, Node, ["statsdx"], _Query, Lang) -> +web_page_node(_, Node, [<<"statsdx">>], _Query, Lang) -> TransactionsCommited = rpc:call(Node, mnesia, system_info, [transaction_commits]), TransactionsAborted = @@ -1189,10 +1197,10 @@ web_page_node(_, Node, ["statsdx"], _Query, Lang) -> rpc:call(Node, mnesia, system_info, [transaction_log_writes]), Res = - [?XC("h1", io_lib:format(?T("~p statistics"), [Node])), - ?XC("h3", "Connections"), - ?XAE("table", [], - [?XE("tbody", [ + [?XC(<<"h1">>, list_to_binary(io_lib:format(?T("~p statistics"), [Node]))), + ?XC(<<"h3">>, <<"Connections">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "onlineusers"), do_stat(Node, Lang, "httppollusers"), do_stat(Node, Lang, "httpbindusers"), @@ -1200,15 +1208,15 @@ web_page_node(_, Node, ["statsdx"], _Query, Lang) -> do_stat(Node, Lang, "s2sservers") ]) ]), - ?XC("h3", "ejabberd"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"ejabberd">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(Node, Lang, "ejabberdversion") ]) ]), - ?XC("h3", "Erlang"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Erlang">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(Node, Lang, "operatingsystem"), do_stat(Node, Lang, "erlangmachine"), do_stat(Node, Lang, "erlangmachinetarget"), @@ -1217,18 +1225,18 @@ web_page_node(_, Node, ["statsdx"], _Query, Lang) -> do_stat(Node, Lang, "totalerlproc") ]) ]), - ?XC("h3", "Times"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Times">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(Node, Lang, "uptime"), do_stat(Node, Lang, "uptimehuman"), do_stat(Node, Lang, "lastrestart"), do_stat(Node, Lang, "cputime") ]) ]), - ?XC("h3", "CPU"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"CPU">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(Node, Lang, "cpu_avg1"), do_stat(Node, Lang, "cpu_avg5"), do_stat(Node, Lang, "cpu_avg15"), @@ -1248,9 +1256,9 @@ web_page_node(_, Node, ["statsdx"], _Query, Lang) -> %% do_stat(Node, Lang, "reductions") %%]) %%]), - ?XC("h3", "Memory (bytes)"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Memory (bytes)">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(Node, Lang, "memory_total"), do_stat(Node, Lang, "memory_processes"), do_stat(Node, Lang, "memory_processes_used"), @@ -1262,84 +1270,84 @@ web_page_node(_, Node, ["statsdx"], _Query, Lang) -> do_stat(Node, Lang, "memory_ets") ]) ]), - ?XC("h3", "Database"), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Database">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ - ?XE("tr", [?XCT("td", "Transactions commited"), - ?XAC("td", [{"class", "alignright"}], - integer_to_list(TransactionsCommited))]), - ?XE("tr", [?XCT("td", "Transactions aborted"), - ?XAC("td", [{"class", "alignright"}], - integer_to_list(TransactionsAborted))]), - ?XE("tr", [?XCT("td", "Transactions restarted"), - ?XAC("td", [{"class", "alignright"}], - integer_to_list(TransactionsRestarted))]), - ?XE("tr", [?XCT("td", "Transactions logged"), - ?XAC("td", [{"class", "alignright"}], - integer_to_list(TransactionsLogged))]) + ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions commited">>), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], + list_to_binary(integer_to_list(TransactionsCommited)))]), + ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions aborted">>), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], + list_to_binary(integer_to_list(TransactionsAborted)))]), + ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions restarted">>), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], + list_to_binary(integer_to_list(TransactionsRestarted)))]), + ?XE(<<"tr">>, [?XCT(<<"td">>, <<"Transactions logged">>), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], + list_to_binary(integer_to_list(TransactionsLogged)))]) ]) ])], {stop, Res}; web_page_node(Acc, _, _, _, _) -> Acc. web_page_host(_, Host, - #request{path = ["statsdx"], + #request{path = [<<"statsdx">>], lang = Lang} = _Request) -> - Res = [?XC("h1", ?T("Statistics")++" Dx"), - ?XC("h2", Host), - ?XC("h3", "Accounts"), - ?XAE("table", [], - [?XE("tbody", [ + Res = [?XC(<<"h1">>, <<(?T(<<"Statistics">>))/binary, " Dx">>), + ?XC(<<"h2">>, Host), + ?XC(<<"h3">>, <<"Accounts">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "registeredusers", Host) ]) ]), - ?XC("h3", "Roster"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Roster">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "totalrosteritems", Host), %%get_meanitemsinroster2(TotalRosterItems, RegisteredUsers) - ?XE("tr", - [?XE("td", [?C("Top rosters") ]), - ?XE("td", [ - ?ACT("top/roster/30", "30"), ?C(", "), - ?ACT("top/roster/100", "100"), ?C(", "), - ?ACT("top/roster/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?C(<<"Top rosters">>) ]), + ?XE(<<"td">>, [ + ?ACT(<<"top/roster/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/roster/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/roster/500">>, <<"500">>) ])] ) ]) ]), - ?XC("h3", "Users"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Users">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "onlineusers", Host), %%do_stat(global, Lang, "offlinemsg", Host), %% This make take a lot of time %%do_stat(global, Lang, "vcards", Host) %% This make take a lot of time - ?XE("tr", - [?XE("td", [?C("Top offline message queues")]), - ?XE("td", [ - ?ACT("top/offlinemsg/30", "30"), ?C(", "), - ?ACT("top/offlinemsg/100", "100"), ?C(", "), - ?ACT("top/offlinemsg/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?C(<<"Top offline message queues">>)]), + ?XE(<<"td">>, [ + ?ACT(<<"top/offlinemsg/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/offlinemsg/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/offlinemsg/500">>, <<"500">>) ])] ), - ?XE("tr", - [?XE("td", [?C("Top vCard sizes") ]), - ?XE("td", [ - ?ACT("top/vcard/5", "5"), ?C(", "), - ?ACT("top/vcard/30", "30"), ?C(", "), - ?ACT("top/vcard/100", "100"), ?C(", "), - ?ACT("top/vcard/500", "500") ])] + ?XE(<<"tr">>, + [?XE(<<"td">>, [?C(<<"Top vCard sizes">>) ]), + ?XE(<<"td">>, [ + ?ACT(<<"top/vcard/5">>, <<"5">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/30">>, <<"30">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/100">>, <<"100">>), ?C(<<", ">>), + ?ACT(<<"top/vcard/500">>, <<"500">>) ])] ) ]) ]), - ?XC("h3", "Connections"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Connections">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "s2sconnections", Host) ]) ]), - ?XC("h3", "MUC"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"MUC">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "totalmucrooms", Host), do_stat(global, Lang, "permmucrooms", Host), do_stat(global, Lang, "regmucrooms", Host) @@ -1357,45 +1365,45 @@ web_page_host(_, Host, %% do_stat(global, Lang, "regpubsubnodes", Host) %% ]) %%]), - ?XC("h3", "Sessions: " ++ get_stat_n("client")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client", Host) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("os")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("os"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "os", Host) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("os")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary, "/", (get_stat_n("os"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client_os", Host) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("conntype")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("conntype"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "conntype", Host) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("client") ++ "/" ++ get_stat_n("conntype")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("client"))/binary, "/", (get_stat_n("conntype"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "client_conntype", Host) ) ]), - ?XC("h3", "Sessions: " ++ get_stat_n("languages")), - ?XAE("table", [], - [?XE("tbody", + ?XC(<<"h3">>, <<"Sessions: ", (get_stat_n("languages"))/binary>>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, do_stat_table(global, Lang, "languages", Host) ) ]), - ?XC("h3", "Ratios"), - ?XAE("table", [], - [?XE("tbody", [ + ?XC(<<"h3">>, <<"Ratios">>), + ?XAE(<<"table">>, [], + [?XE(<<"tbody">>, [ do_stat(global, Lang, "user_login", Host), do_stat(global, Lang, "user_logout", Host), do_stat(global, Lang, "register_user", Host), @@ -1416,24 +1424,24 @@ web_page_host(_, Host, ]) ], {stop, Res}; -web_page_host(_, Host, #request{path=["statsdx", "top", Topic, Topnumber], q = _Q, lang = Lang} = _Request) -> +web_page_host(_, Host, #request{path=[<<"statsdx">>, <<"top">>, Topic, Topnumber], q = _Q, lang = Lang} = _Request) -> Res = [?XC("h1", ?T("Statistics")++" Dx"), case Topic of - "offlinemsg" -> ?XCT("h2", "Top offline message queues"); - "vcard" -> ?XCT("h2", "Top vCard sizes"); - "roster" -> ?XCT("h2", "Top rosters") + <<"offlinemsg">> -> ?XCT(<<"h2">>, <<"Top offline message queues">>); + <<"vcard">> -> ?XCT(<<"h2">>, <<"Top vCard sizes">>); + <<"roster">> -> ?XCT(<<"h2">>, <<"Top rosters">>) end, - ?XE("table", - [?XE("thead", [?XE("tr", - [?XE("td", [?CT("#")]), - ?XE("td", [?CT("Jabber ID")]), - ?XE("td", [?CT("Value")])] + ?XE(<<"table">>, + [?XE(<<"thead">>, [?XE(<<"tr">>, + [?XE(<<"td">>, [?CT(<<"#">>)]), + ?XE(<<"td">>, [?CT(<<"Jabber ID">>)]), + ?XE(<<"td">>, [?CT(<<"Value">>)])] )]), - ?XE("tbody", do_top_table(global, Lang, Topic, Topnumber, Host)) + ?XE(<<"tbody">>, do_top_table(global, Lang, Topic, Topnumber, Host)) ]) ], {stop, Res}; -web_page_host(_, Host, #request{path=["statsdx" | FilterURL], q = Q, +web_page_host(_, Host, #request{path=[<<"statsdx">> | FilterURL], q = Q, lang = Lang} = _Request) -> Filter = parse_url_filter(FilterURL), Sort_query = get_sort_query(Q), @@ -1455,19 +1463,20 @@ web_page_host(Acc, _, _) -> Acc. do_table_element(Lang, L, StatLink, N) -> do_table_element(no_counter, Lang, L, StatLink, N). do_table_element(Counter, Lang, L, StatLink, N) -> - ?XE("tr", [ + ?XE(<<"tr">>, [ case Counter of - no_counter -> ?C(""); - _ -> ?XE("td", [?C(integer_to_list(Counter))]) + no_counter -> ?C(<<"">>); + _ -> ?XE(<<"td">>, [?C(integer_to_list(Counter))]) end, case StatLink of - no_link -> ?XCT("td", L); - {fixed_url, Fixedurl} -> ?XE("td", [?AC(Fixedurl, L)]); - _ -> ?XE("td", [?AC(make_url(StatLink, L), L)]) + no_link -> ?XCT(<<"td">>, L); + {fixed_url, Fixedurl} -> ?XE(<<"td">>, [?AC(Fixedurl, L)]); + _ -> ?XE(<<"td">>, [?AC(list_to_binary(make_url(StatLink, L)), list_to_binary(L))]) end, case N of - {url, NUrl, NName} -> ?XAE("td", [{"class", "alignright"}], [?AC(NUrl, NName)]); - _ -> ?XAC("td", [{"class", "alignright"}], N) + {url, NUrl, NName} -> ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], [?AC(NUrl, NName)]); + N when is_list(N) -> ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], list_to_binary(N)); + _ -> ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], N) end ]). @@ -1499,14 +1508,14 @@ do_sessions_table(_Node, _Lang, Filter, {Sort_direction, Sort_column}, Host) -> Server = JID#jid.lserver, UserURL = "/admin/server/" ++ Server ++ "/user/" ++ User ++ "/", ?XE("tr", [ - ?XE("td", [?AC(UserURL, jlib:jid_to_string(JID))]), - ?XCT("td", atom_to_list(Client_id)), - ?XCT("td", atom_to_list(OS_id)), - ?XCT("td", Lang), - ?XCT("td", atom_to_list(ConnType)), - ?XCT("td", Client), - ?XCT("td", Version), - ?XCT("td", OS) + ?XE(<<"td">>, [?AC(UserURL, jlib:jid_to_string(JID))]), + ?XCTB("td", atom_to_list(Client_id)), + ?XCTB("td", atom_to_list(OS_id)), + ?XCTB("td", Lang), + ?XCTB("td", atom_to_list(ConnType)), + ?XCTB("td", Client), + ?XCTB("td", Version), + ?XCTB("td", OS) ]) end, SessionsSorted @@ -1544,9 +1553,9 @@ get_sessions_filtered(Filter, Host) -> ets:match_object(table_name(Host), Match). do_stat(Node, Lang, Stat) -> - ?XE("tr", [ - ?XCT("td", get_stat_n(Stat)), - ?XAC("td", [{"class", "alignright"}], + ?XE(<<"tr">>, [ + ?XCT(<<"td">>, get_stat_n(Stat)), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], get_stat_v(Node, [Stat]))]). do_stat(Node, Lang, Stat, Host) -> @@ -1556,9 +1565,9 @@ do_stat(Node, Lang, Stat, Host) -> %% Get a stat name get_stat_n(Stat) -> - mod_statsdx:get_statistic(foo, [Stat, title]). + list_to_binary(mod_statsdx:get_statistic(foo, [Stat, title])). %% Get a stat value -get_stat_v(Node, Stat) -> get_stat_v2(mod_statsdx:get_statistic(Node, Stat)). +get_stat_v(Node, Stat) -> list_to_binary(get_stat_v2(mod_statsdx:get_statistic(Node, Stat))). get_stat_v2(Value) when is_list(Value) -> Value; get_stat_v2(Value) when is_float(Value) -> io_lib:format("~.4f", [Value]); get_stat_v2(Value) when is_integer(Value) -> @@ -1616,11 +1625,11 @@ get_top_users(Number, Topic) -> get_top_users(server, Number, Topic). %% Returns: [{Integer, User, Server}] -get_top_users(Host, Number, "vcard") -> +get_top_users(Host, Number, <<"vcard">>) -> get_top_users_vcard(Host, Number); -get_top_users(Host, Number, "offlinemsg") -> +get_top_users(Host, Number, <<"offlinemsg">>) -> get_top_users(Host, Number, offline_msg, #offline_msg.us); -get_top_users(Host, Number, "roster") -> +get_top_users(Host, Number, <<"roster">>) -> get_top_users(Host, Number, roster, #roster.us). diff --git a/mod_webpresence/README.txt b/mod_webpresence/README.txt index 310debf..47cc1e3 100644 --- a/mod_webpresence/README.txt +++ b/mod_webpresence/README.txt @@ -1,8 +1,6 @@ - mod_webpresence - Presence on the Web - Authors: Igor Goryachev, Badlop - Requires: ejabberd SVN (not possible with 1.1.x) + Authors: Igor Goryachev, Badlop, runcom http://www.ejabberd.im/mod_webpresence @@ -35,23 +33,19 @@ No web server, database, additional libraries or programs are required. 3. Copy the directory data/pixmaps to a directory you prefer. 4. Edit ejabberd.cfg and add the HTTP and module definitions: -{listen, [ - ... - {5280, ejabberd_http, [ - ... - {request_handlers, [ - ... - {["presence"], mod_webpresence} - ]} - ]} -]}. -{modules, [ - ... - {mod_webpresence, [ - {pixmaps_path, "/path/to/pixmaps"} - ]} -]}. +listen: + - + port: 5280 + module: ejabberd_http + [...] + request_handlers: + "presence": mod_webpresence + +modules: + [...] + mod_webpresence: + pixmaps_path: "/path/to/pixmaps" 5. Restart ejabberd. If problems appear, remember to always look first the ejabberd log files @@ -102,10 +96,14 @@ In that case, all the output methods are enabled, the icon theme is 'jsf-jabber-text' and RandomID is disabled. The default behaviour is to not have automatic webpresence: - {access, webpresence_auto, [{deny, all}]}. + access: + webpresence_auto: + all: deny For example, if you want all the local users to be automatically enabled in the service: - {access, webpresence_auto, [{allow, local}]}. + access: + webpresence_auto: + local: allow Note that this ACCESS rule is only checked if the user is not registered. So, if the user registers and disables all output methods, @@ -119,50 +117,40 @@ register and disable output methods, you can use the Access configurable paramet Example 1 --------- -{listen, [ - ... - {5280, ejabberd_http, [ - ... - {request_handlers, [ - ... - {["presence"], mod_webpresence} - ]} - ]} -]}. +listen: + - + port: 5280 + module: ejabberd_http + [...] + request_handlers: + "presence": mod_webpresence -{modules, [ - ... - {mod_webpresence, [ - {pixmaps_path, "/path/to/pixmaps"} - ]} -]}. +modules: + [...] + mod_webpresence: + pixmaps_path: "/path/to/pixmaps" Example 2 --------- -{listen, [ - ... - {80, ejabberd_http, [ - ... - {request_handlers, [ - ... - {["status"], mod_webpresence} - ]} - ]} -]}. +listen: + - + port: 80 + module: ejabberd_http + [...] + request_handlers: + "status": mod_webpresence -{modules, [ - ... - {mod_webpresence, [ - {host, "webstatus.@HOST@"}, - {access, local}, - {pixmaps_path, "/path/to/pixmaps"}, - {port, 80}, - {path, "status"}, - {baseurl, "http://www.example.org/status/"} - ]} -]}. +modules: + [...] + mod_webpresence: + host: "webstatus.@HOST@" + access: local + pixmaps_path: "/path/to/pixmaps" + port: 80 + path: "status" + baseurl: "http://www.example.org/status/" USAGE diff --git a/mod_webpresence/build.bat b/mod_webpresence/build.bat index fff9b3b..8ce7043 100644 --- a/mod_webpresence/build.bat +++ b/mod_webpresence/build.bat @@ -1 +1 @@ -erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make +erl -pa ../../ejabberd-dev/ebin -pa ebin -make diff --git a/mod_webpresence/data/pixmaps/simple/available.png b/mod_webpresence/data/pixmaps/simple/available.png new file mode 100644 index 0000000..ab57011 Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/available.png differ diff --git a/mod_webpresence/data/pixmaps/simple/away.png b/mod_webpresence/data/pixmaps/simple/away.png new file mode 100644 index 0000000..0d148f3 Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/away.png differ diff --git a/mod_webpresence/data/pixmaps/simple/chat.png b/mod_webpresence/data/pixmaps/simple/chat.png new file mode 100644 index 0000000..d6fb64c Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/chat.png differ diff --git a/mod_webpresence/data/pixmaps/simple/dnd.png b/mod_webpresence/data/pixmaps/simple/dnd.png new file mode 100644 index 0000000..d2bca6c Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/dnd.png differ diff --git a/mod_webpresence/data/pixmaps/simple/unavailable.png b/mod_webpresence/data/pixmaps/simple/unavailable.png new file mode 100644 index 0000000..e9186fa Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/unavailable.png differ diff --git a/mod_webpresence/data/pixmaps/simple/xa.png b/mod_webpresence/data/pixmaps/simple/xa.png new file mode 100644 index 0000000..3b65868 Binary files /dev/null and b/mod_webpresence/data/pixmaps/simple/xa.png differ diff --git a/mod_webpresence/src/mod_webpresence.erl b/mod_webpresence/src/mod_webpresence.erl index 53d3454..238d2c9 100644 --- a/mod_webpresence/src/mod_webpresence.erl +++ b/mod_webpresence/src/mod_webpresence.erl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% File : mod_webpresence.erl -%%% Author : Igor Goryachev , Badlop +%%% Author : Igor Goryachev , Badlop, runcom %%% Purpose : Allow user to show presence in the web %%% Created : 30 Apr 2006 by Igor Goryachev %%% Id : $Id: mod_webpresence.erl 1083 2010-06-01 18:32:55Z badlop $ @@ -17,7 +17,8 @@ stop/1, remove_user/2, web_menu_host/3, web_page_host/3, - process/2]). + process/2 + ]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -25,8 +26,9 @@ -include("ejabberd.hrl"). -include("jlib.hrl"). --include("web/ejabberd_web_admin.hrl"). --include("web/ejabberd_http.hrl"). +-include("logger.hrl"). +-include("ejabberd_web_admin.hrl"). +-include("ejabberd_http.hrl"). -record(webpresence, {us, ridurl = false, jidurl = false, xml = false, avatar = false, js = false, text = false, icon = "---"}). -record(state, {host, server_host, base_url, access}). @@ -36,7 +38,7 @@ -record(session, {sid, usr, us, priority, info}). -define(PROCNAME, ejabberd_mod_webpresence). --define(PIXMAPS_DIR, "pixmaps"). +-define(PIXMAPS_DIR, <<"pixmaps">>). -define(AUTO_ACL, webpresence_auto). @@ -53,18 +55,13 @@ start_link(Host, Opts) -> start(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = - {Proc, - {?MODULE, start_link, [Host, Opts]}, - temporary, - 1000, - worker, - [?MODULE]}, + ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, + temporary, 1000, worker, [?MODULE]}, Default_dir = case code:priv_dir(ejabberd) of {error, _} -> ?PIXMAPS_DIR; Path -> filename:join([Path, ?PIXMAPS_DIR]) end, - Dir = gen_mod:get_opt(pixmaps_path, Opts, Default_dir), + Dir = gen_mod:get_opt(pixmaps_path, Opts, fun(D) -> D end, Default_dir), catch ets:new(pixmaps_dirs, [named_table, public]), ets:insert(pixmaps_dirs, {directory, Dir}), supervisor:start_child(ejabberd_sup, ChildSpec). @@ -92,11 +89,12 @@ init([Host, Opts]) -> {attributes, record_info(fields, webpresence)}]), mnesia:add_table_index(webpresence, ridurl), update_table(), - MyHost = gen_mod:get_opt_host(Host, Opts, "webpresence.@HOST@"), - Access = gen_mod:get_opt(access, Opts, local), - Port = gen_mod:get_opt(port, Opts, 5280), - Path = gen_mod:get_opt(path, Opts, "presence"), - BaseURL = gen_mod:get_opt(baseurl, Opts, io_lib:format("http://~s:~p/~s/",[Host, Port, Path])), + MyHost = gen_mod:get_opt_host(Host, Opts, <<"webpresence.@HOST@">>), + Access = gen_mod:get_opt(access, Opts, fun(O) -> O end, local), + Port = gen_mod:get_opt(port, Opts, fun(O) -> O end, 5280), + Path = gen_mod:get_opt(path, Opts, fun(O) -> O end, <<"presence">>), + BaseURL = gen_mod:get_opt(baseurl, Opts, fun(O) -> O end, + iolist_to_binary(io_lib:format(<<"http://~s:~p/~s/">>, [Host, Port, Path]))), ejabberd_router:register_route(MyHost), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), @@ -179,26 +177,25 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - do_route(Host, ServerHost, Access, From, To, Packet, BaseURL) -> case acl:match_rule(ServerHost, Access, From) of allow -> do_route1(Host, From, To, Packet, BaseURL); _ -> - {xmlelement, _Name, Attrs, _Els} = Packet, - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Access denied by service policy", + #xmlel{attrs = Attrs} = Packet, + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + ErrText = <<"Access denied by service policy">>, Err = jlib:make_error_reply(Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), ejabberd_router:route(To, From, Err) end. do_route1(Host, From, To, Packet, BaseURL) -> - {xmlelement, Name, Attrs, _Els} = Packet, + #xmlel{name = Name, attrs = Attrs} = Packet, case Name of - "iq" -> do_route1_iq(Host, From, To, Packet, BaseURL, jlib:iq_query_info(Packet)); - _ -> case xml:get_attr_s("type", Attrs) of - "error" -> ok; - "result" -> ok; + <<"iq">> -> do_route1_iq(Host, From, To, Packet, BaseURL, jlib:iq_query_info(Packet)); + _ -> case xml:get_attr_s(<<"type">>, Attrs) of + <<"error">> -> ok; + <<"result">> -> ok; _ -> Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND), ejabberd_router:route(To, From, Err) end @@ -206,8 +203,12 @@ do_route1(Host, From, To, Packet, BaseURL) -> do_route1_iq(_, From, To, _, _, #iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = IQ) -> - SubEl2 = {xmlelement, "query", [{"xmlns", ?NS_DISCO_INFO}], iq_disco_info(Lang)}, - Res = IQ#iq{type = result, sub_el = [SubEl2]}, + SubEl = #xmlel{ + name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}], + children = iq_disco_info(Lang) + }, + Res = IQ#iq{type = result, sub_el = [SubEl]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); do_route1_iq(_, _, _, _, _, @@ -216,15 +217,23 @@ do_route1_iq(_, _, _, _, _, do_route1_iq(Host, From, To, _, _, #iq{type = get, xmlns = ?NS_REGISTER, lang = Lang} = IQ) -> - SubEl2 = {xmlelement, "query", [{"xmlns", ?NS_REGISTER}], iq_get_register_info(Host, From, Lang)}, - Res = IQ#iq{type = result, sub_el = [SubEl2]}, + SubEl = #xmlel{ + name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}], + children = iq_get_register_info(Host, From, Lang) + }, + Res = IQ#iq{type = result, sub_el = [SubEl]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); do_route1_iq(Host, From, To, Packet, BaseURL, #iq{type = set, xmlns = ?NS_REGISTER, lang = Lang, sub_el = SubEl} = IQ) -> case process_iq_register_set(From, SubEl, Host, BaseURL, Lang) of {result, IQRes} -> - SubEl2 = {xmlelement, "query", [{"xmlns", ?NS_REGISTER}], IQRes}, + SubEl2 = #xmlel{ + name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_REGISTER}], + children = IQRes + }, Res = IQ#iq{type = result, sub_el = [SubEl2]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); {error, Error} -> @@ -234,58 +243,92 @@ do_route1_iq(Host, From, To, Packet, BaseURL, do_route1_iq(_Host, From, To, _, _, #iq{type = get, xmlns = ?NS_VCARD = XMLNS} = IQ) -> - SubEl2 = {xmlelement, "vCard", [{"xmlns", XMLNS}], iq_get_vcard()}, - Res = IQ#iq{type = result, sub_el = [SubEl2]}, + SubEl = #xmlel{ + name = <<"vCard">>, + attrs = [{<<"xmlns">>, XMLNS}], + children = iq_get_vcard() + }, + Res = IQ#iq{type = result, sub_el = [SubEl]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); do_route1_iq(_Host, From, To, Packet, _, #iq{}) -> - Err = jlib:make_error_reply( Packet, ?ERR_FEATURE_NOT_IMPLEMENTED), + Err = jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED), ejabberd_router:route(To, From, Err); do_route1_iq(_, _, _, _, _, _) -> ok. iq_disco_info(Lang) -> - [{xmlelement, "identity", - [{"category", "component"}, - {"type", "presence"}, - {"name", ?T("Web Presence")}], []}, - {xmlelement, "feature", [{"var", ?NS_REGISTER}], []}, - {xmlelement, "feature", [{"var", ?NS_VCARD}], []}]. + [#xmlel{ + name = <<"identity">>, + attrs = [{<<"category">>, <<"component">>}, + {<<"type">>, <<"presence">>}, + {<<"name">>, ?T(<<"Web Presence">>)}], + children = [] + }, + #xmlel{ + name = <<"feature">>, + attrs = [{<<"var">>, ?NS_REGISTER}], + children = [] + }, + #xmlel{ + name = <<"feature">>, + attrs = [{<<"var">>, ?NS_VCARD}], + children = [] + }]. -define(XFIELDS(Type, Label, Var, Vals), - {xmlelement, "field", [{"type", Type}, - {"label", ?T(Label)}, - {"var", Var}], - Vals}). + #xmlel{ + name = <<"field">>, + attrs = [ + {<<"type">>, Type}, + {<<"label">>, ?T(Label)}, + {<<"var">>, Var} + ], + children = Vals + }). -define(XFIELD(Type, Label, Var, Val), - ?XFIELDS(Type, Label, Var, - [{xmlelement, "value", [], [{xmlcdata, Val}]}]) + ?XFIELDS(Type, Label, Var, + [ + #xmlel{ + name = <<"value">>, + attrs = [], + children = [{xmlcdata, Val}] + }]) ). -define(XFIELDFIXED(Val), - {xmlelement, "field", [{"type", "fixed"}], - [{xmlelement, "value", [], [{xmlcdata, Val}]}]} + #xmlel{ + name = <<"field">>, + attrs = [{<<"type">>, <<"fixed">>}], + children = [ + #xmlel{ + name = <<"value">>, + attrs = [], + children = [{xmlcdata, Val}] + } + ] + } ). -%% @spec ridurl_out(ridurl()) -> boolean_string() -%% @type ridurl() = string() | false -%% @type boolean_string() = "true" | "false" -ridurl_out(false) -> "false"; -ridurl_out(Id) when is_list(Id) -> "true". +-define(ATOM2BINARY(Val), iolist_to_binary(atom_to_list(Val))). +-define(BC(L), iolist_to_binary(L)). -to_bool("false") -> false; -to_bool("true") -> true; -to_bool("0") -> false; -to_bool("1") -> true. +ridurl_out(false) -> <<"false">>; +ridurl_out(Id) when is_binary(Id) -> <<"true">>. + +to_bool(<<"false">>) -> false; +to_bool(<<"true">>) -> true; +to_bool(<<"0">>) -> false; +to_bool(<<"1">>) -> true. get_pr(LUS) -> case catch mnesia:dirty_read(webpresence, LUS) of [#webpresence{jidurl = J, ridurl = H, xml = X, avatar = A, js = S, text = T, icon = I}] -> {J, H, X, A, S, T, I, true}; _ -> - {true, false, false, false, false, false, "---", false} + {true, false, false, false, false, false, <<"---">>, false} end. get_pr_rid(LUS) -> @@ -296,40 +339,69 @@ iq_get_register_info(_Host, From, Lang) -> {LUser, LServer, _} = jlib:jid_tolower(From), LUS = {LUser, LServer}, {JidUrl, RidUrl, XML, Avatar, JS, Text, Icon, Registered} = get_pr(LUS), - RegisteredXML = case Registered of - true -> [{xmlelement, "registered", [], []}]; + RegisteredXML = case Registered of + true -> [#xmlel{name = <<"registered">>, attrs = [], children = []}]; false -> [] end, RegisteredXML ++ - [{xmlelement, "instructions", [], - [{xmlcdata, ?T("You need an x:data capable client to register presence")}]}, - {xmlelement, "x", - [{"xmlns", ?NS_XDATA}], - [{xmlelement, "title", [], - [{xmlcdata, - ?T("Web Presence")}]}, - {xmlelement, "instructions", [], [{xmlcdata, ?T("What features do you want to enable?")}]}, - ?XFIELDFIXED(?T("URL Type")++". "++?T("Select one at least")), - ?XFIELD("boolean", "Jabber ID", "jidurl", atom_to_list(JidUrl)), - ?XFIELD("boolean", "Random ID", "ridurl", ridurl_out(RidUrl)), - ?XFIELDFIXED(?T("Output Type")++". "++?T("Select one at least")), - ?XFIELDS("list-single", ?T("Icon Theme"), "icon", - [{xmlelement, "value", [], [{xmlcdata, Icon}]}, - {xmlelement, "option", [{"label", "---"}], - [{xmlelement, "value", [], [{xmlcdata, "---"}]}]} - ] ++ available_themes(xdata) - ), - ?XFIELD("boolean", "XML", "xml", atom_to_list(XML)), - ?XFIELD("boolean", "JavaScript", "js", atom_to_list(JS)), - ?XFIELD("boolean", "Text", "text", atom_to_list(Text)), - ?XFIELD("boolean", "Avatar", "avatar", atom_to_list(Avatar))]}]. + [ + #xmlel{ + name = <<"instructions">>, + attrs = [], + children = [{xmlcdata, ?T(<<"You need an x:data capable client to register presence">>)}] + }, + #xmlel{ + name = <<"x">>, + attrs = [{<<"xmlns">>, ?NS_XDATA}], + children = [ + #xmlel{ + name = <<"title">>, + attrs = [], + children = [{xmlcdata, ?T(<<"Web Presence">>)}] + }, + #xmlel{ + name = <<"instructions">>, + attrs = [], + children = [{xmlcdata, ?T(<<"What features do you want to enable?">>)}] + }, + ?XFIELDFIXED(?BC([?T(<<"URL Type">>), <<". ">>, ?T(<<"Select one at least">>)])), + ?XFIELD(<<"boolean">>, <<"Jabber ID">>, <<"jidurl">>, ?ATOM2BINARY(JidUrl)), + ?XFIELD(<<"boolean">>, <<"Random ID">>, <<"ridurl">>, ridurl_out(RidUrl)), + ?XFIELDFIXED(?BC([?T(<<"Output Type">>), <<". ">>, ?T(<<"Select one at least">>)])), + ?XFIELDS(<<"list-single">>, ?T(<<"Icon Theme">>), <<"icon">>, + [ + #xmlel{ + name = <<"value">>, + attrs = [], + children = [{xmlcdata, Icon}] + }, + #xmlel{ + name = <<"option">>, + attrs = [{<<"label">>, <<"---">>}], + children = [ + #xmlel{ + name = <<"value">>, + attrs = [], + children = [{xmlcdata, <<"---">>}] + } + ] + } + ] ++ available_themes(xdata) + ), + ?XFIELD(<<"boolean">>, <<"XML">>, <<"xml">>, ?ATOM2BINARY(XML)), + ?XFIELD(<<"boolean">>, <<"JavaScript">>, <<"js">>, ?ATOM2BINARY(JS)), + ?XFIELD(<<"boolean">>, <<"Text">>, <<"text">>, ?ATOM2BINARY(Text)), + ?XFIELD(<<"boolean">>, <<"Avatar">>, <<"avatar">>, ?ATOM2BINARY(Avatar)) + ] + } + ]. -%% TODO: Check if remote users are allowed to reach here: they should not be allowed +%%%% TODO: Check if remote users are allowed to reach here: they should not be allowed iq_set_register_info(From, {Host, JidUrl, RidUrl, XML, Avatar, JS, Text, Icon, _, Lang} = Opts) -> {LUser, LServer, _} = jlib:jid_tolower(From), LUS = {LUser, LServer}, Check_URLTypes = (JidUrl == true) or (RidUrl =/= false), - Check_OutputTypes = (XML == true) or (Avatar == true) or (JS == true) or (Text == true) or (Icon =/= "---"), + Check_OutputTypes = (XML == true) or (Avatar == true) or (JS == true) or (Text == true) or (Icon =/= <<"---">>), case Check_URLTypes and Check_OutputTypes of true -> iq_set_register_info2(From, LUS, Opts); false -> unregister_webpresence(From, Host, Lang) @@ -358,93 +430,110 @@ get_rid_final_value(false, _) -> false; get_rid_final_value(true, {U, S} = LUS) -> case get_pr_rid(LUS) of false -> - integer_to_list(erlang:phash2(U) * erlang:phash2(S) + iolist_to_binary(integer_to_list(erlang:phash2(U) * erlang:phash2(S) * calendar:datetime_to_gregorian_seconds( - calendar:local_time())) - ++ randoms:get_string(); - H when is_list(H) -> + calendar:local_time())) + ++ randoms:get_string()); + H when is_binary(H) -> H end. send_message_registered(WP, To, Host, BaseURL, Lang) -> {User, Server} = WP#webpresence.us, - JID = jlib:make_jid(User, Server, ""), + JID = jlib:make_jid(User, Server, <<"">>), JIDS = jlib:jid_to_string(JID), Oavatar = case WP#webpresence.avatar of - false -> ""; - true -> " avatar\n" - " avatar/my.png\n" + false -> <<"">>; + true -> <<" avatar\n" + " avatar/my.png\n">> end, Ojs = case WP#webpresence.js of - false -> ""; - true -> " js\n" + false -> <<"">>; + true -> <<" js\n">> end, Otext = case WP#webpresence.text of - false -> ""; - true -> " text\n" - " text/res/<"++?T("Resource")++">\n" + false -> <<"">>; + true -> ?BC([ + <<" text\n" + " text/res/<">>, ?T(<<"Resource">>), <<">\n">> + ]) end, Oimage = case WP#webpresence.icon of - "---" -> ""; - I when is_list(I) -> - " image\n" - " image/example.php\n" - " image/mypresence.png\n" - " image/res/<"++?T("Resource")++">\n" - " image/theme/<"++?T("Icon Theme")++">\n" - " image/theme/<"++?T("Icon Theme")++">/res/<"++?T("Resource")++">\n" + <<"---">> -> ""; + I when is_binary(I) -> + ?BC([ + <<" image\n" + " image/example.php\n" + " image/mypresence.png\n" + " image/res/<">>, ?T(<<"Resource">>), <<">\n" + " image/theme/<">>, ?T(<<"Icon Theme">>), <<">\n" + " image/theme/<">>, ?T(<<"Icon Theme">>), <<">/res/<">>, ?T(<<"Resource">>), <<">\n">> + ]) end, Oxml = case WP#webpresence.xml of - false -> ""; - true -> " xml\n" + false -> <<"">>; + true -> <<" xml\n">> end, Allowed_type = case {Oimage, Oxml, Oavatar, Otext, Ojs} of - {"", "", "", "", _} -> "js"; - {"", "", "", _, _} -> "text"; - {"", "", _, _, _} -> "avatar"; - {"", _, _, _, _} -> "xml"; - {_, _, _, _, _} -> "image" + {<<"">>, <<"">>, <<"">>, <<"">>, _} -> <<"js">>; + {<<"">>, <<"">>, <<"">>, _, _} -> <<"text">>; + {<<"">>, <<"">>, _, _, _} -> <<"avatar">>; + {<<"">>, _, _, _, _} -> <<"xml">>; + {_, _, _, _, _} -> <<"image">> end, {USERID_jid, Example_jid} = case WP#webpresence.jidurl of - false -> {"", ""}; - true -> - JIDT = "jid/"++User++"/"++Server, - {" "++JIDT++"\n", - " "++BaseURL++JIDT++"/"++Allowed_type++"/\n"} + false -> {<<"">>, <<"">>}; + true -> + JIDT = ?BC([<<"jid/">>, User, <<"/">>, Server]), + {?BC([<<" ">>, JIDT, <<"\n">>]), + ?BC([<<" ">>, BaseURL, JIDT, <<"/">>, Allowed_type, <<"/\n">>])} end, {USERID_rid, Example_rid, Text_rid} = case WP#webpresence.ridurl of - false -> {"", "", ""}; - RID when is_list(RID) -> - RIDT = "rid/"++RID, - {" "++RIDT++"\n", - " "++BaseURL++RIDT++"/"++Allowed_type++"/\n", - ?T("If you forget your RandomID, register again to receive this message.")++"\n" - ++?T("To get a new RandomID, disable the option and register again.")++"\n" + false -> {<<"">>, <<"">>, <<"">>}; + RID when is_binary(RID) -> + RIDT = ?BC([<<"rid/">>, RID]), + {?BC([<<" ">>, RIDT, <<"\n">>]), + ?BC([<<" ">>, BaseURL, RIDT, <<"/">>, Allowed_type, <<"/\n">>]), + ?BC([?T(<<"If you forget your RandomID, register again to receive this message.">>), <<"\n">>, + ?T(<<"To get a new RandomID, disable the option and register again.">>), <<"\n">>]) } end, - Subject = ?T("Web Presence")++": "++?T("registered"), - Body = ?T("You have registered:")++" "++JIDS++"\n\n" - ++?T("Use URLs like:")++"\n" - " "++BaseURL++"USERID/OUTPUT/\n" - "\n" - "USERID:\n"++USERID_jid++USERID_rid++"\n" - "OUTPUT:\n"++Oimage++Oxml++Ojs++Otext++Oavatar++"\n" - ++?T("Example:")++"\n"++Example_jid++Example_rid++"\n" - ++Text_rid, + Subject = ?BC([?T(<<"Web Presence">>), <<": ">>, ?T(<<"registered">>)]), + Body = ?BC([?T(<<"You have registered:">>), <<" ">>, JIDS, <<"\n\n">>, + ?T(<<"Use URLs like:">>), <<"\n">>, + <<" ">>, BaseURL, <<"USERID/OUTPUT/\n">>, + <<"\n">>, + <<"USERID:\n">>, USERID_jid, USERID_rid, <<"\n">>, + <<"OUTPUT:\n">>, Oimage, Oxml, Ojs, Otext, Oavatar, <<"\n">>, + ?T(<<"Example:">>), <<"\n">>, Example_jid, Example_rid, <<"\n">>, + Text_rid]), send_headline(Host, To, Subject, Body). send_message_unregistered(To, Host, Lang) -> - Subject = ?T("Web Presence")++": "++?T("unregistered"), - Body = ?T("You have unregistered.")++"\n\n", + Subject = ?BC([?T(<<"Web Presence">>), <<": ">>, ?T(<<"unregistered">>)]), + Body = ?BC([?T(<<"You have unregistered.">>), <<"\n\n">>]), send_headline(Host, To, Subject, Body). send_headline(Host, To, Subject, Body) -> ejabberd_router:route( - jlib:make_jid("", Host, ""), + jlib:make_jid(<<"">>, Host, <<"">>), To, - {xmlelement, "message", [{"type", "headline"}], - [{xmlelement, "subject", [], [{xmlcdata, Subject}]}, - {xmlelement, "body", [], [{xmlcdata, Body}]}]}). + #xmlel{ + name = <<"message">>, + attrs = [{<<"type">>, <<"headline">>}], + children = [ + #xmlel{ + name = <<"subject">>, + attrs = [], + children = [{xmlcdata, Subject}] + }, + #xmlel{ + name = <<"body">>, + attrs = [], + children = [{xmlcdata, Body}] + } + ] + }). get_attr(Attr, XData, Default) -> case lists:keysearch(Attr, 1, XData) of @@ -453,8 +542,8 @@ get_attr(Attr, XData, Default) -> end. process_iq_register_set(From, SubEl, Host, BaseURL, Lang) -> - {xmlelement, _Name, _Attrs, Els} = SubEl, - case xml:get_subtag(SubEl, "remove") of + #xmlel{name = _, attrs = _, children = Els} = SubEl, + case xml:get_subtag(SubEl, <<"remove">>) of false -> case catch process_iq_register_set2(From, Els, Host, BaseURL, Lang) of {'EXIT', _} -> {error, ?ERR_BAD_REQUEST}; R -> R @@ -463,20 +552,26 @@ process_iq_register_set(From, SubEl, Host, BaseURL, Lang) -> end. process_iq_register_set2(From, Els, Host, BaseURL, Lang) -> - [{xmlelement, "x", _Attrs1, _Els1} = XEl] = xml:remove_cdata(Els), - case {xml:get_tag_attr_s("xmlns", XEl), xml:get_tag_attr_s("type", XEl)} of - {?NS_XDATA, "cancel"} -> + [ + #xmlel{ + name = <<"x">>, + attrs = _Attrs1, + children = _Els1 + } = XEl + ] = xml:remove_cdata(Els), + case {xml:get_tag_attr_s(<<"xmlns">>, XEl), xml:get_tag_attr_s(<<"type">>, XEl)} of + {?NS_XDATA, <<"cancel">>} -> {result, []}; - {?NS_XDATA, "submit"} -> + {?NS_XDATA, <<"submit">>} -> XData = jlib:parse_xdata_submit(XEl), false = (invalid == XData), - JidUrl = get_attr("jidurl", XData, "false"), - RidUrl = get_attr("ridurl", XData, "false"), - XML = get_attr("xml", XData, "false"), - Avatar = get_attr("avatar", XData, "false"), - JS = get_attr("js", XData, "false"), - Text = get_attr("text", XData, "false"), - Icon = get_attr("icon", XData, "---"), + JidUrl = get_attr(<<"jidurl">>, XData, <<"false">>), + RidUrl = get_attr(<<"ridurl">>, XData, <<"false">>), + XML = get_attr(<<"xml">>, XData, <<"false">>), + Avatar = get_attr(<<"avatar">>, XData, <<"false">>), + JS = get_attr(<<"js">>, XData, <<"false">>), + Text = get_attr(<<"text">>, XData, <<"false">>), + Icon = get_attr(<<"icon">>, XData, <<"---">>), iq_set_register_info(From, {Host, to_bool(JidUrl), to_bool(RidUrl), to_bool(XML), to_bool(Avatar), to_bool(JS), to_bool(Text), Icon, BaseURL, Lang}) end. @@ -490,52 +585,61 @@ remove_user(User, Server) -> mnesia:dirty_delete(webpresence, {User, Server}). iq_get_vcard() -> - [{xmlelement, "FN", [], - [{xmlcdata, "ejabberd/mod_webpresence"}]}, - {xmlelement, "URL", [], - [{xmlcdata, "http://www.ejabberd.im/mod_webpresence"}]}, - {xmlelement, "DESC", [], - [{xmlcdata, "ejabberd web presence module\nCopyright (c) 2006-2007 Igor Goryachev, 2007 Badlop"}]}]. + [ + #xmlel{ + name = <<"FN">>, + attrs = [], + children = [{xmlcdata, <<"ejabberd/mod_webpresence">>}] + }, + #xmlel{ + name = <<"URL">>, + attrs = [], + children = [{xmlcdata, <<"http://www.ejabberd.im/mod_webpresence">>}] + }, + #xmlel{ + name = <<"DESC">>, + attrs = [], + children = [{xmlcdata, <<"ejabberd web presence module\nCopyright (c) 2006-2007 Igor Goryachev, 2007 Badlop, 2014 runcom ">>}] + } + ]. get_wp(LUser, LServer) -> LUS = {LUser, LServer}, case catch mnesia:dirty_read(webpresence, LUS) of - {'EXIT', _Reason} -> + {'EXIT', _Reason} -> try_auto_webpresence(LUser, LServer); - [] -> + [] -> try_auto_webpresence(LUser, LServer); [WP] when is_record(WP, webpresence) -> WP end. try_auto_webpresence(LUser, LServer) -> - From = jlib:make_jid(LUser, LServer, ""), + From = jlib:make_jid(LUser, LServer, <<"">>), case acl:match_rule(LServer, ?AUTO_ACL, From) of - deny -> + deny -> #webpresence{}; - allow -> - #webpresence{us = {LUser, LServer}, - ridurl = false, - jidurl = true, - xml = true, - avatar = true, - js = true, - text = true, - icon = "jsf-jabber-text"} - end. - + allow -> + #webpresence{us = {LUser, LServer}, + ridurl = false, + jidurl = true, + xml = true, + avatar = true, + js = true, + text = true, + icon = <<"jsf-jabber-text">>} + end. get_status_weight(Show) -> case Show of - "chat" -> 0; - "available" -> 1; - "away" -> 2; - "xa" -> 3; - "dnd" -> 4; - _ -> 9 + <<"chat">> -> 0; + <<"available">> -> 1; + <<"away">> -> 2; + <<"xa">> -> 3; + <<"dnd">> -> 4; + _ -> 9 end. - session_to_presence(#session{sid = {_, Pid}, priority = Priority}) -> {_User, Resource, Show, Status} = ejabberd_c2s:get_presence(Pid), #presence{resource = Resource, @@ -546,6 +650,7 @@ session_to_presence(#session{sid = {_, Pid}, priority = Priority}) -> get_presences({bare, LUser, LServer}) -> [session_to_presence(Session) || Session <- mnesia:dirty_index_read(session, {LUser, LServer}, #session.us)]; + get_presences({sorted, LUser, LServer}) -> lists:sort( fun(A, B) -> @@ -559,112 +664,120 @@ get_presences({sorted, LUser, LServer}) -> end end, get_presences({bare, LUser, LServer})); + get_presences({xml, LUser, LServer, Show_us}) -> - {xmlelement, "presence", - case Show_us of - true -> [{"user", LUser}, {"server", LServer}]; - false -> [] - end, - lists:map( - fun(Presence) -> - {xmlelement, "resource", - [{"name", Presence#presence.resource}, - {"show", Presence#presence.show}, - {"priority", integer_to_list(Presence#presence.priority)}], - [{xmlcdata, Presence#presence.status}]} - end, - get_presences({sorted, LUser, LServer}))}; + #xmlel{ + name = <<"presence">>, + attrs = case Show_us of + true -> [{<<"user">>, LUser}, {<<"server">>, LServer}]; + false -> [] + end, + children = lists:map( + fun(Presence) -> + #xmlel{ + name = <<"resource">>, + attrs = [ + {<<"name">>, Presence#presence.resource}, + {<<"show">>, Presence#presence.show}, + {<<"priority">>, iolist_to_binary(integer_to_list(Presence#presence.priority))} + ], + children = [{xmlcdata, Presence#presence.status}] + } + end, + get_presences({sorted, LUser, LServer}) + ) + }; + get_presences({status, LUser, LServer, LResource}) -> case get_presences({sorted, LUser, LServer}) of - [] -> "unavailable"; - Rs -> + [] -> <<"unavailable">>; + Rs -> {value, R} = lists:keysearch(LResource, 2, Rs), - R#presence.status + R#presence.show %% TODO why was this "status"?! end; + get_presences({status, LUser, LServer}) -> case get_presences({sorted, LUser, LServer}) of [Highest | _Rest] -> - Highest#presence.status; + Highest#presence.show; %% TODO why was this "status"?! _ -> - "unavailable" + <<"unavailable">> end; + get_presences({show, LUser, LServer, LResource}) -> case get_presences({sorted, LUser, LServer}) of - [] -> "unavailable"; - Rs -> + [] -> <<"unavailable">>; + Rs -> {value, R} = lists:keysearch(LResource, 2, Rs), R#presence.show end; + get_presences({show, LUser, LServer}) -> case get_presences({sorted, LUser, LServer}) of [Highest | _Rest] -> Highest#presence.show; _ -> - "unavailable" + <<"unavailable">> end. make_js(WP, Prs, Show_us, Lang, Q) -> {User, Server} = WP#webpresence.us, BaseURL = get_baseurl(Server), US_string = case Show_us of - true -> - "var jabber_user='"++User++"';\n" - "var jabber_server='"++Server++"';\n"; - false -> "" + true -> + ?BC([<<"var jabber_user='">>, User, <<"';\n">>, <<"var jabber_server='">>, Server, <<"';\n">>]); false -> <<"">> end, FunImage = fun(I, S) -> case I of - "---" -> ""; - Icon -> " image:'"++BaseURL++"image/"++Icon++"/"++S++"'\n" + <<"---">> -> <<"">>; + Icon -> ?BC([<<" image:'">>, BaseURL, <<"image/">>, Icon, <<"/">>, S, <<"'\n">>]) end end, R_string_list = case Prs of - [] -> - Show = "unavailable", - ["{show:'"++Show++"',\n" - " long_show:'"++long_show(Show, Lang)++"',\n" - " status:'',\n" % TODO - ++ FunImage(WP#webpresence.icon, Show) ++ - "}"]; + [] -> + Show = <<"unavailable">>, + [?BC([<<"{show:'">>, Show, <<"',\n">>, + <<" long_show:'">>, long_show(Show, Lang), <<"',\n">>, + <<" status:'',\n">>, % TODO + FunImage(WP#webpresence.icon, Show), + <<"}">>])]; _ -> lists:map( fun(Pr) -> Show = Pr#presence.show, - "{name:'"++Pr#presence.resource++"',\n" - " priority:"++intund2string(Pr#presence.priority)++",\n" - " show:'"++Show++"',\n" - " long_show:'"++long_show(Show, Lang)++"',\n" - " status:'"++escape(Pr#presence.status)++"',\n" - ++ FunImage(WP#webpresence.icon, Show) ++ - "}" + ?BC([<<"{name:'">>, Pr#presence.resource, <<"',\n">>, + <<" priority:">>, intund2string(Pr#presence.priority), <<",\n">>, + <<" show:'">>, Show, <<"',\n">>, + <<" long_show:'">>, long_show(Show, Lang), <<"',\n">>, + <<" status:'">>, escape(Pr#presence.status), <<"',\n">>, + FunImage(WP#webpresence.icon, Show), + <<"}">>]) end, Prs) end, R_string = lists:foldl( fun(RS, Res) -> case Res of - "" -> RS; - _ -> Res ++ ",\n" ++ RS + <<"">> -> RS; + _ -> ?BC([Res, <<",\n">>, RS]) end end, - "", + <<"">>, R_string_list), - CB_string = case lists:keysearch("cb", 1, Q) of - {value, {_, CB}} -> " " ++ CB ++ "();"; - _ -> "" + CB_string = case lists:keysearch(<<"cb">>, 1, Q) of + {value, {_, CB}} -> ?BC([<<" ">>, CB, <<"();">>]); + _ -> <<"">> end, - US_string ++ "var jabber_resources=[\n"++R_string++"];" ++ CB_string. + ?BC([US_string, <<"var jabber_resources=[\n">>, R_string, <<"];">>, CB_string]). -long_show("available", Lang) -> ?T("available"); -long_show("chat", Lang) -> ?T("free for chat"); -long_show("away", Lang) -> ?T("away"); -long_show("xa", Lang) -> ?T("extended away"); -long_show("dnd", Lang) -> ?T("do not disturb"); -long_show(_, Lang) -> ?T("unavailable"). +long_show(<<"available">>, Lang) -> ?T(<<"available">>); +long_show(<<"chat">>, Lang) -> ?T(<<"free for chat">>); +long_show(<<"away">>, Lang) -> ?T(<<"away">>); +long_show(<<"xa">>, Lang) -> ?T(<<"extended away">>); +long_show(<<"dnd">>, Lang) -> ?T(<<"do not disturb">>); +long_show(_, Lang) -> ?T(<<"unavailable">>). -%% @spec(A) -> string() -%% where A = integer() | undefined -intund2string(undefined) -> "undefined"; -intund2string(Int) when is_integer(Int) -> integer_to_list(Int). +intund2string(undefined) -> <<"undefined">>; +intund2string(Int) when is_integer(Int) -> list_to_binary(integer_to_list(Int)). escape(S1) -> S2 = re:replace(S1, "\'", "\\'", [global, {return, list}]), @@ -673,11 +786,11 @@ escape(S1) -> get_baseurl(Host) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), Proc ! {tell_baseurl, self()}, - receive + receive {baseurl_is, BaseURL} -> BaseURL end. --define(XML_HEADER, ""). +-define(XML_HEADER, <<"">>). get_pixmaps_directory() -> [{directory, Path} | _] = ets:lookup(pixmaps_dirs, directory), @@ -692,47 +805,57 @@ available_themes(list) -> {error, _} -> [] end; + available_themes(xdata) -> lists:map( fun(Theme) -> - {xmlelement, "option", [{"label", Theme}], - [{xmlelement, "value", [], [{xmlcdata, Theme}]}]} + #xmlel{ + name = <<"option">>, + attrs = [{<<"label">>, iolist_to_binary(Theme)}], + children = [ + #xmlel{ + name = <<"value">>, + attrs = [], + children = [{xmlcdata, iolist_to_binary(Theme)}] + } + ] + } end, available_themes(list)). show_presence({image_no_check, Theme, Pr}) -> Dir = get_pixmaps_directory(), - Image = Pr ++ ".{gif,png,jpg}", - [First | _Rest] = filelib:wildcard(filename:join([Dir, Theme, Image])), + Image = ?BC([Pr, <<".{gif,png,jpg}">>]), + [First | _Rest] = filelib:wildcard(binary_to_list(filename:join([Dir, Theme, Image]))), Mime = string:substr(First, string:len(First) - 2, 3), {ok, Content} = file:read_file(First), - {200, [{"Content-Type", "image/" ++ Mime}], binary_to_list(Content)}; + {200, [{<<"Content-Type">>, ?BC([<<"image/">>, ?BC(Mime)])}], binary_to_list(Content)}; show_presence({image, WP, LUser, LServer}) -> Icon = WP#webpresence.icon, - false = ("---" == Icon), + false = (<<"---">> == Icon), Pr = get_presences({show, LUser, LServer}), show_presence({image_no_check, Icon, Pr}); show_presence({image, WP, LUser, LServer, Theme}) -> - false = ("---" == WP#webpresence.icon), + false = (<<"---">> == WP#webpresence.icon), Pr = get_presences({show, LUser, LServer}), show_presence({image_no_check, Theme, Pr}); show_presence({image_res, WP, LUser, LServer, LResource}) -> Icon = WP#webpresence.icon, - false = ("---" == Icon), + false = (<<"---">> == Icon), Pr = get_presences({show, LUser, LServer, LResource}), show_presence({image_no_check, Icon, Pr}); show_presence({image_res, WP, LUser, LServer, Theme, LResource}) -> - false = ("---" == WP#webpresence.icon), + false = (<<"---">> == WP#webpresence.icon), Pr = get_presences({show, LUser, LServer, LResource}), show_presence({image_no_check, Theme, Pr}); show_presence({xml, WP, LUser, LServer, Show_us}) -> true = WP#webpresence.xml, - Presence_xml = xml:element_to_string(get_presences({xml, LUser, LServer, Show_us})), - {200, [{"Content-Type", "text/xml; charset=utf-8"}], ?XML_HEADER ++ Presence_xml}; + Presence_xml = xml:element_to_binary(get_presences({xml, LUser, LServer, Show_us})), + {200, [{"Content-Type", "text/xml; charset=utf-8"}], ?BC([?XML_HEADER, Presence_xml])}; show_presence({js, WP, LUser, LServer, Show_us, Lang, Q}) -> true = WP#webpresence.js, @@ -753,23 +876,22 @@ show_presence({text, WP, LUser, LServer, LResource}) -> show_presence({avatar, WP, LUser, LServer}) -> true = WP#webpresence.avatar, [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, LServer}), - JID = jlib:make_jid(LUser, LServer, ""), + JID = jlib:make_jid(LUser, LServer, <<"">>), IQ = #iq{type = get, xmlns = ?NS_VCARD}, IQr = Module:Function(JID, JID, IQ), [VCard] = IQr#iq.sub_el, - Mime = xml:get_path_s(VCard, [{elem, "PHOTO"}, {elem, "TYPE"}, cdata]), - BinVal = xml:get_path_s(VCard, [{elem, "PHOTO"}, {elem, "BINVAL"}, cdata]), + Mime = xml:get_path_s(VCard, [{elem, <<"PHOTO">>}, {elem, <<"TYPE">>}, cdata]), + BinVal = xml:get_path_s(VCard, [{elem, <<"PHOTO">>}, {elem, <<"BINVAL">>}, cdata]), Photo = jlib:decode_base64(BinVal), {200, [{"Content-Type", Mime}], Photo}; show_presence({image_example, Theme, Show}) -> Dir = get_pixmaps_directory(), - Image = Show ++ ".{gif,png,jpg}", - [First | _Rest] = filelib:wildcard(filename:join([Dir, Theme, Image])), + Image = ?BC([Show, <<".{gif,png,jpg}">>]), + [First | _Rest] = filelib:wildcard(binary_to_list(filename:join([Dir, Theme, Image]))), Mime = string:substr(First, string:len(First) - 2, 3), {ok, Content} = file:read_file(First), - {200, [{"Content-Type", "image/" ++ Mime}], binary_to_list(Content)}. - + {200, [{<<"Content-Type">>, ?BC([<<"image/">>, ?BC(Mime)])}], binary_to_list(Content)}. %% --------------------- %% Web Publish @@ -777,65 +899,86 @@ show_presence({image_example, Theme, Show}) -> make_xhtml(Els) -> make_xhtml([], Els). make_xhtml(Title, Els) -> - {xmlelement, "html", [{"xmlns", "http://www.w3.org/1999/xhtml"}, - {"xml:lang", "en"}, - {"lang", "en"}], - [{xmlelement, "head", [], - [{xmlelement, "meta", [{"http-equiv", "Content-Type"}, - {"content", "text/html; charset=utf-8"}], []}] - ++ Title}, - {xmlelement, "body", [], Els} - ]}. + #xmlel{ + name = <<"html">>, + attrs = [ + {<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}, + {<<"xml:lang">>, <<"en">>}, + {<<"lang">>, <<"en">>} + ], + children = [ + #xmlel{ + name = <<"head">>, + attrs = [], + children = [ + #xmlel{ + name = <<"meta">>, + attrs = [ + {<<"http-equiv">>, <<"Content-Type">>}, + {<<"content">>, <<"text/html; charset=utf-8">>} + ], + children = [] + } + ] ++ Title + }, + #xmlel{ + name = <<"body">>, + attrs = [], + children = Els + } + ] + }. themes_to_xhtml(Themes) -> ShowL = ["available", "chat", "dnd", "away", "xa", "unavailable"], THeadL = [""] ++ ShowL, - [?XAE("table", [], - [?XE("tr", [?XC("th", T) || T <- THeadL])] ++ - [?XE("tr", [?XC("td", Theme) | - [?XE("td", [?XA("img", [{"src", "image/"++Theme++"/"++T}])]) || T <- ShowL] + [?XAE(<<"table">>, [], + [?XE( <<"tr">>, [?XC(<<"th">>, ?BC(T)) || T <- THeadL])] ++ + [?XE(<<"tr">>, [?XC(<<"td">>, ?BC(Theme)) | + [?XE(<<"td">>, [?XA(<<"img">>, [{<<"src">>, ?BC([<<"image/">>, ?BC(Theme), <<"/">>, ?BC(T)]) }])]) || T <- ShowL] ] ) || Theme <- Themes] ) ]. -parse_lang(Lang) -> hd(string:tokens(Lang,"-")). +parse_lang(Lang) -> iolist_to_binary(hd(string:tokens(binary_to_list(Lang),"-"))). process(LocalPath, Request) -> case catch process2(LocalPath, Request) of {'EXIT', Reason} -> + ?DEBUG("~p", [Request]), ?DEBUG("The call to path ~p in the~nrequest: ~p~ncrashed with error: ~p", [LocalPath, Request, Reason]), - {404, [], make_xhtml([?XC("h1", "Not found")])}; + {404, [], make_xhtml([?XC(<<"h1">>, <<"Not found">>)])}; Res -> Res end. process2([], #request{lang = Lang1}) -> Lang = parse_lang(Lang1), - Title = [?XC("title", ?T("Web Presence"))], - Desc = [?XC("p", ?T("To publish your presence using this system you need a Jabber account in this Jabber server.")++" "++ - ?T("Login with a Jabber client, open the Service Discovery and register in Web Presence.")++ - ?T("You will receive a message with further instructions."))], - Link_themes = [?AC("themes", ?T("Icon Theme"))], - Body = [?XC("h1", ?T("Web Presence"))] ++ Desc ++ Link_themes, + Title = [?XC(<<"title">>, ?T(<<"Web Presence">>))], + Desc = [?XC(<<"p">>, ?BC([ ?T(<<"To publish your presence using this system you need a Jabber account in this Jabber server.">>), <<" ">>, + ?T(<<"Login with a Jabber client, open the Service Discovery and register in Web Presence.">>), + ?T(<<"You will receive a message with further instructions.">>)]))], + Link_themes = [?AC(<<"themes">>, ?T(<<"Icon Theme">>))], + Body = [?XC(<<"h1">>, ?T(<<"Web Presence">>))] ++ Desc ++ Link_themes, make_xhtml(Title, Body); -process2(["themes"], #request{lang = Lang1}) -> +process2([<<"themes">>], #request{lang = Lang1}) -> Lang = parse_lang(Lang1), - Title = [?XC("title", ?T("Web Presence")++" - "++?T("Icon Theme"))], + Title = [?XC(<<"title">>, ?BC([?T(<<"Web Presence">>), <<" - ">>, ?T("Icon Theme")]))], Themes = available_themes(list), Icon_themes = themes_to_xhtml(Themes), - Body = [?XC("h1", ?T("Icon Theme"))] ++ Icon_themes, + Body = [?XC(<<"h1">>, ?T(<<"Icon Theme">>))] ++ Icon_themes, make_xhtml(Title, Body); -process2(["image", Theme, Show], _Request) -> +process2([<<"image">>, Theme, Show], #request{} = _Request) -> Args = {image_example, Theme, Show}, show_presence(Args); -process2(["jid", User, Server | Tail], Request) -> +process2([<<"jid">>, User, Server | Tail], Request) -> serve_web_presence(jid, User, Server, Tail, Request); -process2(["rid", Rid | Tail], Request) -> +process2([<<"rid">>, Rid | Tail], Request) -> [Pr] = mnesia:dirty_index_read(webpresence, Rid, #webpresence.ridurl), {User, Server} = Pr#webpresence.us, serve_web_presence(rid, User, Server, Tail, Request); @@ -844,78 +987,77 @@ process2(["rid", Rid | Tail], Request) -> process2([User, Server | Tail], Request) -> serve_web_presence(jid, User, Server, Tail, Request). - serve_web_presence(TypeURL, User, Server, Tail, #request{lang = Lang1, q = Q}) -> LServer = jlib:nameprep(Server), - true = lists:member(LServer, ?MYHOSTS), + true = lists:member(Server, ?MYHOSTS), LUser = jlib:nodeprep(User), WP = get_wp(LUser, LServer), case TypeURL of jid -> true = WP#webpresence.jidurl; - rid -> true = is_list(WP#webpresence.ridurl) + rid -> true = is_binary(WP#webpresence.ridurl) end, Show_us = (TypeURL == jid), Lang = parse_lang(Lang1), Args = case Tail of - ["image", "theme", Theme, "res", Resource | _] -> - {image_res, WP, LUser, LServer, Theme, Resource}; - ["image", "theme", Theme | _] -> - {image, WP, LUser, LServer, Theme}; - ["image", "res", Resource | _] -> - {image_res, WP, LUser, LServer, Resource}; - ["image" | _] -> - {image, WP, LUser, LServer}; - ["xml"] -> - {xml, WP, LUser, LServer, Show_us}; - ["js"] -> - {js, WP, LUser, LServer, Show_us, Lang, Q}; - ["text"] -> - {text, WP, LUser, LServer}; - ["text", "res", Resource] -> - {text, WP, LUser, LServer, Resource}; - ["avatar" | _] -> - {avatar, WP, LUser, LServer} + [<<"image">>, <<"theme">>, Theme, <<"res">>, Resource | _] -> + {image_res, WP, LUser, LServer, Theme, Resource}; + [<<"image">>, <<"theme">>, Theme | _] -> + {image, WP, LUser, LServer, Theme}; + [<<"image">>, <<"res">>, Resource | _] -> + {image_res, WP, LUser, LServer, Resource}; + [<<"image">> | _] -> + {image, WP, LUser, LServer}; + [<<"xml">>] -> + {xml, WP, LUser, LServer, Show_us}; + [<<"js">>] -> + {js, WP, LUser, LServer, Show_us, Lang, Q}; + [<<"text">>] -> + {text, WP, LUser, LServer}; + [<<"text">>, <<"res">>, Resource] -> + {text, WP, LUser, LServer, Resource}; + [<<"avatar">> | _] -> + {avatar, WP, LUser, LServer} end, show_presence(Args). -%% --------------------- -%% Web Admin -%% --------------------- +%%%% --------------------- +%%%% Web Admin +%%%% --------------------- web_menu_host(Acc, _Host, Lang) -> - [{"webpresence", ?T("Web Presence")} | Acc]. + [{<<"webpresence">>, ?T(<<"Web Presence">>)} | Acc]. -web_page_host(_, _Host, - #request{path = ["webpresence"], +web_page_host(_, _Host, + #request{path = [<<"webpresence">>], lang = Lang} = _Request) -> - Res = [?XCT("h1", "Web Presence"), - ?XE("ul", [ - ?LI([?ACT("stats", "Statistics")]), - ?LI([?ACT("users", "Registered Users")])])], + Res = [?XCT(<<"h1">>, <<"Web Presence">>), + ?XE(<<"ul">>, [ + ?LI([?ACT(<<"stats">>, <<"Statistics">>)]), + ?LI([?ACT(<<"users">>, <<"Registered Users">>)])])], {stop, Res}; -web_page_host(_, Host, - #request{path = ["webpresence", "users"], +web_page_host(_, Host, + #request{path = [<<"webpresence">>, <<"users">>], lang = Lang} = _Request) -> Users = get_users(Host), Table = make_users_table(Users, Lang), - Res = [?XCT("h1", "Web Presence"), - ?XCT("h2", "Registered Users")] ++ Table, + Res = [?XCT(<<"h1">>, <<"Web Presence">>), + ?XCT(<<"h2">>, <<"Registered Users">>)] ++ Table, {stop, Res}; -web_page_host(_, Host, - #request{path = ["webpresence", "stats"], +web_page_host(_, Host, + #request{path = [<<"webpresence">>, <<"stats">>], lang = Lang} = _Request) -> Users = get_users(Host), - Res = [?XCT("h1", "Web Presence"), + Res = [?XCT(<<"h1">>, <<"Web Presence">>), css_table(), - ?XCT("h2", "Statistics")] + ?XCT(<<"h2">>, <<"Statistics">>)] ++ make_stats_options(Users, Lang) ++ make_stats_iconthemes(Users, Lang), {stop, Res}; -web_page_host(Acc, _, _) -> Acc. +web_page_host(Acc, _, _) -> Acc. get_users(Host) -> Select = [{{webpresence, {'$1', Host}, '$2', '$3', '$4', '$5', '$6', '$7', '$8'}, [], ['$$']}], @@ -924,29 +1066,29 @@ get_users(Host) -> make_users_table(Records, Lang) -> TList = lists:map( fun([User, RidUrl, JIDUrl, XML, Avatar, JS, Text, Icon]) -> - ?XE("tr", - [?XE("td", [?AC("../user/"++User++"/", User)]), - ?XC("td", atom_to_list(JIDUrl)), - ?XC("td", ridurl_out(RidUrl)), - ?XC("td", Icon), - ?XC("td", atom_to_list(XML)), - ?XC("td", atom_to_list(JS)), - ?XC("td", atom_to_list(Text)), - ?XC("td", atom_to_list(Avatar))]) + ?XE(<<"tr">>, + [?XE(<<"td">>, [?AC(?BC([<<"../user/">>, User, <<"/">>]), User)]), + ?XC(<<"td">>, iolist_to_binary(atom_to_list(JIDUrl))), + ?XC(<<"td">>, ridurl_out(RidUrl)), + ?XC(<<"td">>, Icon), + ?XC(<<"td">>, iolist_to_binary(atom_to_list(XML))), + ?XC(<<"td">>, iolist_to_binary(atom_to_list(JS))), + ?XC(<<"td">>, iolist_to_binary(atom_to_list(Text))), + ?XC(<<"td">>, iolist_to_binary(atom_to_list(Avatar)))]) end, Records), - [?XE("table", - [?XE("thead", - [?XE("tr", - [?XCT("td", "User"), - ?XCT("td", "Jabber ID"), - ?XCT("td", "Random ID"), - ?XCT("td", "Icon Theme"), - ?XC("td", "XML"), - ?XC("td", "JS"), - ?XCT("td", "Text"), - ?XCT("td", "Avatar") + [?XE(<<"table">>, + [?XE(<<"thead">>, + [?XE(<<"tr">>, + [?XCT(<<"td">>, <<"User">>), + ?XCT(<<"td">>, <<"Jabber ID">>), + ?XCT(<<"td">>, <<"Random ID">>), + ?XCT(<<"td">>, <<"Icon Theme">>), + ?XC(<<"td">>, <<"XML">>), + ?XC(<<"td">>, <<"JS">>), + ?XCT(<<"td">>, <<"Text">>), + ?XCT(<<"td">>, <<"Avatar">>) ])]), - ?XE("tbody", TList)])]. + ?XE(<<"tbody">>, TList)])]. make_stats_options(Records, Lang) -> [RegUsers, JJ, RR, XX, AA, SS, TT, II] = lists:foldl( @@ -957,22 +1099,22 @@ make_stats_options(Records, Lang) -> A2 = A + case Avatar of false -> 0; true -> 1 end, S2 = S + case JS of false -> 0; true -> 1 end, T2 = T + case Text of false -> 0; true -> 1 end, - I2 = I + case Icon of "---" -> 0; _ -> 1 end, + I2 = I + case Icon of <<"---">> -> 0; _ -> 1 end, [N+1, J2, R2, X2, A2, S2, T2, I2] - end, + end, [0, 0, 0, 0, 0, 0, 0, 0], Records), - URLTList = [{"Jabber ID", JJ}, {"Random ID", RR}], - OutputTList = [{"Icon Theme", II}, {"XML", XX}, {"JavaScript", SS}, {"Text", TT}, {"Avatar", AA}], + URLTList = [{<<"Jabber ID">>, JJ}, {<<"Random ID">>, RR}], + OutputTList = [{<<"Icon Theme">>, II}, {<<"XML">>, XX}, {<<"JavaScript">>, SS}, {<<"Text">>, TT}, {<<"Avatar">>, AA}], [ - ?C("Registered Users" ++": "++ integer_to_list(RegUsers)), - ?XCT("h3", "URL Type"), - ?XAE("table", [{"class", "stats"}], - [?XE("tbody", do_stat_table_with(URLTList, RegUsers))] + ?C(?BC([<<"Registered Users">>, <<": ">>, iolist_to_binary(integer_to_list(RegUsers))])), + ?XCT(<<"h3">>, <<"URL Type">>), + ?XAE(<<"table">>, [{<<"class">>, <<"stats">>}], + [?XE(<<"tbody">>, do_stat_table_with(URLTList, RegUsers))] ), - ?XCT("h3", "Output Type"), - ?XAE("table", [{"class", "stats"}], - [?XE("tbody", do_stat_table_with(OutputTList, RegUsers))] + ?XCT(<<"h3">>, <<"Output Type">>), + ?XAE(<<"table">>, [{<<"class">>, <<"stats">>}], + [?XE(<<"tbody">>, do_stat_table_with(OutputTList, RegUsers))] )]. make_stats_iconthemes(Records, Lang) -> @@ -980,13 +1122,13 @@ make_stats_iconthemes(Records, Lang) -> Dict = lists:foldl( fun([_, _, _, _, _, _, _, Icon], D) -> dict:update_counter(Icon, 1, D) - end, + end, dict:from_list(Themes1), Records), Themes = lists:keysort(1, dict:to_list(Dict)), - [?XCT("h3", "Icon Theme"), - ?XAE("table", [{"class", "stats"}], - [?XE("tbody", do_stat_table_with(Themes))] + [?XCT(<<"h3">>, <<"Icon Theme">>), + ?XAE(<<"table">>, [{<<"class">>, <<"stats">>}], + [?XE(<<"tbody">>, do_stat_table_with(Themes))] )]. %% Do table with bars @@ -997,32 +1139,33 @@ do_stat_table_with(Values) -> do_stat_table_with(Values, Total) -> lists:map( - fun({L, N}) -> + fun({L, N}) -> Perc = case Total of - 0 -> "0"; - _ -> integer_to_list(trunc(100 * N / Total)) + 0 -> <<"0">>; + _ -> iolist_to_binary(integer_to_list(trunc(100 * N / Total))) end, do_table_element(?C(L), io_lib:format("~p", [N]), Perc) end, Values). do_table_element(L, [N], Perc) -> - ?XE("tr", - [?XE("td", [L]), - ?XAC("td", [{"class", "alignright"}], [N]), - ?XE("td", - [?XAE("div", - [{"class", "graph"}], - [?XAC("div", - [{"class", "bar"}, {"style", "width: " ++ Perc ++ "%;"}], + ?XE(<<"tr">>, + [?XE(<<"td">>, [L]), + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], [N]), + ?XE(<<"td">>, + [?XAE(<<"div">>, + [{<<"class">>, <<"graph">>}], + [?XAC(<<"div">>, + [{<<"class">>, <<"bar">>}, {<<"style">>, ?BC([<<"width: ">>, Perc, <<"%;">>])}], [] )] )] ), - ?XAC("td", [{"class", "alignright"}], [Perc++"%"]) + ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], [?BC([Perc, <<"%">>])]) ]). + css_table()-> - ?XAE("style", [{"type", "text/css"}], - [?C(".stats { + ?XAE(<<"style">>, [{<<"type">>, <<"text/css">>}], + [?C(<<".stats { padding-left: 20px; padding-top: 10px; } @@ -1041,7 +1184,7 @@ css_table()-> height: 1.5em; line-height: 1.5em; } - .graph .bar span { position: absolute; left: 1em; }")]). + .graph .bar span { position: absolute; left: 1em; }">>)]). %%%--------------------------------