25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-22 16:20:52 +01:00
xmpp.chapril.org-ejabberd/src/mod_bosh.erl

395 lines
19 KiB
Erlang
Raw Normal View History

2016-11-22 13:17:05 +01:00
%%%-------------------------------------------------------------------
%%% File : mod_bosh.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Purpose : This module acts as a bridge to ejabberd_bosh which implements
%%% the real stuff, this is to handle the new pluggable architecture
%%% for extending ejabberd's http service.
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
2016-11-22 13:17:05 +01:00
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-module(mod_bosh).
-author('steve@zeank.in-berlin.de').
%%-define(ejabberd_debug, true).
-behaviour(gen_mod).
-export([start_link/0]).
-export([start/2, stop/1, reload/3, process/2, open_session/2,
close_session/1, find_session/1, clean_cache/1]).
2016-11-22 13:17:05 +01:00
-export([depends/2, mod_opt_type/1]).
2016-11-22 13:17:05 +01:00
-include("ejabberd.hrl").
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
2017-02-18 07:36:27 +01:00
-include("xmpp.hrl").
2016-11-22 13:17:05 +01:00
-include("ejabberd_http.hrl").
-include("bosh.hrl").
-callback init() -> any().
-callback open_session(binary(), pid()) -> ok | {error, any()}.
-callback close_session(binary()) -> ok | {error, any()}.
-callback find_session(binary()) -> {ok, pid()} | {error, any()}.
-callback use_cache() -> boolean().
-callback cache_nodes() -> [node()].
-optional_callbacks([use_cache/0, cache_nodes/0]).
2016-11-22 13:17:05 +01:00
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
process([], #request{method = 'POST', data = <<>>}) ->
?DEBUG("Bad Request: no data", []),
{400, ?HEADER(?CT_XML),
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"400 Bad Request">>}]}};
process([],
#request{method = 'POST', data = Data, ip = IP, headers = Hdrs}) ->
?DEBUG("Incoming data: ~p", [Data]),
Type = get_type(Hdrs),
ejabberd_bosh:process_request(Data, IP, Type);
process([], #request{method = 'GET', data = <<>>}) ->
{200, ?HEADER(?CT_XML), get_human_html_xmlel()};
process([], #request{method = 'OPTIONS', data = <<>>}) ->
{200, ?OPTIONS_HEADER, []};
process(_Path, _Request) ->
?DEBUG("Bad Request: ~p", [_Request]),
{400, ?HEADER(?CT_XML),
#xmlel{name = <<"h1">>, attrs = [],
children = [{xmlcdata, <<"400 Bad Request">>}]}}.
-spec open_session(binary(), pid()) -> ok | {error, any()}.
2016-11-22 13:17:05 +01:00
open_session(SID, Pid) ->
Mod = gen_mod:ram_db_mod(global, ?MODULE),
case Mod:open_session(SID, Pid) of
ok ->
delete_cache(Mod, SID);
{error, _} = Err ->
Err
end.
2016-11-22 13:17:05 +01:00
-spec close_session(binary()) -> ok.
2016-11-22 13:17:05 +01:00
close_session(SID) ->
Mod = gen_mod:ram_db_mod(global, ?MODULE),
Mod:close_session(SID),
delete_cache(Mod, SID).
2016-11-22 13:17:05 +01:00
-spec find_session(binary()) -> {ok, pid()} | error.
2016-11-22 13:17:05 +01:00
find_session(SID) ->
Mod = gen_mod:ram_db_mod(global, ?MODULE),
case use_cache(Mod) of
true ->
ets_cache:lookup(
?BOSH_CACHE, SID,
fun() ->
case Mod:find_session(SID) of
{ok, Pid} -> {ok, Pid};
{error, _} -> error
end
end);
false ->
case Mod:find_session(SID) of
{ok, Pid} -> {ok, Pid};
{error, _} -> error
end
end.
2016-11-22 13:17:05 +01:00
start(Host, Opts) ->
start_jiffy(Opts),
Mod = gen_mod:ram_db_mod(global, ?MODULE),
init_cache(Mod),
Mod:init(),
clean_cache(),
2017-02-14 10:39:26 +01:00
TmpSup = gen_mod:get_module_proc(Host, ?MODULE),
2016-11-22 13:17:05 +01:00
TmpSupSpec = {TmpSup,
{ejabberd_tmp_sup, start_link, [TmpSup, ejabberd_bosh]},
permanent, infinity, supervisor, [ejabberd_tmp_sup]},
2017-02-24 10:05:47 +01:00
supervisor:start_child(ejabberd_gen_mod_sup, TmpSupSpec).
2016-11-22 13:17:05 +01:00
stop(Host) ->
2017-02-14 10:39:26 +01:00
TmpSup = gen_mod:get_module_proc(Host, ?MODULE),
2017-02-24 10:05:47 +01:00
supervisor:terminate_child(ejabberd_gen_mod_sup, TmpSup),
supervisor:delete_child(ejabberd_gen_mod_sup, TmpSup).
2016-11-22 13:17:05 +01:00
reload(_Host, NewOpts, _OldOpts) ->
start_jiffy(NewOpts),
Mod = gen_mod:ram_db_mod(global, ?MODULE),
init_cache(Mod),
Mod:init(),
ok.
2016-11-22 13:17:05 +01:00
%%%===================================================================
%%% Internal functions
%%%===================================================================
start_jiffy(Opts) ->
case gen_mod:get_opt(json, Opts, false) of
2016-11-22 13:17:05 +01:00
false ->
ok;
true ->
case catch ejabberd:start_app(jiffy) of
ok ->
ok;
Err ->
?WARNING_MSG("Failed to start JSON codec (jiffy): ~p. "
"JSON support will be disabled", [Err])
end
end.
get_type(Hdrs) ->
try
{_, S} = lists:keyfind('Content-Type', 1, Hdrs),
[T|_] = str:tokens(S, <<";">>),
[_, <<"json">>] = str:tokens(T, <<"/">>),
json
catch _:_ ->
xml
end.
depends(_Host, _Opts) ->
[].
mod_opt_type(json) ->
fun (false) -> false;
(true) -> true
end;
mod_opt_type(max_concat) ->
fun (unlimited) -> unlimited;
(N) when is_integer(N), N > 0 -> N
end;
mod_opt_type(max_inactivity) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(max_pause) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(prebind) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(ram_db_type) ->
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(queue_type) ->
fun(ram) -> ram; (file) -> file end;
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun(B) when is_boolean(B) -> B end;
mod_opt_type(O) when O == cache_size; O == cache_life_time ->
fun(I) when is_integer(I), I>0 -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
2016-11-22 13:17:05 +01:00
mod_opt_type(_) ->
[json, max_concat, max_inactivity, max_pause, prebind, ram_db_type,
queue_type, use_cache, cache_size, cache_missed, cache_life_time].
%%%----------------------------------------------------------------------
%%% Cache stuff
%%%----------------------------------------------------------------------
-spec init_cache(module()) -> ok.
init_cache(Mod) ->
case use_cache(Mod) of
true ->
ets_cache:new(?BOSH_CACHE, cache_opts());
false ->
ets_cache:delete(?BOSH_CACHE)
end.
-spec use_cache(module()) -> boolean().
use_cache(Mod) ->
case erlang:function_exported(Mod, use_cache, 0) of
true -> Mod:use_cache();
false ->
gen_mod:get_module_opt(
global, ?MODULE, use_cache,
ejabberd_config:use_cache(global))
end.
-spec cache_nodes(module()) -> [node()].
cache_nodes(Mod) ->
case erlang:function_exported(Mod, cache_nodes, 0) of
true -> Mod:cache_nodes();
false -> ejabberd_cluster:get_nodes()
end.
-spec delete_cache(module(), binary()) -> ok.
delete_cache(Mod, SID) ->
case use_cache(Mod) of
true ->
ets_cache:delete(?BOSH_CACHE, SID, cache_nodes(Mod));
false ->
ok
end.
-spec cache_opts() -> [proplists:property()].
cache_opts() ->
MaxSize = gen_mod:get_module_opt(
global, ?MODULE, cache_size,
ejabberd_config:cache_size(global)),
CacheMissed = gen_mod:get_module_opt(
global, ?MODULE, cache_missed,
ejabberd_config:cache_missed(global)),
LifeTime = case gen_mod:get_module_opt(
global, ?MODULE, cache_life_time,
ejabberd_config:cache_life_time(global)) of
infinity -> infinity;
I -> timer:seconds(I)
end,
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
-spec clean_cache(node()) -> ok.
clean_cache(Node) ->
ets_cache:filter(
?BOSH_CACHE,
fun(_, error) ->
false;
(_, {ok, Pid}) ->
node(Pid) /= Node
end).
-spec clean_cache() -> ok.
clean_cache() ->
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
%%%----------------------------------------------------------------------
%%% Help Web Page
%%%----------------------------------------------------------------------
get_human_html_xmlel() ->
Heading = <<"ejabberd ",
(iolist_to_binary(atom_to_list(?MODULE)))/binary>>,
#xmlel{name = <<"html">>,
attrs =
[{<<"xmlns">>, <<"http://www.w3.org/1999/xhtml">>}],
children =
[#xmlel{name = <<"head">>,
children =
[#xmlel{name = <<"title">>,
children = [{xmlcdata, Heading}]},
#xmlel{name = <<"style">>,
children = [{xmlcdata, get_style_cdata()}]}]},
#xmlel{name = <<"body">>,
children =
[#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"container">>}],
children = get_container_children(Heading)}]}]}.
get_container_children(Heading) ->
[#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"section">>}],
children =
[#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"block">>}],
children =
[#xmlel{name = <<"a">>,
attrs = [{<<"href">>, <<"https://www.ejabberd.im">>}],
children =
[#xmlel{name = <<"img">>,
attrs = [{<<"height">>, <<"32">>},
{<<"src">>, get_image_src()}]}]}]}]},
#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"white section">>}],
children =
[#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"block">>}],
children =
[#xmlel{name = <<"h1">>, children = [{xmlcdata, Heading}]},
#xmlel{name = <<"p">>, children =
[{xmlcdata, <<"An implementation of ">>},
#xmlel{name = <<"a">>,
attrs = [{<<"href">>, <<"http://xmpp.org/extensions/xep-0206.html">>}],
children = [{xmlcdata, <<"XMPP over BOSH (XEP-0206)">>}]}]},
#xmlel{name = <<"p">>, children =
[{xmlcdata, <<"This web page is only informative. To "
"use HTTP-Bind you need a Jabber/XMPP "
"client that supports it.">>}]}]}]},
#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"section">>}],
children =
[#xmlel{name = <<"div">>,
attrs = [{<<"class">>, <<"block">>}],
children =
[#xmlel{name = <<"a">>,
attrs = [{<<"href">>, <<"https://www.ejabberd.im">>},
{<<"title">>, <<"ejabberd XMPP server">>}],
children = [{xmlcdata, <<"ejabberd">>}]},
{xmlcdata, <<" is maintained by ">>},
#xmlel{name = <<"a">>,
attrs = [{<<"href">>, <<"https://www.process-one.net">>},
{<<"title">>, <<"ProcessOne - Leader in Instant Messaging and Push Solutions">>}],
children = [{xmlcdata, <<"ProcessOne">>}]} ]}]}
].
get_style_cdata() ->
<<"
body {
margin: 0;
padding: 0;
font-family: sans-serif;
color: #fff;
}
h1 {
font-size: 3em;
color: #444;
}
p {
line-height: 1.5em;
color: #888;
}
a {
color: #fff;
}
a:hover,
a:active {
text-decoration: underline;
}
.container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #424A55;
background-image: -webkit-linear-gradient(270deg, rgba(48,52,62,0) 24%, #30353e 100%);
background-image: linear-gradient(-180deg, rgba(48,52,62,0) 24%, #30353e 100%);
}
.section {
padding: 3em;
}
.white.section {
background: #fff;
border-bottom: 4px solid #41AFCA;
}
.white.section a {
text-decoration: none;
color: #41AFCA;
}
.white.section a:hover,
.white.section a:active {
text-decoration: underline;
}
.block {
margin: 0 auto;
max-width: 900px;
width: 100%;
}">>.
get_image_src() ->
<<"data:image/png;base64,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