25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

* src/mod_muc/mod_muc_room.erl: Support for history management

(thanks to Sergei Golovan)

* src/mod_stats.erl: Updated error codes (thanks to Sergei
Golovan)
* src/mod_irc/mod_irc.erl: Likewise

* src/mod_configure.erl: "jabber:iq:data" replaced with
"ejabber:config" namespace (thanks to Sergei Golovan)
* src/mod_disco.erl: Likewise

SVN Revision: 204
This commit is contained in:
Alexey Shchepin 2004-02-15 20:10:40 +00:00
parent 35bfd03669
commit fdf25720e0
6 changed files with 639 additions and 325 deletions

View File

@ -1,3 +1,16 @@
2004-02-15 Alexey Shchepin <alexey@sevcom.net>
* src/mod_muc/mod_muc_room.erl: Support for history management
(thanks to Sergei Golovan)
* src/mod_stats.erl: Updated error codes (thanks to Sergei
Golovan)
* src/mod_irc/mod_irc.erl: Likewise
* src/mod_configure.erl: "jabber:iq:data" replaced with
"ejabber:config" namespace (thanks to Sergei Golovan)
* src/mod_disco.erl: Likewise
2004-02-12 Alexey Shchepin <alexey@sevcom.net> 2004-02-12 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: Added <session/> to stream features * src/ejabberd_c2s.erl: Added <session/> to stream features

View File

@ -23,15 +23,15 @@
start(Opts) -> start(Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_IQDATA, gen_iq_handler:add_iq_handler(ejabberd_local, ?NS_EJABBERD_CONFIG,
?MODULE, process_local_iq, IQDisc), ?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_IQDATA, gen_iq_handler:add_iq_handler(ejabberd_sm, ?NS_EJABBERD_CONFIG,
?MODULE, process_sm_iq, IQDisc), ?MODULE, process_sm_iq, IQDisc),
ok. ok.
stop() -> stop() ->
gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_IQDATA), gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_EJABBERD_CONFIG),
gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_IQDATA). gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_EJABBERD_CONFIG).
process_local_iq(From, _To, #iq{id = ID, type = Type, process_local_iq(From, _To, #iq{id = ID, type = Type,
@ -43,38 +43,44 @@ process_local_iq(From, _To, #iq{id = ID, type = Type,
Lang = xml:get_tag_attr_s("xml:lang", SubEl), Lang = xml:get_tag_attr_s("xml:lang", SubEl),
case Type of case Type of
set -> set ->
case xml:get_tag_attr_s("type", SubEl) of XDataEl = find_xdata_el(SubEl),
"cancel" -> case XDataEl of
IQ#iq{type = result, false ->
sub_el = [{xmlelement, "query", IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
[{"xmlns", XMLNS}], []}]}; {xmlelement, _Name, Attrs, SubEls} ->
"submit" -> case xml:get_attr_s("type", Attrs) of
XData = jlib:parse_xdata_submit(SubEl), "cancel" ->
case XData of IQ#iq{type = result,
invalid -> sub_el = [{xmlelement, "query",
IQ#iq{type = error, [{"xmlns", XMLNS}], []}]};
sub_el = [SubEl, ?ERR_BAD_REQUEST]}; "submit" ->
_ -> XData = jlib:parse_xdata_submit(XDataEl),
Node = case XData of
string:tokens( invalid ->
xml:get_tag_attr_s("node", SubEl),
"/"),
case set_form(Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error, IQ#iq{type = error,
sub_el = [SubEl, Error]} sub_el = [SubEl, ?ERR_BAD_REQUEST]};
end _ ->
end; Node =
_ -> string:tokens(
IQ#iq{type = error, xml:get_tag_attr_s("node", SubEl),
sub_el = [SubEl, ?ERR_NOT_ALLOWED]} "/"),
case set_form(Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error,
sub_el = [SubEl, Error]}
end
end;
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end
end; end;
get -> get ->
Node = Node =
@ -137,27 +143,28 @@ get_form(["running nodes", ENode, "DB"], Lang) ->
{error, ?ERR_INTERNAL_SERVER_ERROR}; {error, ?ERR_INTERNAL_SERVER_ERROR};
Tables -> Tables ->
STables = lists:sort(Tables), STables = lists:sort(Tables),
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "DB Tables Configuration")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "DB Tables Configuration")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Choose storage type of tables")}]} | translate:translate(
lists:map( Lang, "Choose storage type of tables")}]} |
fun(Table) -> lists:map(
case rpc:call(Node, fun(Table) ->
mnesia, case rpc:call(Node,
table_info, mnesia,
[Table, storage_type]) of table_info,
{badrpc, _} -> [Table, storage_type]) of
?TABLEFIELD(Table, unknown); {badrpc, _} ->
Type -> ?TABLEFIELD(Table, unknown);
?TABLEFIELD(Table, Type) Type ->
end ?TABLEFIELD(Table, Type)
end, STables) end
]} end, STables)
]}]}
end end
end; end;
@ -171,217 +178,228 @@ get_form(["running nodes", ENode, "modules", "stop"], Lang) ->
{error, ?ERR_INTERNAL_SERVER_ERROR}; {error, ?ERR_INTERNAL_SERVER_ERROR};
Modules -> Modules ->
SModules = lists:sort(Modules), SModules = lists:sort(Modules),
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Stop Modules")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Stop Modules")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Choose modules to stop")}]} | translate:translate(
lists:map(fun(M) -> Lang, "Choose modules to stop")}]} |
S = atom_to_list(M), lists:map(fun(M) ->
?XFIELD("boolean", S, S, "0") S = atom_to_list(M),
end, SModules) ?XFIELD("boolean", S, S, "0")
]} end, SModules)
]}]}
end end
end; end;
get_form(["running nodes", ENode, "modules", "start"], Lang) -> get_form(["running nodes", ENode, "modules", "start"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Start Modules")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Start Modules")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter list of {Module, [Options]}")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-multi"}, Lang, "Enter list of {Module, [Options]}")}]},
{"label", {xmlelement, "field", [{"type", "text-multi"},
translate:translate( {"label",
Lang, "List of modules to start")}, translate:translate(
{"var", "modules"}], Lang, "List of modules to start")},
[{xmlelement, "value", [], [{xmlcdata, "[]."}]}] {"var", "modules"}],
} [{xmlelement, "value", [], [{xmlcdata, "[]."}]}]
]}; }
]}]};
get_form(["running nodes", ENode, "backup", "backup"], Lang) -> get_form(["running nodes", ENode, "backup", "backup"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Backup to File")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Backup to File")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter path to backup file")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Enter path to backup file")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate( {"label",
Lang, "Path to File")}, translate:translate(
{"var", "path"}], Lang, "Path to File")},
[{xmlelement, "value", [], [{xmlcdata, ""}]}] {"var", "path"}],
} [{xmlelement, "value", [], [{xmlcdata, ""}]}]
]}; }
]}]};
get_form(["running nodes", ENode, "backup", "restore"], Lang) -> get_form(["running nodes", ENode, "backup", "restore"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Restore Backup from File")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Restore Backup from File")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter path to backup file")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Enter path to backup file")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate( {"label",
Lang, "Path to File")}, translate:translate(
{"var", "path"}], Lang, "Path to File")},
[{xmlelement, "value", [], [{xmlcdata, ""}]}] {"var", "path"}],
} [{xmlelement, "value", [], [{xmlcdata, ""}]}]
]}; }
]}]};
get_form(["running nodes", ENode, "backup", "textfile"], Lang) -> get_form(["running nodes", ENode, "backup", "textfile"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Dump Backup to Text File")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Dump Backup to Text File")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter path to text file")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Enter path to text file")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate( {"label",
Lang, "Path to File")}, translate:translate(
{"var", "path"}], Lang, "Path to File")},
[{xmlelement, "value", [], [{xmlcdata, ""}]}] {"var", "path"}],
} [{xmlelement, "value", [], [{xmlcdata, ""}]}]
]}; }
]}]};
get_form(["running nodes", ENode, "import", "file"], Lang) -> get_form(["running nodes", ENode, "import", "file"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Import User from File")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Import User from File")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter path to jabberd1.4 spool file")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Enter path to jabberd1.4 spool file")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate( {"label",
Lang, "Path to File")}, translate:translate(
{"var", "path"}], Lang, "Path to File")},
[{xmlelement, "value", [], [{xmlcdata, ""}]}] {"var", "path"}],
} [{xmlelement, "value", [], [{xmlcdata, ""}]}]
]}; }
]}]};
get_form(["running nodes", ENode, "import", "dir"], Lang) -> get_form(["running nodes", ENode, "import", "dir"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Import User from Dir")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Import User from Dir")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Enter path to jabberd1.4 spool dir")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Enter path to jabberd1.4 spool dir")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate( {"label",
Lang, "Path to Dir")}, translate:translate(
{"var", "path"}], Lang, "Path to Dir")},
[{xmlelement, "value", [], [{xmlcdata, ""}]}] {"var", "path"}],
} [{xmlelement, "value", [], [{xmlcdata, ""}]}]
]}; }
]}]};
get_form(["config", "hostname"], Lang) -> get_form(["config", "hostname"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Hostname Configuration")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Hostname Configuration")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Choose host name")}]}, translate:translate(
{xmlelement, "field", [{"type", "text-single"}, Lang, "Choose host name")}]},
{"label", {xmlelement, "field", [{"type", "text-single"},
translate:translate(Lang, "Host name")}, {"label",
{"var", "hostname"}], translate:translate(Lang, "Host name")},
[{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]} {"var", "hostname"}],
]}; [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
]}]};
get_form(["config", "acls"], Lang) -> get_form(["config", "acls"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "ACLs Configuration")}]}, translate:translate(
%{xmlelement, "instructions", [], Lang, "ACLs Configuration")}]},
% [{xmlcdata, %{xmlelement, "instructions", [],
% translate:translate( % [{xmlcdata,
% Lang, "")}]}, % translate:translate(
{xmlelement, "field", [{"type", "text-multi"}, % Lang, "")}]},
{"label", {xmlelement, "field", [{"type", "text-multi"},
translate:translate(Lang, "ACLs")}, {"label",
{"var", "acls"}], translate:translate(Lang, "ACLs")},
lists:map(fun(S) -> {"var", "acls"}],
{xmlelement, "value", [], [{xmlcdata, S}]} lists:map(fun(S) ->
end, {xmlelement, "value", [], [{xmlcdata, S}]}
string:tokens( end,
lists:flatten(io_lib:format("~p.", string:tokens(
[ets:tab2list(acl)])), lists:flatten(io_lib:format("~p.",
"\n")) [ets:tab2list(acl)])),
} "\n"))
]}; }
]}]};
get_form(["config", "access"], Lang) -> get_form(["config", "access"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Access Configuration")}]}, translate:translate(
%{xmlelement, "instructions", [], Lang, "Access Configuration")}]},
% [{xmlcdata, %{xmlelement, "instructions", [],
% translate:translate( % [{xmlcdata,
% Lang, "")}]}, % translate:translate(
{xmlelement, "field", [{"type", "text-multi"}, % Lang, "")}]},
{"label", {xmlelement, "field", [{"type", "text-multi"},
translate:translate( {"label",
Lang, "Access Rules")}, translate:translate(
{"var", "access"}], Lang, "Access Rules")},
lists:map(fun(S) -> {"var", "access"}],
{xmlelement, "value", [], [{xmlcdata, S}]} lists:map(fun(S) ->
end, {xmlelement, "value", [], [{xmlcdata, S}]}
string:tokens( end,
lists:flatten( string:tokens(
io_lib:format( lists:flatten(
"~p.", io_lib:format(
[ets:select(config, "~p.",
[{{config, {access, '$1'}, '$2'}, [ets:select(config,
[], [{{config, {access, '$1'}, '$2'},
[{{access, '$1', '$2'}}]}]) [],
])), [{{access, '$1', '$2'}}]}])
"\n")) ])),
} "\n"))
]}; }
]}]};
get_form(["config", "remusers"], Lang) -> get_form(["config", "remusers"], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Remove Users")}]}, translate:translate(
{xmlelement, "instructions", [], Lang, "Remove Users")}]},
[{xmlcdata, {xmlelement, "instructions", [],
translate:translate( [{xmlcdata,
Lang, "Choose users to remove")}]}] ++ translate:translate(
case catch ejabberd_auth:dirty_get_registered_users() of Lang, "Choose users to remove")}]}] ++
{'EXIT', Reason} -> case catch ejabberd_auth:dirty_get_registered_users() of
[]; {'EXIT', Reason} ->
Users -> [];
lists:map(fun(U) -> Users ->
?XFIELD("boolean", U, U, "0") lists:map(fun(U) ->
end, lists:sort(Users)) ?XFIELD("boolean", U, U, "0")
end end, lists:sort(Users))
}; end
}]};
get_form(_, Lang) -> get_form(_, Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}. {error, ?ERR_SERVICE_UNAVAILABLE}.
@ -712,39 +730,45 @@ process_sm_iq(From, To,
Lang = xml:get_tag_attr_s("xml:lang", SubEl), Lang = xml:get_tag_attr_s("xml:lang", SubEl),
case Type of case Type of
set -> set ->
case xml:get_tag_attr_s("type", SubEl) of XDataEl = find_xdata_el(SubEl),
"cancel" -> case XDataEl of
IQ#iq{type = result, false ->
sub_el = [{xmlelement, "query", IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
[{"xmlns", XMLNS}], []}]}; {xmlelement, _Name, Attrs, SubEls} ->
"submit" -> case xml:get_attr_s("type", Attrs) of
XData = jlib:parse_xdata_submit(SubEl), "cancel" ->
case XData of IQ#iq{type = result,
invalid -> sub_el = [{xmlelement, "query",
IQ#iq{type = error, [{"xmlns", XMLNS}], []}]};
sub_el = [SubEl, ?ERR_BAD_REQUEST]}; "submit" ->
_ -> XData = jlib:parse_xdata_submit(XDataEl),
Node = case XData of
string:tokens( invalid ->
xml:get_tag_attr_s("node", SubEl),
"/"),
case set_sm_form(
User, Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error, IQ#iq{type = error,
sub_el = [SubEl, Error]} sub_el = [SubEl, ?ERR_BAD_REQUEST]};
end _ ->
end; Node =
_ -> string:tokens(
IQ#iq{type = error, xml:get_tag_attr_s("node", SubEl),
sub_el = [SubEl, ?ERR_NOT_ALLOWED]} "/"),
case set_sm_form(
User, Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el =
[{xmlelement, "query",
[{"xmlns", XMLNS}],
Res
}]};
{error, Error} ->
IQ#iq{type = error,
sub_el = [SubEl, Error]}
end
end;
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end
end; end;
get -> get ->
Node = Node =
@ -764,34 +788,35 @@ process_sm_iq(From, To,
get_sm_form(User, [], Lang) -> get_sm_form(User, [], Lang) ->
{result, [{xmlelement, "title", [], {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
[{xmlcdata, [{xmlelement, "title", [],
translate:translate( [{xmlcdata,
Lang, "Administration of " ++ User)}]}, translate:translate(
%{xmlelement, "instructions", [], Lang, "Administration of " ++ User)}]},
% [{xmlcdata, %{xmlelement, "instructions", [],
% translate:translate( % [{xmlcdata,
% Lang, "Choose host name")}]}, % translate:translate(
{xmlelement, "field", % Lang, "Choose host name")}]},
[{"type", "list-single"}, {xmlelement, "field",
{"label", translate:translate(Lang, "Action on user")}, [{"type", "list-single"},
{"var", "action"}], {"label", translate:translate(Lang, "Action on user")},
[{xmlelement, "value", [], [{xmlcdata, "edit"}]}, {"var", "action"}],
{xmlelement, "option", [{xmlelement, "value", [], [{xmlcdata, "edit"}]},
[{"label", translate:translate(Lang, "Edit Properties")}], {xmlelement, "option",
[{xmlelement, "value", [], [{xmlcdata, "edit"}]}]}, [{"label", translate:translate(Lang, "Edit Properties")}],
{xmlelement, "option", [{xmlelement, "value", [], [{xmlcdata, "edit"}]}]},
[{"label", translate:translate(Lang, "Remove User")}], {xmlelement, "option",
[{xmlelement, "value", [], [{xmlcdata, "remove"}]}]} [{"label", translate:translate(Lang, "Remove User")}],
]}, [{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
?XFIELD("text-private", "Password", "password", ]},
ejabberd_auth:get_password_s(User)) ?XFIELD("text-private", "Password", "password",
%{xmlelement, "field", [{"type", "text-single"}, ejabberd_auth:get_password_s(User))
% {"label", %{xmlelement, "field", [{"type", "text-single"},
% translate:translate(Lang, "Host name")}, % {"label",
% {"var", "hostname"}], % translate:translate(Lang, "Host name")},
% [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]} % {"var", "hostname"}],
]}; % [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
]}]};
get_sm_form(_, _, Lang) -> get_sm_form(_, _, Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}. {error, ?ERR_SERVICE_UNAVAILABLE}.
@ -820,3 +845,20 @@ set_sm_form(User, [], Lang, XData) ->
end; end;
set_sm_form(_, _, Lang, XData) -> set_sm_form(_, _, Lang, XData) ->
{error, ?ERR_SERVICE_UNAVAILABLE}. {error, ?ERR_SERVICE_UNAVAILABLE}.
find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
find_xdata_el1(SubEls).
find_xdata_el1([]) ->
false;
find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_XDATA ->
{xmlelement, Name, Attrs, SubEls};
_ ->
find_xdata_el1(Els)
end;
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).

View File

@ -157,7 +157,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
"query", "query",
[{"xmlns", XMLNS}, [{"xmlns", XMLNS},
{"node", SNode}], {"node", SNode}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "modules"]} -> {allow, ["running nodes", ENode, "modules"]} ->
?EMPTY_INFO_RESULT; ?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "modules", _]} -> {allow, ["running nodes", ENode, "modules", _]} ->
@ -165,7 +165,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query", sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}, [{"xmlns", XMLNS},
{"node", SNode}], {"node", SNode}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "backup"]} -> {allow, ["running nodes", ENode, "backup"]} ->
?EMPTY_INFO_RESULT; ?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "backup", _]} -> {allow, ["running nodes", ENode, "backup", _]} ->
@ -173,7 +173,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query", sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}, [{"xmlns", XMLNS},
{"node", SNode}], {"node", SNode}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "import"]} -> {allow, ["running nodes", ENode, "import"]} ->
?EMPTY_INFO_RESULT; ?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "import", _]} -> {allow, ["running nodes", ENode, "import", _]} ->
@ -181,13 +181,13 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query", sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}, [{"xmlns", XMLNS},
{"node", SNode}], {"node", SNode}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["config", _]} -> {allow, ["config", _]} ->
IQ#iq{type = result, IQ#iq{type = result,
sub_el = [{xmlelement, "query", sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}, [{"xmlns", XMLNS},
{"node", SNode}], {"node", SNode}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
_ -> _ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
end end
@ -489,7 +489,7 @@ process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
"" -> "" ->
IQ#iq{type = result, IQ#iq{type = result,
sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}],
[feature_to_xml({?NS_IQDATA})]}]}; [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
_ -> _ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]} IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
end end

View File

@ -358,16 +358,16 @@ set_form(From, [], Lang, XData) ->
{atomic, _} -> {atomic, _} ->
{result, []}; {result, []};
_ -> _ ->
{error, "406", "Not Acceptable"} {error, ?ERR_NOT_ACCEPTABLE}
end; end;
_ -> _ ->
{error, "406", "Not Acceptable"} {error, ?ERR_NOT_ACCEPTABLE}
end; end;
_ -> _ ->
{error, "406", "Not Acceptable"} {error, ?ERR_NOT_ACCEPTABLE}
end; end;
_ -> _ ->
{error, "406", "Not Acceptable"} {error, ?ERR_NOT_ACCEPTABLE}
end; end;

View File

@ -63,7 +63,7 @@
config = #config{}, config = #config{},
users = ?DICT:new(), users = ?DICT:new(),
affiliations = ?DICT:new(), affiliations = ?DICT:new(),
history = lqueue_new(10), history = lqueue_new(20),
subject = "", subject = "",
subject_author = ""}). subject_author = ""}).
@ -831,7 +831,8 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
add_online_user(From, Nick, Role, StateData)), add_online_user(From, Nick, Role, StateData)),
send_new_presence(From, NewState), send_new_presence(From, NewState),
send_existing_presences(From, NewState), send_existing_presences(From, NewState),
case send_history(From, NewState) of Shift = count_stanza_shift(Nick, Els, NewState),
case send_history(From, Shift, NewState) of
true -> true ->
ok; ok;
_ -> _ ->
@ -879,7 +880,259 @@ extract_password([{xmlelement, Name, Attrs, SubEls} = El | Els]) ->
extract_password([_ | Els]) -> extract_password([_ | Els]) ->
extract_password(Els). extract_password(Els).
count_stanza_shift(Nick, Els, StateData) ->
HL = lqueue_to_list(StateData#state.history),
Since = extract_history(Els, "since"),
Shift0 = case Since of
false ->
0;
_ ->
Sin = calendar:datetime_to_gregorian_seconds(Since),
count_seconds_shift(Sin, HL)
end,
Seconds = extract_history(Els, "seconds"),
Shift1 = case Seconds of
false ->
0;
_ ->
Sec = calendar:datetime_to_gregorian_seconds(
calendar:now_to_universal_time(now())) - Seconds,
count_seconds_shift(Sec, HL)
end,
MaxStanzas = extract_history(Els, "maxstanzas"),
Shift2 = case MaxStanzas of
false ->
0;
_ ->
count_maxstanzas_shift(MaxStanzas, HL)
end,
MaxChars = extract_history(Els, "maxchars"),
Shift3 = case MaxChars of
false ->
0;
_ ->
count_maxchars_shift(Nick, MaxChars, HL)
end,
lists:max([Shift0, Shift1, Shift2, Shift3]).
count_seconds_shift(Seconds, HistoryList) ->
lists:sum(
lists:map(
fun({_Nick, _Packet, _HaveSubject, TimeStamp, _Size}) ->
T = calendar:datetime_to_gregorian_seconds(TimeStamp),
if
T < Seconds ->
1;
true ->
0
end
end, HistoryList)).
count_maxstanzas_shift(MaxStanzas, HistoryList) ->
S = length(HistoryList) - MaxStanzas,
if
S =< 0 ->
0;
true ->
S
end.
count_maxchars_shift(Nick, MaxSize, HistoryList) ->
NLen = string:len(Nick) + 1,
Sizes = lists:map(
fun({_Nick, _Packet, _HaveSubject, _TimeStamp, Size}) ->
Size + NLen
end, HistoryList),
calc_shift(MaxSize, Sizes).
calc_shift(MaxSize, Sizes) ->
Total = lists:sum(Sizes),
calc_shift(MaxSize, Total, 0, Sizes).
calc_shift(_MaxSize, _Size, Shift, []) ->
Shift;
calc_shift(MaxSize, Size, Shift, [S | TSizes]) ->
if
MaxSize >= Size ->
Shift;
true ->
calc_shift(MaxSize, Size - S, Shift + 1, TSizes)
end.
extract_history([], Type) ->
false;
extract_history([{xmlelement, Name, Attrs, SubEls} = El | Els], Type) ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_MUC ->
AttrVal = xml:get_path_s(El,
[{elem, "history"}, {attr, Type}]),
case Type of
"since" ->
parse_datetime(AttrVal);
_ ->
case catch list_to_integer(AttrVal) of
{'EXIT', _} ->
false;
IntVal ->
if
IntVal >= 0 ->
IntVal;
true ->
false
end
end
end;
_ ->
extract_history(Els, Type)
end;
extract_history([_ | Els], Type) ->
extract_history(Els, Type).
% JEP-0082
% yyyy-mm-ddThh:mm:ss[.sss]{Z|{+|-}hh:mm} -> {{yyyy, mm, dd}, {hh, mm, ss}} (UTC)
parse_datetime(TimeStr) ->
DateTime = string:tokens(TimeStr, "T"),
case DateTime of
[Date, Time] ->
case parse_date(Date) of
false ->
false;
D ->
case parse_time(Time) of
false ->
false;
{T, TZH, TZM} ->
S = calendar:datetime_to_gregorian_seconds(
{D, T}),
calendar:gregorian_seconds_to_datetime(
S - TZH * 60 * 60 - TZM * 60 * 30)
end
end;
_ ->
false
end.
% yyyy-mm-dd
parse_date(Date) ->
YearMonthDay = string:tokens(Date, "-"),
case length(YearMonthDay) of
3 ->
[Y, M, D] = lists:map(
fun(L)->
case catch list_to_integer(L) of
{'EXIT', _} ->
false;
Int ->
Int
end
end, YearMonthDay),
case catch calendar:valid_date(Y, M, D) of
true ->
{Y, M, D};
_ ->
false
end;
_ ->
false
end.
% hh:mm:ss[.sss]TZD
parse_time(Time) ->
case string:str(Time, "Z") of
0 ->
parse_time_with_timezone(Time);
_ ->
[T | _] = string:tokens(Time, "Z"),
case parse_time1(T) of
false ->
false;
TT ->
{TT, 0, 0}
end
end.
parse_time_with_timezone(Time) ->
case string:str(Time, "+") of
0 ->
case string:str(Time, "-") of
0 ->
false;
_ ->
parse_time_with_timezone(Time, "-")
end;
_ ->
parse_time_with_timezone(Time, "+")
end.
parse_time_with_timezone(Time, Delim) ->
TTZ = string:tokens(Time, Delim),
case TTZ of
[T, TZ] ->
case parse_timezone(TZ) of
false ->
false;
{TZH, TZM} ->
case parse_time1(T) of
false ->
false;
TT ->
case Delim of
"-" ->
{TT, -TZH, -TZM};
"+" ->
{TT, TZH, TZM};
_ ->
false
end
end
end;
_ ->
false
end.
parse_timezone(TZ) ->
case string:tokens(TZ, ":") of
[H, M] ->
case check_list([{H, 12}, {M, 60}]) of
{[H, M], true} ->
{H, M};
_ ->
false
end;
_ ->
false
end.
parse_time1(Time) ->
case string:tokens(Time, ".") of
[HMS | _] ->
case string:tokens(HMS, ":") of
[H, M, S] ->
case check_list([{H, 24}, {M, 60}, {S, 60}]) of
{[H1, M1, S1], true} ->
{H1, M1, S1};
_ ->
false
end;
_ ->
false
end;
_ ->
false
end.
check_list(List) ->
lists:mapfoldl(
fun({L, N}, B)->
case catch list_to_integer(L) of
{'EXIT', _} ->
{false, false};
Int when (Int >= 0) and (Int =< N) ->
{Int, B};
_ ->
{false, false}
end
end, true, List).
send_update_presence(JID, StateData) -> send_update_presence(JID, StateData) ->
LJID = jlib:jid_tolower(JID), LJID = jlib:jid_tolower(JID),
@ -1087,22 +1340,28 @@ add_message_to_history(FromNick, Packet, StateData) ->
_ -> _ ->
true true
end, end,
TimeStamp = calendar:now_to_universal_time(now()),
TSPacket = append_subtags(Packet, TSPacket = append_subtags(Packet,
[jlib:timestamp_to_xml( [jlib:timestamp_to_xml(TimeStamp)]),
calendar:now_to_universal_time( {xmlelement, Name, Attrs, Els} = TSPacket,
now()))]), SPacket = jlib:replace_from_to(
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject}, StateData#state.history), jlib:jid_replace_resource(StateData#state.jid, FromNick),
StateData#state.jid,
TSPacket),
Size = string:len(xml:element_to_string(SPacket)),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
StateData#state.history),
StateData#state{history = Q1}. StateData#state{history = Q1}.
send_history(JID, StateData) -> send_history(JID, Shift, StateData) ->
lists:foldl( lists:foldl(
fun({Nick, Packet, HaveSubject}, B) -> fun({Nick, Packet, HaveSubject, _TimeStamp, _Size}, B) ->
ejabberd_router:route( ejabberd_router:route(
jlib:jid_replace_resource(StateData#state.jid, Nick), jlib:jid_replace_resource(StateData#state.jid, Nick),
JID, JID,
Packet), Packet),
B or HaveSubject B or HaveSubject
end, false, lqueue_to_list(StateData#state.history)). end, false, lists:nthtail(Shift, lqueue_to_list(StateData#state.history))).
send_subject(JID, StateData) -> send_subject(JID, StateData) ->

View File

@ -89,7 +89,7 @@ get_local_stats(["running nodes", _], []) ->
get_local_stats(["running nodes", ENode], Names) -> get_local_stats(["running nodes", ENode], Names) ->
case search_running_node(ENode) of case search_running_node(ENode) of
false -> false ->
{error, "404", "Not Found"}; {error, ?ERR_ITEM_NOT_FOUND};
Node -> Node ->
{result, {result,
lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)} lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}