From 0b4539caa1271e65e2dbe24d8ae8baba236af3d3 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sat, 22 May 2004 19:48:35 +0000 Subject: [PATCH] * src/web/ejabberd_http.erl: Added options for enabling HTTP polling and admin interface * src/web/ejabberd_web.erl: Likewise * src/ejabberd.cfg.example: Updated * src/web/ejabberd_web_admin.erl: Updated * doc/guide.tex: Updated SVN Revision: 236 --- ChangeLog | 11 +++ doc/guide.html | 86 +++++++++++++++--- doc/guide.tex | 85 +++++++++++++++--- src/ejabberd.cfg.example | 3 +- src/web/ejabberd_http.erl | 34 ++++++-- src/web/ejabberd_web.erl | 10 ++- src/web/ejabberd_web_admin.erl | 153 ++++++++++++++++++++++++++++++--- 7 files changed, 330 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 259d74839..ba3d43ae6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2004-05-22 Alexey Shchepin + + * src/web/ejabberd_http.erl: Added options for enabling HTTP + polling and admin interface + * src/web/ejabberd_web.erl: Likewise + * src/ejabberd.cfg.example: Updated + + * src/web/ejabberd_web_admin.erl: Updated + + * doc/guide.tex: Updated + 2004-05-17 Alexey Shchepin * src/mod_muc/mod_muc.erl: Added access rules for using serveice diff --git a/doc/guide.html b/doc/guide.html index fe22bbd91..f139dac6a 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -455,11 +455,23 @@ The following options are defined:
ejabberd_service
This module serves connections from Jabber services (i. e. that use the jabber:component:accept namespace). -For example, the following configuration defines that C2S connections are -listened on port 5222 and 5223 (SSL) and denied for user ``bad'', S2S -on port 5269, and that service conference.example.org must be -connected to port 8888 with a password ``secret''. Also all users -except admins have traffic limit 1000 b/s. +For example, the following configuration defines that: +
  • +C2S connections are listened on port 5222 and 5223 (SSL) and denied for + user ``bad'' +
  • S2S connections are listened on port 5269 +
  • All users except admins have traffic limit 1000 B/s +
  • AIM service aim.example.org is connected to port 5233 with + password ``aimsecret'' +
  • JIT services icq.example.org and sms.example.org are + connected to port 5234 with password ``jitsecret'' +
  • MSN service msn.example.org is connected to port 5235 with + password ``msnsecret'' +
  • YAHOO service yahoo.example.org is connected to port 5236 with + password ``yahoosecret'' +
  • ILE service ile.example.org is connected to port 5237 with + password ``ilesecret'' +
 {acl, blocked, {user, "bad"}}.
 {access, c2s, [{deny, blocked},
@@ -467,14 +479,62 @@ except admins have traffic limit 1000 b/s.
 {shaper, normal, {maxrate, 1000}}.
 {access, c2s_shaper, [{none, admin},
                       {normal, all}]}.
-{listen, [{5222, ejabberd_c2s,     [{access, c2s},
-                                    {shaper, c2s_shaper}]},
-          {5223, ejabberd_c2s,     [{access, c2s},
-                                    {ssl, [{certfile, "/path/to/ssl.pem"}]}]},
-          {5269, ejabberd_s2s_in,  []},
-          {8888, ejabberd_service,
-           [{hosts, ["conference.example.org"], [{password, "secret"}]}]}
-         ]}.
+{listen,
+ [{5222, ejabberd_c2s,     [{access, c2s}, {shaper, c2s_shaper}]},
+  {5223, ejabberd_c2s,     [{access, c2s},
+                            ssl, {certfile, "/path/to/ssl.pem"}]},
+  {5269, ejabberd_s2s_in,  []},
+  {5233, ejabberd_service, [{host, "aim.example.org",
+                             [{password, "aimsecret"}]}]},
+  {5234, ejabberd_service, [{hosts, ["icq.example.org", "sms.example.org"],
+                             [{password, "jitsecret"}]}]},
+  {5235, ejabberd_service, [{host, "msn.example.org",
+                             [{password, "msnsecret"}]}]},
+  {5236, ejabberd_service, [{host, "yahoo.example.org",
+                             [{password, "yahoosecret"}]}]},
+  {5237, ejabberd_service, [{host, "gg.example.org",
+                             [{password, "ggsecret"}]}]},
+  {5238, ejabberd_service, [{host, "ile.example.org",
+                             [{password, "ilesecret"}]}]}
+ ]
+}.
+
Note, that for jabberd14- or wpjabberd-based services you need to make the +transports log and do xdb by themselves: +
+  <!--
+     You need to add elogger and rlogger entries here when using ejabberd.
+     In this case the transport will do the logging.
+  -->
+
+  <log id='elogger'>
+    <host/>
+    <logtype/>
+    <format>%d: [%t] (%h): %s</format>
+    <file>/var/log/jabber/error/aim-t.log</file>
+  </log>
+
+  <log id='rlogger'>
+    <host/>
+    <logtype>record</logtype>
+    <format>%d %h %s</format>
+    <file>/var/log/jabber/record/aim-t.log</file>
+  </log>
+
+  <!--
+     Some Jabber server implementations do not provide
+     XDB services (for example jabberd 2.0 and ejabberd).
+     AIM-t is loaded into handle all XDB requests.
+  -->
+
+  <xdb id="xdb">
+    <host/>
+    <load>
+      <xdb_file>/usr/lib/jabber/xdb_file.so</xdb_file> <!-- this is a lib of wpjabber or jabberd -->
+      </load>
+    <xdb_file xmlns="jabber:config:xdb_file">
+      <spool><jabberd:cmdline flag='s'>/var/spool/jabber</jabberd:cmdline></spool>
+    </xdb_file>
+  </xdb>
 
diff --git a/doc/guide.tex b/doc/guide.tex index 7e980b3d9..1d9bbe9e0 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -437,11 +437,23 @@ Currently three modules are implemented: services (i.\,e.\ that use the \texttt{jabber:component:accept} namespace). \end{description} -For example, the following configuration defines that C2S connections are -listened on port 5222 and 5223 (SSL) and denied for user ``\texttt{bad}'', S2S -on port 5269, and that service \texttt{conference.example.org} must be -connected to port 8888 with a password ``\texttt{secret}''. Also all users -except admins have traffic limit 1000\,b/s. +For example, the following configuration defines that: +\begin{itemize} +\item C2S connections are listened on port 5222 and 5223 (SSL) and denied for + user ``\texttt{bad}'' +\item S2S connections are listened on port 5269 +\item All users except admins have traffic limit 1000\,B/s +\item AIM service \texttt{aim.example.org} is connected to port 5233 with + password ``\texttt{aimsecret}'' +\item JIT services \texttt{icq.example.org} and \texttt{sms.example.org} are + connected to port 5234 with password ``\texttt{jitsecret}'' +\item MSN service \texttt{msn.example.org} is connected to port 5235 with + password ``\texttt{msnsecret}'' +\item YAHOO service \texttt{yahoo.example.org} is connected to port 5236 with + password ``\texttt{yahoosecret}'' +\item ILE service \texttt{ile.example.org} is connected to port 5237 with + password ``\texttt{ilesecret}'' +\end{itemize} \begin{verbatim} {acl, blocked, {user, "bad"}}. {access, c2s, [{deny, blocked}, @@ -449,17 +461,64 @@ except admins have traffic limit 1000\,b/s. {shaper, normal, {maxrate, 1000}}. {access, c2s_shaper, [{none, admin}, {normal, all}]}. -{listen, [{5222, ejabberd_c2s, [{access, c2s}, - {shaper, c2s_shaper}]}, - {5223, ejabberd_c2s, [{access, c2s}, - ssl, {certfile, "/path/to/ssl.pem"}]}, - {5269, ejabberd_s2s_in, []}, - {8888, ejabberd_service, - [{hosts, ["conference.example.org"], [{password, "secret"}]}]} - ]}. +{listen, + [{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]}, + {5223, ejabberd_c2s, [{access, c2s}, + ssl, {certfile, "/path/to/ssl.pem"}]}, + {5269, ejabberd_s2s_in, []}, + {5233, ejabberd_service, [{host, "aim.example.org", + [{password, "aimsecret"}]}]}, + {5234, ejabberd_service, [{hosts, ["icq.example.org", "sms.example.org"], + [{password, "jitsecret"}]}]}, + {5235, ejabberd_service, [{host, "msn.example.org", + [{password, "msnsecret"}]}]}, + {5236, ejabberd_service, [{host, "yahoo.example.org", + [{password, "yahoosecret"}]}]}, + {5237, ejabberd_service, [{host, "gg.example.org", + [{password, "ggsecret"}]}]}, + {5238, ejabberd_service, [{host, "ile.example.org", + [{password, "ilesecret"}]}]} + ] +}. \end{verbatim} +Note, that for jabberd14- or wpjabberd-based services you need to make the +transports log and do xdb by themselves: +\begin{verbatim} + + + + + %d: [%t] (%h): %s + /var/log/jabber/error/aim-t.log + + + + record + %d %h %s + /var/log/jabber/record/aim-t.log + + + + + + + + /usr/lib/jabber/xdb_file.so + + + /var/spool/jabber + + +\end{verbatim} \subsubsection{Modules} diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example index cae56ec53..2285ef84a 100644 --- a/src/ejabberd.cfg.example +++ b/src/ejabberd.cfg.example @@ -91,8 +91,9 @@ [{5222, ejabberd_c2s, [{access, c2s}, {shaper, c2s_shaper}]}, {5223, ejabberd_c2s, [{access, c2s}, ssl, {certfile, "./ssl.pem"}]}, {5269, ejabberd_s2s_in, [{shaper, s2s_shaper}]}, + {5280, ejabberd_http, [http_poll, web_admin]}, {8888, ejabberd_service, [{access, all}, - {hosts, ["conference.e.localhost", "muc.e.localhost"], + {hosts, ["icq.localhost", "sms.localhost"], [{password, "secret"}]}]} ]}. diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index 6ef8a4032..0723fd572 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -25,7 +25,9 @@ request_path, request_auth, request_content_length, - request_lang = "en" + request_lang = "en", + use_http_poll = false, + use_web_admin = false }). @@ -49,9 +51,15 @@ start_link({SockMod, Socket}, Opts) -> ssl -> ssl:setopts(Socket, [{packet, http}, {recbuf, 8192}]) end, + UseHTTPPoll = lists:member(http_poll, Opts), + UseWebAdmin = lists:member(web_admin, Opts), + io:format("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]), {ok, proc_lib:spawn_link(ejabberd_http, receive_headers, - [#state{sockmod = SockMod, socket = Socket}])}. + [#state{sockmod = SockMod, + socket = Socket, + use_http_poll = UseHTTPPoll, + use_web_admin = UseWebAdmin}])}. send_text(State, Text) -> @@ -93,7 +101,10 @@ receive_headers(State) -> ssl -> ssl:setopts(Socket, [{packet, http}]) end, - receive_headers(#state{sockmod = SockMod, socket = Socket}); + receive_headers(#state{sockmod = SockMod, + socket = Socket, + use_http_poll = State#state.use_http_poll, + use_web_admin = State#state.use_web_admin}); {error, _Reason} -> ok; _ -> @@ -105,7 +116,9 @@ receive_headers(State) -> process_request(#state{request_method = 'GET', request_path = {abs_path, Path}, request_auth = Auth, - request_lang = Lang}) -> + request_lang = Lang, + use_http_poll = UseHTTPPoll, + use_web_admin = UseWebAdmin}) -> User = case Auth of {U, P} -> case ejabberd_auth:check_password(U, P) of @@ -136,7 +149,10 @@ process_request(#state{request_method = 'GET', q = LQuery, user = User, lang = Lang}, - case ejabberd_web:process_get(Request) of + io:format("~p~n", [{{UseHTTPPoll, UseWebAdmin}, + Request}]), + case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + Request) of El when element(1, El) == xmlelement -> make_xhtml_output(200, [], El); {Status, Headers, El} when @@ -157,7 +173,10 @@ process_request(#state{request_method = 'POST', request_content_length = Len, request_lang = Lang, sockmod = SockMod, - socket = Socket} = State) when is_integer(Len) -> + socket = Socket, + use_http_poll = UseHTTPPoll, + use_web_admin = UseWebAdmin} = State) + when is_integer(Len) -> User = case Auth of {U, P} -> case ejabberd_auth:check_password(U, P) of @@ -197,7 +216,8 @@ process_request(#state{request_method = 'POST', user = User, data = Data, lang = Lang}, - case ejabberd_web:process_get(Request) of + case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, + Request) of El when element(1, El) == xmlelement -> make_xhtml_output(200, [], El); {Status, Headers, El} when diff --git a/src/web/ejabberd_web.erl b/src/web/ejabberd_web.erl index b59641c44..4ef511e52 100644 --- a/src/web/ejabberd_web.erl +++ b/src/web/ejabberd_web.erl @@ -12,7 +12,7 @@ %% External exports -export([make_xhtml/1, - process_get/1]). + process_get/2]). -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -49,7 +49,8 @@ make_xhtml(Els) -> {"value", Value}])). -process_get(#request{user = User, +process_get({_, true}, + #request{user = User, path = ["admin" | RPath], q = Query, lang = Lang} = Request) -> @@ -69,13 +70,14 @@ process_get(#request{user = User, [{xmlcdata, "401 Unauthorized"}]}])} end; -process_get(#request{user = User, +process_get({true, _}, + #request{user = User, path = ["http-poll" | RPath], q = Query, lang = Lang} = Request) -> ejabberd_http_poll:process_request(Request#request{path = RPath}); -process_get(_Request) -> +process_get(_, _Request) -> {404, [], make_xhtml([?XC("h1", "Not found")])}. diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl index 46801bc1f..c021fde2b 100644 --- a/src/web/ejabberd_web_admin.erl +++ b/src/web/ejabberd_web_admin.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_web_admin.erl %%% Author : Alexey Shchepin -%%% Purpose : +%%% Purpose : Administration web interface %%% Created : 9 Apr 2004 by Alexey Shchepin %%% Id : $Id$ %%%---------------------------------------------------------------------- @@ -543,7 +543,7 @@ process_admin(#request{user = User, ACLs = lists:flatten(io_lib:format("~p.", [ets:tab2list(acl)])), make_xhtml([?XCT("h1", "ejabberd ACLs configuration")] ++ case Res of - ok -> [?CT("submited"), ?P]; + ok -> [?CT("submitted"), ?P]; error -> [?CT("bad format"), ?P]; nothing -> [] end ++ @@ -584,7 +584,7 @@ process_admin(#request{method = Method, ACLs = lists:keysort(2, ets:tab2list(acl)), make_xhtml([?XCT("h1", "ejabberd ACLs configuration")] ++ case Res of - ok -> [?CT("submited"), ?P]; + ok -> [?CT("submitted"), ?P]; error -> [?CT("bad format"), ?P]; nothing -> [] end ++ @@ -651,7 +651,7 @@ process_admin(#request{user = User, [{{access, '$1', '$2'}}]}])])), make_xhtml([?XC("h1", "ejabberd access rules configuration")] ++ case Res of - ok -> [?C("submited"), ?P]; + ok -> [?C("submitted"), ?P]; error -> [?C("bad format"), ?P]; nothing -> [] end ++ @@ -689,7 +689,7 @@ process_admin(#request{method = Method, [{{access, '$1', '$2'}}]}]), make_xhtml([?XC("h1", "ejabberd access rules configuration")] ++ case Res of - ok -> [?C("submited"), ?P]; + ok -> [?C("submitted"), ?P]; error -> [?C("bad format"), ?P]; nothing -> [] end ++ @@ -730,7 +730,7 @@ process_admin(#request{method = Method, make_xhtml([?XC("h1", "'" ++ SName ++ "' access rule configuration")] ++ case Res of - ok -> [?C("submited"), ?P]; + ok -> [?C("submitted"), ?P]; error -> [?C("bad format"), ?P]; nothing -> [] end ++ @@ -1103,7 +1103,7 @@ user_info(User, Query, Lang) -> ?INPUTT("submit", "chpassword", "Change Password")], [?XC("h1", "User: " ++ User)] ++ case Res of - ok -> [?C("submited"), ?P]; + ok -> [?C("submitted"), ?P]; error -> [?C("bad format"), ?P]; nothing -> [] end ++ @@ -1174,12 +1174,23 @@ search_running_node(SNode, [Node | Nodes]) -> end. get_node(Node, [], Query, Lang) -> - [?XC("h1", "Node: " ++ atom_to_list(Node)), - ?XE("ul", - [?LI([?ACT("db/", "DB Management")]), - ?LI([?ACT("backup/", "Backup Management")]), - ?LI([?ACT("statistics/", "Statistics")]) - ])]; + Res = node_parse_query(Node, Query), + [?XC("h1", "Node: " ++ atom_to_list(Node))] ++ + case Res of + ok -> [?C("submitted"), ?P]; + error -> [?C("bad format"), ?P]; + nothing -> [] + end ++ + [?XE("ul", + [?LI([?ACT("db/", "DB Management")]), + ?LI([?ACT("backup/", "Backup Management")]), + ?LI([?ACT("stats/", "Statistics")]) + ]), + ?XAE("form", [{"method", "post"}], + [?INPUTT("submit", "restart", "Restart"), + ?C(" "), + ?INPUTT("submit", "stop", "Stop")]) + ]; get_node(Node, ["db"], Query, Lang) -> case rpc:call(Node, mnesia, system_info, [tables]) of @@ -1224,7 +1235,7 @@ get_node(Node, ["db"], Query, Lang) -> end, STables), [?XC("h1", "DB Tables at " ++ atom_to_list(Node))] ++ case Res of - ok -> [?C("submited"), ?P]; + ok -> [?C("submitted"), ?P]; error -> [?C("bad format"), ?P]; nothing -> [] end ++ @@ -1249,6 +1260,7 @@ get_node(Node, ["db"], Query, Lang) -> end; get_node(Node, ["backup"], Query, Lang) -> + Res = node_backup_parse_query(Node, Query), [?XC("h1", "Backup Management at " ++ atom_to_list(Node)), ?XAE("form", [{"method", "post"}], [?XAE("table", [], @@ -1292,10 +1304,77 @@ get_node(Node, ["backup"], Query, Lang) -> ]) ])])]; +get_node(Node, ["stats"], Query, Lang) -> + UpTime = rpc:call(Node, erlang, statistics, [wall_clock]), + UpTimeS = io_lib:format("~.3f", [element(1, UpTime)/1000]), + CPUTime = rpc:call(Node, erlang, statistics, [runtime]), + CPUTimeS = io_lib:format("~.3f", [element(1, CPUTime)/1000]), + Users = length( + rpc:call(Node, ejabberd_sm, dirty_get_my_sessions_list, [])), + TransactionsCommited = + rpc:call(Node, mnesia, system_info, [transaction_commits]), + TransactionsAborted = + rpc:call(Node, mnesia, system_info, [transaction_failures]), + TransactionsRestarted = + rpc:call(Node, mnesia, system_info, [transaction_restarts]), + TransactionsLogged = + rpc:call(Node, mnesia, system_info, [transaction_log_writes]), + + [?XC("h1", atom_to_list(Node) ++ " statistics"), + ?XAE("table", [], + [?XE("tbody", + [?XE("tr", [?XCT("td", "Uptime"), + ?XAC("td", [{"class", "alignright"}], + UpTimeS)]), + ?XE("tr", [?XCT("td", "CPU Time"), + ?XAC("td", [{"class", "alignright"}], + CPUTimeS)]), + ?XE("tr", [?XCT("td", "Authentificated users"), + ?XAC("td", [{"class", "alignright"}], + integer_to_list(Users))]), + ?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))]) + ]) + ])]; + get_node(Node, NPath, Query, Lang) -> [?XC("h1", "Not found")]. +node_parse_query(Node, Query) -> + case lists:keysearch("restart", 1, Query) of + {value, _} -> + case rpc:call(Node, init, restart, []) of + {badrpc, _Reason} -> + error; + _ -> + ok + end; + _ -> + case lists:keysearch("delete", 1, Query) of + {value, _} -> + case rpc:call(Node, init, restart, []) of + {badrpc, _Reason} -> + error; + _ -> + ok + end; + _ -> + nothing + end + end. + + db_storage_select(ID, Opt, Lang) -> ?XAE("select", [{"name", "table" ++ ID}], lists:map( @@ -1345,3 +1424,49 @@ node_db_parse_query(Node, Tables, Query) -> end, Tables), ok. +node_backup_parse_query(Node, Query) -> + lists:foldl( + fun(Action, nothing) -> + case lists:keysearch(Action, 1, Query) of + {value, _} -> + case lists:keysearch(Action ++ "path", 1, Query) of + {value, {_, Path}} -> + Res = + case Action of + "store" -> + rpc:call(Node, mnesia, + backup, [Path]); + "restore" -> + rpc:call(Node, mnesia, + restore, + [Path, [{default_op, + keep_tables}]]); + "fallback" -> + rpc:call(Node, mnesia, + install_fallback, [Path]); + "dump" -> + rpc:call(Node, mnesia, + dump_to_textfile, [Path]); + "load" -> + rpc:call(Node, mnesia, + load_textfile, [Path]) + end, + case Res of + {error, _Reason} -> + error; + {badrpc, _Reason} -> + error; + _ -> + ok + end; + _ -> + error + end; + _ -> + nothing + end; + (_Action, Res) -> + Res + end, nothing, ["store", "restore", "fallback", "dump", "load"]). + +