summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--src/mod_configure.erl652
-rw-r--r--src/mod_disco.erl12
-rw-r--r--src/mod_irc/mod_irc.erl8
-rw-r--r--src/mod_muc/mod_muc_room.erl277
-rw-r--r--src/mod_stats.erl2
6 files changed, 639 insertions, 325 deletions
diff --git a/ChangeLog b/ChangeLog
index d55aae06..ae8dce87 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* src/ejabberd_c2s.erl: Added <session/> to stream features
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index edf55202..fba0b31d 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -23,15 +23,15 @@
start(Opts) ->
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),
- 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),
ok.
stop() ->
- gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_IQDATA),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_IQDATA).
+ gen_iq_handler:remove_iq_handler(ejabberd_local, ?NS_EJABBERD_CONFIG),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, ?NS_EJABBERD_CONFIG).
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),
case Type of
set ->
- case xml:get_tag_attr_s("type", SubEl) of
- "cancel" ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}], []}]};
- "submit" ->
- XData = jlib:parse_xdata_submit(SubEl),
- case XData of
- invalid ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_BAD_REQUEST]};
- _ ->
- Node =
- string:tokens(
- 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} ->
+ XDataEl = find_xdata_el(SubEl),
+ case XDataEl of
+ false ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
+ {xmlelement, _Name, Attrs, SubEls} ->
+ case xml:get_attr_s("type", Attrs) of
+ "cancel" ->
+ IQ#iq{type = result,
+ sub_el = [{xmlelement, "query",
+ [{"xmlns", XMLNS}], []}]};
+ "submit" ->
+ XData = jlib:parse_xdata_submit(XDataEl),
+ case XData of
+ invalid ->
IQ#iq{type = error,
- sub_el = [SubEl, Error]}
- end
- end;
- _ ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
+ sub_el = [SubEl, ?ERR_BAD_REQUEST]};
+ _ ->
+ Node =
+ string:tokens(
+ 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,
+ sub_el = [SubEl, Error]}
+ end
+ end;
+ _ ->
+ IQ#iq{type = error,
+ sub_el = [SubEl, ?ERR_BAD_REQUEST]}
+ end
end;
get ->
Node =
@@ -137,27 +143,28 @@ get_form(["running nodes", ENode, "DB"], Lang) ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
Tables ->
STables = lists:sort(Tables),
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "DB Tables Configuration")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Choose storage type of tables")}]} |
- lists:map(
- fun(Table) ->
- case rpc:call(Node,
- mnesia,
- table_info,
- [Table, storage_type]) of
- {badrpc, _} ->
- ?TABLEFIELD(Table, unknown);
- Type ->
- ?TABLEFIELD(Table, Type)
- end
- end, STables)
- ]}
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "DB Tables Configuration")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Choose storage type of tables")}]} |
+ lists:map(
+ fun(Table) ->
+ case rpc:call(Node,
+ mnesia,
+ table_info,
+ [Table, storage_type]) of
+ {badrpc, _} ->
+ ?TABLEFIELD(Table, unknown);
+ Type ->
+ ?TABLEFIELD(Table, Type)
+ end
+ end, STables)
+ ]}]}
end
end;
@@ -171,217 +178,228 @@ get_form(["running nodes", ENode, "modules", "stop"], Lang) ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
Modules ->
SModules = lists:sort(Modules),
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Stop Modules")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Choose modules to stop")}]} |
- lists:map(fun(M) ->
- S = atom_to_list(M),
- ?XFIELD("boolean", S, S, "0")
- end, SModules)
- ]}
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Stop Modules")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Choose modules to stop")}]} |
+ lists:map(fun(M) ->
+ S = atom_to_list(M),
+ ?XFIELD("boolean", S, S, "0")
+ end, SModules)
+ ]}]}
end
end;
get_form(["running nodes", ENode, "modules", "start"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Start Modules")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter list of {Module, [Options]}")}]},
- {xmlelement, "field", [{"type", "text-multi"},
- {"label",
- translate:translate(
- Lang, "List of modules to start")},
- {"var", "modules"}],
- [{xmlelement, "value", [], [{xmlcdata, "[]."}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Start Modules")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter list of {Module, [Options]}")}]},
+ {xmlelement, "field", [{"type", "text-multi"},
+ {"label",
+ translate:translate(
+ Lang, "List of modules to start")},
+ {"var", "modules"}],
+ [{xmlelement, "value", [], [{xmlcdata, "[]."}]}]
+ }
+ ]}]};
get_form(["running nodes", ENode, "backup", "backup"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Backup to File")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter path to backup file")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "Path to File")},
- {"var", "path"}],
- [{xmlelement, "value", [], [{xmlcdata, ""}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Backup to File")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter path to backup file")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "Path to File")},
+ {"var", "path"}],
+ [{xmlelement, "value", [], [{xmlcdata, ""}]}]
+ }
+ ]}]};
get_form(["running nodes", ENode, "backup", "restore"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Restore Backup from File")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter path to backup file")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "Path to File")},
- {"var", "path"}],
- [{xmlelement, "value", [], [{xmlcdata, ""}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Restore Backup from File")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter path to backup file")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "Path to File")},
+ {"var", "path"}],
+ [{xmlelement, "value", [], [{xmlcdata, ""}]}]
+ }
+ ]}]};
get_form(["running nodes", ENode, "backup", "textfile"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Dump Backup to Text File")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter path to text file")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "Path to File")},
- {"var", "path"}],
- [{xmlelement, "value", [], [{xmlcdata, ""}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Dump Backup to Text File")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter path to text file")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "Path to File")},
+ {"var", "path"}],
+ [{xmlelement, "value", [], [{xmlcdata, ""}]}]
+ }
+ ]}]};
get_form(["running nodes", ENode, "import", "file"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Import User from File")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter path to jabberd1.4 spool file")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "Path to File")},
- {"var", "path"}],
- [{xmlelement, "value", [], [{xmlcdata, ""}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Import User from File")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter path to jabberd1.4 spool file")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "Path to File")},
+ {"var", "path"}],
+ [{xmlelement, "value", [], [{xmlcdata, ""}]}]
+ }
+ ]}]};
get_form(["running nodes", ENode, "import", "dir"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Import User from Dir")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter path to jabberd1.4 spool dir")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "Path to Dir")},
- {"var", "path"}],
- [{xmlelement, "value", [], [{xmlcdata, ""}]}]
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Import User from Dir")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Enter path to jabberd1.4 spool dir")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(
+ Lang, "Path to Dir")},
+ {"var", "path"}],
+ [{xmlelement, "value", [], [{xmlcdata, ""}]}]
+ }
+ ]}]};
get_form(["config", "hostname"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Hostname Configuration")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Choose host name")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(Lang, "Host name")},
- {"var", "hostname"}],
- [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Hostname Configuration")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Choose host name")}]},
+ {xmlelement, "field", [{"type", "text-single"},
+ {"label",
+ translate:translate(Lang, "Host name")},
+ {"var", "hostname"}],
+ [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
+ ]}]};
get_form(["config", "acls"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "ACLs Configuration")}]},
- %{xmlelement, "instructions", [],
- % [{xmlcdata,
- % translate:translate(
- % Lang, "")}]},
- {xmlelement, "field", [{"type", "text-multi"},
- {"label",
- translate:translate(Lang, "ACLs")},
- {"var", "acls"}],
- lists:map(fun(S) ->
- {xmlelement, "value", [], [{xmlcdata, S}]}
- end,
- string:tokens(
- lists:flatten(io_lib:format("~p.",
- [ets:tab2list(acl)])),
- "\n"))
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "ACLs Configuration")}]},
+ %{xmlelement, "instructions", [],
+ % [{xmlcdata,
+ % translate:translate(
+ % Lang, "")}]},
+ {xmlelement, "field", [{"type", "text-multi"},
+ {"label",
+ translate:translate(Lang, "ACLs")},
+ {"var", "acls"}],
+ lists:map(fun(S) ->
+ {xmlelement, "value", [], [{xmlcdata, S}]}
+ end,
+ string:tokens(
+ lists:flatten(io_lib:format("~p.",
+ [ets:tab2list(acl)])),
+ "\n"))
+ }
+ ]}]};
get_form(["config", "access"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Access Configuration")}]},
- %{xmlelement, "instructions", [],
- % [{xmlcdata,
- % translate:translate(
- % Lang, "")}]},
- {xmlelement, "field", [{"type", "text-multi"},
- {"label",
- translate:translate(
- Lang, "Access Rules")},
- {"var", "access"}],
- lists:map(fun(S) ->
- {xmlelement, "value", [], [{xmlcdata, S}]}
- end,
- string:tokens(
- lists:flatten(
- io_lib:format(
- "~p.",
- [ets:select(config,
- [{{config, {access, '$1'}, '$2'},
- [],
- [{{access, '$1', '$2'}}]}])
- ])),
- "\n"))
- }
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Access Configuration")}]},
+ %{xmlelement, "instructions", [],
+ % [{xmlcdata,
+ % translate:translate(
+ % Lang, "")}]},
+ {xmlelement, "field", [{"type", "text-multi"},
+ {"label",
+ translate:translate(
+ Lang, "Access Rules")},
+ {"var", "access"}],
+ lists:map(fun(S) ->
+ {xmlelement, "value", [], [{xmlcdata, S}]}
+ end,
+ string:tokens(
+ lists:flatten(
+ io_lib:format(
+ "~p.",
+ [ets:select(config,
+ [{{config, {access, '$1'}, '$2'},
+ [],
+ [{{access, '$1', '$2'}}]}])
+ ])),
+ "\n"))
+ }
+ ]}]};
get_form(["config", "remusers"], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Remove Users")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Choose users to remove")}]}] ++
- case catch ejabberd_auth:dirty_get_registered_users() of
- {'EXIT', Reason} ->
- [];
- Users ->
- lists:map(fun(U) ->
- ?XFIELD("boolean", U, U, "0")
- end, lists:sort(Users))
- end
- };
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Remove Users")}]},
+ {xmlelement, "instructions", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Choose users to remove")}]}] ++
+ case catch ejabberd_auth:dirty_get_registered_users() of
+ {'EXIT', Reason} ->
+ [];
+ Users ->
+ lists:map(fun(U) ->
+ ?XFIELD("boolean", U, U, "0")
+ end, lists:sort(Users))
+ end
+ }]};
get_form(_, Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
@@ -712,39 +730,45 @@ process_sm_iq(From, To,
Lang = xml:get_tag_attr_s("xml:lang", SubEl),
case Type of
set ->
- case xml:get_tag_attr_s("type", SubEl) of
- "cancel" ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}], []}]};
- "submit" ->
- XData = jlib:parse_xdata_submit(SubEl),
- case XData of
- invalid ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_BAD_REQUEST]};
- _ ->
- Node =
- string:tokens(
- 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} ->
+ XDataEl = find_xdata_el(SubEl),
+ case XDataEl of
+ false ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
+ {xmlelement, _Name, Attrs, SubEls} ->
+ case xml:get_attr_s("type", Attrs) of
+ "cancel" ->
+ IQ#iq{type = result,
+ sub_el = [{xmlelement, "query",
+ [{"xmlns", XMLNS}], []}]};
+ "submit" ->
+ XData = jlib:parse_xdata_submit(XDataEl),
+ case XData of
+ invalid ->
IQ#iq{type = error,
- sub_el = [SubEl, Error]}
- end
- end;
- _ ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
+ sub_el = [SubEl, ?ERR_BAD_REQUEST]};
+ _ ->
+ Node =
+ string:tokens(
+ 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,
+ sub_el = [SubEl, Error]}
+ end
+ end;
+ _ ->
+ IQ#iq{type = error,
+ sub_el = [SubEl, ?ERR_BAD_REQUEST]}
+ end
end;
get ->
Node =
@@ -764,34 +788,35 @@ process_sm_iq(From, To,
get_sm_form(User, [], Lang) ->
- {result, [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Administration of " ++ User)}]},
- %{xmlelement, "instructions", [],
- % [{xmlcdata,
- % translate:translate(
- % Lang, "Choose host name")}]},
- {xmlelement, "field",
- [{"type", "list-single"},
- {"label", translate:translate(Lang, "Action on user")},
- {"var", "action"}],
- [{xmlelement, "value", [], [{xmlcdata, "edit"}]},
- {xmlelement, "option",
- [{"label", translate:translate(Lang, "Edit Properties")}],
- [{xmlelement, "value", [], [{xmlcdata, "edit"}]}]},
- {xmlelement, "option",
- [{"label", translate:translate(Lang, "Remove User")}],
- [{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
- ]},
- ?XFIELD("text-private", "Password", "password",
- ejabberd_auth:get_password_s(User))
- %{xmlelement, "field", [{"type", "text-single"},
- % {"label",
- % translate:translate(Lang, "Host name")},
- % {"var", "hostname"}],
- % [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
- ]};
+ {result, [{xmlelement, "x", [{"xmlns", ?NS_XDATA}],
+ [{xmlelement, "title", [],
+ [{xmlcdata,
+ translate:translate(
+ Lang, "Administration of " ++ User)}]},
+ %{xmlelement, "instructions", [],
+ % [{xmlcdata,
+ % translate:translate(
+ % Lang, "Choose host name")}]},
+ {xmlelement, "field",
+ [{"type", "list-single"},
+ {"label", translate:translate(Lang, "Action on user")},
+ {"var", "action"}],
+ [{xmlelement, "value", [], [{xmlcdata, "edit"}]},
+ {xmlelement, "option",
+ [{"label", translate:translate(Lang, "Edit Properties")}],
+ [{xmlelement, "value", [], [{xmlcdata, "edit"}]}]},
+ {xmlelement, "option",
+ [{"label", translate:translate(Lang, "Remove User")}],
+ [{xmlelement, "value", [], [{xmlcdata, "remove"}]}]}
+ ]},
+ ?XFIELD("text-private", "Password", "password",
+ ejabberd_auth:get_password_s(User))
+ %{xmlelement, "field", [{"type", "text-single"},
+ % {"label",
+ % translate:translate(Lang, "Host name")},
+ % {"var", "hostname"}],
+ % [{xmlelement, "value", [], [{xmlcdata, ?MYNAME}]}]}
+ ]}]};
get_sm_form(_, _, Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
@@ -820,3 +845,20 @@ set_sm_form(User, [], Lang, XData) ->
end;
set_sm_form(_, _, Lang, XData) ->
{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).
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 1c7f8d0b..0dbf759e 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -157,7 +157,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
"query",
[{"xmlns", XMLNS},
{"node", SNode}],
- [feature_to_xml({?NS_IQDATA})]}]};
+ [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "modules"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "modules", _]} ->
@@ -165,7 +165,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS},
{"node", SNode}],
- [feature_to_xml({?NS_IQDATA})]}]};
+ [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "backup"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "backup", _]} ->
@@ -173,7 +173,7 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS},
{"node", SNode}],
- [feature_to_xml({?NS_IQDATA})]}]};
+ [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["running nodes", ENode, "import"]} ->
?EMPTY_INFO_RESULT;
{allow, ["running nodes", ENode, "import", _]} ->
@@ -181,13 +181,13 @@ process_local_iq_info(From, _To, #iq{type = Type, xmlns = XMLNS,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS},
{"node", SNode}],
- [feature_to_xml({?NS_IQDATA})]}]};
+ [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
{allow, ["config", _]} ->
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS},
{"node", SNode}],
- [feature_to_xml({?NS_IQDATA})]}]};
+ [feature_to_xml({?NS_EJABBERD_CONFIG})]}]};
_ ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
end
@@ -489,7 +489,7 @@ process_sm_iq_info(From, To, #iq{type = Type, xmlns = XMLNS,
"" ->
IQ#iq{type = result,
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]}
end
diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl
index 8a5a3567..cd8616b8 100644
--- a/src/mod_irc/mod_irc.erl
+++ b/src/mod_irc/mod_irc.erl
@@ -358,16 +358,16 @@ set_form(From, [], Lang, XData) ->
{atomic, _} ->
{result, []};
_ ->
- {error, "406", "Not Acceptable"}
+ {error, ?ERR_NOT_ACCEPTABLE}
end;
_ ->
- {error, "406", "Not Acceptable"}
+ {error, ?ERR_NOT_ACCEPTABLE}
end;
_ ->
- {error, "406", "Not Acceptable"}
+ {error, ?ERR_NOT_ACCEPTABLE}
end;
_ ->
- {error, "406", "Not Acceptable"}
+ {error, ?ERR_NOT_ACCEPTABLE}
end;
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index f62880c4..7f7b8957 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -63,7 +63,7 @@
config = #config{},
users = ?DICT:new(),
affiliations = ?DICT:new(),
- history = lqueue_new(10),
+ history = lqueue_new(20),
subject = "",
subject_author = ""}).
@@ -831,7 +831,8 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
add_online_user(From, Nick, Role, StateData)),
send_new_presence(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 ->
ok;
_ ->
@@ -879,7 +880,259 @@ extract_password([{xmlelement, Name, Attrs, SubEls} = El | 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) ->
LJID = jlib:jid_tolower(JID),
@@ -1087,22 +1340,28 @@ add_message_to_history(FromNick, Packet, StateData) ->
_ ->
true
end,
+ TimeStamp = calendar:now_to_universal_time(now()),
TSPacket = append_subtags(Packet,
- [jlib:timestamp_to_xml(
- calendar:now_to_universal_time(
- now()))]),
- Q1 = lqueue_in({FromNick, TSPacket, HaveSubject}, StateData#state.history),
+ [jlib:timestamp_to_xml(TimeStamp)]),
+ {xmlelement, Name, Attrs, Els} = TSPacket,
+ SPacket = jlib:replace_from_to(
+ 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}.
-send_history(JID, StateData) ->
+send_history(JID, Shift, StateData) ->
lists:foldl(
- fun({Nick, Packet, HaveSubject}, B) ->
+ fun({Nick, Packet, HaveSubject, _TimeStamp, _Size}, B) ->
ejabberd_router:route(
jlib:jid_replace_resource(StateData#state.jid, Nick),
JID,
Packet),
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) ->
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index ab76021f..629e92e7 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -89,7 +89,7 @@ get_local_stats(["running nodes", _], []) ->
get_local_stats(["running nodes", ENode], Names) ->
case search_running_node(ENode) of
false ->
- {error, "404", "Not Found"};
+ {error, ?ERR_ITEM_NOT_FOUND};
Node ->
{result,
lists:map(fun(Name) -> get_node_stat(Node, Name) end, Names)}