Merge branch 'master' of github.com:processone/ejabberd

Conflicts:
	test/ejabberd_SUITE.erl
This commit is contained in:
Evgeniy Khramtsov 2016-11-18 13:39:10 +03:00
commit f57f267c54
24 changed files with 146 additions and 743 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@
/doc/version.tex
/ebin/
/ejabberd.init
/ejabberd.service
/ejabberdctl.example
XmppAddr.hrl
/rel/ejabberd/

View File

@ -1,301 +0,0 @@
-- LDAPv3 ASN.1 specification, taken from RFC 2251
-- Lightweight-Directory-Access-Protocol-V3 DEFINITIONS
ELDAPv3 DEFINITIONS
IMPLICIT TAGS ::=
BEGIN
LDAPMessage ::= SEQUENCE {
messageID MessageID,
protocolOp CHOICE {
bindRequest BindRequest,
bindResponse BindResponse,
unbindRequest UnbindRequest,
searchRequest SearchRequest,
searchResEntry SearchResultEntry,
searchResDone SearchResultDone,
searchResRef SearchResultReference,
modifyRequest ModifyRequest,
modifyResponse ModifyResponse,
addRequest AddRequest,
addResponse AddResponse,
delRequest DelRequest,
delResponse DelResponse,
modDNRequest ModifyDNRequest,
modDNResponse ModifyDNResponse,
compareRequest CompareRequest,
compareResponse CompareResponse,
abandonRequest AbandonRequest,
extendedReq ExtendedRequest,
extendedResp ExtendedResponse },
controls [0] Controls OPTIONAL }
MessageID ::= INTEGER (0 .. maxInt)
maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
LDAPString ::= OCTET STRING
LDAPOID ::= OCTET STRING
LDAPDN ::= LDAPString
RelativeLDAPDN ::= LDAPString
AttributeType ::= LDAPString
AttributeDescription ::= LDAPString
-- Wahl, et. al. Standards Track [Page 44]
--
-- RFC 2251 LDAPv3 December 1997
AttributeDescriptionList ::= SEQUENCE OF
AttributeDescription
AttributeValue ::= OCTET STRING
AttributeValueAssertion ::= SEQUENCE {
attributeDesc AttributeDescription,
assertionValue AssertionValue }
AssertionValue ::= OCTET STRING
Attribute ::= SEQUENCE {
type AttributeDescription,
vals SET OF AttributeValue }
MatchingRuleId ::= LDAPString
LDAPResult ::= SEQUENCE {
resultCode ENUMERATED {
success (0),
operationsError (1),
protocolError (2),
timeLimitExceeded (3),
sizeLimitExceeded (4),
compareFalse (5),
compareTrue (6),
authMethodNotSupported (7),
strongAuthRequired (8),
-- 9 reserved --
referral (10), -- new
adminLimitExceeded (11), -- new
unavailableCriticalExtension (12), -- new
confidentialityRequired (13), -- new
saslBindInProgress (14), -- new
noSuchAttribute (16),
undefinedAttributeType (17),
inappropriateMatching (18),
constraintViolation (19),
attributeOrValueExists (20),
invalidAttributeSyntax (21),
-- 22-31 unused --
noSuchObject (32),
aliasProblem (33),
invalidDNSyntax (34),
-- 35 reserved for undefined isLeaf --
aliasDereferencingProblem (36),
-- 37-47 unused --
inappropriateAuthentication (48),
-- Wahl, et. al. Standards Track [Page 45]
--
-- RFC 2251 LDAPv3 December 1997
invalidCredentials (49),
insufficientAccessRights (50),
busy (51),
unavailable (52),
unwillingToPerform (53),
loopDetect (54),
-- 55-63 unused --
namingViolation (64),
objectClassViolation (65),
notAllowedOnNonLeaf (66),
notAllowedOnRDN (67),
entryAlreadyExists (68),
objectClassModsProhibited (69),
-- 70 reserved for CLDAP --
affectsMultipleDSAs (71), -- new
-- 72-79 unused --
other (80) },
-- 81-90 reserved for APIs --
matchedDN LDAPDN,
errorMessage LDAPString,
referral [3] Referral OPTIONAL }
Referral ::= SEQUENCE OF LDAPURL
LDAPURL ::= LDAPString -- limited to characters permitted in URLs
Controls ::= SEQUENCE OF Control
Control ::= SEQUENCE {
controlType LDAPOID,
criticality BOOLEAN DEFAULT FALSE,
controlValue OCTET STRING OPTIONAL }
BindRequest ::= [APPLICATION 0] SEQUENCE {
version INTEGER (1 .. 127),
name LDAPDN,
authentication AuthenticationChoice }
AuthenticationChoice ::= CHOICE {
simple [0] OCTET STRING,
-- 1 and 2 reserved
sasl [3] SaslCredentials }
SaslCredentials ::= SEQUENCE {
mechanism LDAPString,
credentials OCTET STRING OPTIONAL }
BindResponse ::= [APPLICATION 1] SEQUENCE {
-- Wahl, et. al. Standards Track [Page 46]
--
-- RFC 2251 LDAPv3 December 1997
COMPONENTS OF LDAPResult,
serverSaslCreds [7] OCTET STRING OPTIONAL }
UnbindRequest ::= [APPLICATION 2] NULL
SearchRequest ::= [APPLICATION 3] SEQUENCE {
baseObject LDAPDN,
scope ENUMERATED {
baseObject (0),
singleLevel (1),
wholeSubtree (2) },
derefAliases ENUMERATED {
neverDerefAliases (0),
derefInSearching (1),
derefFindingBaseObj (2),
derefAlways (3) },
sizeLimit INTEGER (0 .. maxInt),
timeLimit INTEGER (0 .. maxInt),
typesOnly BOOLEAN,
filter Filter,
attributes AttributeDescriptionList }
Filter ::= CHOICE {
and [0] SET OF Filter,
or [1] SET OF Filter,
not [2] Filter,
equalityMatch [3] AttributeValueAssertion,
substrings [4] SubstringFilter,
greaterOrEqual [5] AttributeValueAssertion,
lessOrEqual [6] AttributeValueAssertion,
present [7] AttributeDescription,
approxMatch [8] AttributeValueAssertion,
extensibleMatch [9] MatchingRuleAssertion }
SubstringFilter ::= SEQUENCE {
type AttributeDescription,
-- at least one must be present
substrings SEQUENCE OF CHOICE {
initial [0] LDAPString,
any [1] LDAPString,
final [2] LDAPString } }
MatchingRuleAssertion ::= SEQUENCE {
matchingRule [1] MatchingRuleId OPTIONAL,
type [2] AttributeDescription OPTIONAL,
matchValue [3] AssertionValue,
dnAttributes [4] BOOLEAN DEFAULT FALSE }
-- Wahl, et. al. Standards Track [Page 47]
--
-- RFC 2251 LDAPv3 December 1997
SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
objectName LDAPDN,
attributes PartialAttributeList }
PartialAttributeList ::= SEQUENCE OF SEQUENCE {
type AttributeDescription,
vals SET OF AttributeValue }
SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
SearchResultDone ::= [APPLICATION 5] LDAPResult
ModifyRequest ::= [APPLICATION 6] SEQUENCE {
object LDAPDN,
modification SEQUENCE OF SEQUENCE {
operation ENUMERATED {
add (0),
delete (1),
replace (2) },
modification AttributeTypeAndValues } }
AttributeTypeAndValues ::= SEQUENCE {
type AttributeDescription,
vals SET OF AttributeValue }
ModifyResponse ::= [APPLICATION 7] LDAPResult
AddRequest ::= [APPLICATION 8] SEQUENCE {
entry LDAPDN,
attributes AttributeList }
AttributeList ::= SEQUENCE OF SEQUENCE {
type AttributeDescription,
vals SET OF AttributeValue }
AddResponse ::= [APPLICATION 9] LDAPResult
DelRequest ::= [APPLICATION 10] LDAPDN
DelResponse ::= [APPLICATION 11] LDAPResult
ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
entry LDAPDN,
newrdn RelativeLDAPDN,
deleteoldrdn BOOLEAN,
newSuperior [0] LDAPDN OPTIONAL }
ModifyDNResponse ::= [APPLICATION 13] LDAPResult
-- Wahl, et. al. Standards Track [Page 48]
--
-- RFC 2251 LDAPv3 December 1997
CompareRequest ::= [APPLICATION 14] SEQUENCE {
entry LDAPDN,
ava AttributeValueAssertion }
CompareResponse ::= [APPLICATION 15] LDAPResult
AbandonRequest ::= [APPLICATION 16] MessageID
ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
requestName [0] LDAPOID,
requestValue [1] OCTET STRING OPTIONAL }
ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
COMPONENTS OF LDAPResult,
responseName [10] LDAPOID OPTIONAL,
response [11] OCTET STRING OPTIONAL }
passwdModifyOID LDAPOID ::= "1.3.6.1.4.1.4203.1.11.1"
PasswdModifyRequestValue ::= SEQUENCE {
userIdentity [0] OCTET STRING OPTIONAL,
oldPasswd [1] OCTET STRING OPTIONAL,
newPasswd [2] OCTET STRING OPTIONAL }
PasswdModifyResponseValue ::= SEQUENCE {
genPasswd [0] OCTET STRING OPTIONAL }
END

View File

@ -46,12 +46,13 @@
%% to command, so that the command can perform additional check.
-record(ejabberd_commands,
{name :: atom(),
{name :: atom(),
tags = [] :: [atom()] | '_' | '$2',
desc = "" :: string() | '_' | '$3',
longdesc = "" :: string() | '_',
version = 0 :: integer(),
module :: atom() | '_',
version = 0 :: integer(),
weight = 1 :: integer(),
module :: atom() | '_',
function :: atom() | '_',
args = [] :: [aterm()] | '_' | '$1' | '$2',
policy = restricted :: open | restricted | admin | user,

View File

@ -8,11 +8,11 @@
%%%-------------------------------------------------------------------
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.5"}}},
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.6"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.4"}}},
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.7"}}},
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.6"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.15"}}},
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.16"}}},
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.7"}}},
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.8"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.6"}}},

View File

@ -225,7 +225,6 @@ start_apps() ->
ejabberd:start_app(fast_tls),
ejabberd:start_app(fast_xml),
ejabberd:start_app(stringprep),
http_p1:start(),
ejabberd:start_app(cache_tab).
opt_type(net_ticktime) ->

View File

@ -471,7 +471,7 @@ process(_Handlers,
[{<<"href">>, <<"https://www.ejabberd.im">>},
{<<"title">>, <<"ejabberd XMPP server">>}],
<<"ejabberd">>),
?C(" is maintained by "),
?C(<<" is maintained by ">>),
?XAC(<<"a">>,
[{<<"href">>, <<"https://www.process-one.net">>},
{<<"title">>, <<"ProcessOne - Leader in Instant Messaging and Push Solutions">>}],

View File

@ -45,7 +45,7 @@
start() ->
[code:add_patha(module_ebin_dir(Module))
|| {Module, _} <- installed()],
application:start(inets),
p1_http:start(),
ejabberd_commands:register_commands(get_commands_spec()).
stop() ->
@ -271,10 +271,10 @@ geturl(Url, Hdrs, UsrOpts) ->
[U, Pass] -> [{proxy_user, U}, {proxy_password, Pass}];
_ -> []
end,
case httpc:request(get, {Url, Hdrs}, Host++User++UsrOpts++[{version, "HTTP/1.0"}], []) of
{ok, {{_, 200, _}, Headers, Response}} ->
case p1_http:request(get, Url, Hdrs, [], Host++User++UsrOpts++[{version, "HTTP/1.0"}]) of
{ok, 200, Headers, Response} ->
{ok, Headers, Response};
{ok, {{_, Code, _}, _Headers, Response}} ->
{ok, Code, _Headers, Response} ->
{error, {Code, Response}};
{error, Reason} ->
{error, Reason}

View File

@ -1,358 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : http_p1.erl
%%% Author : Emilio Bustos <ebustos@process-one.net>
%%% Purpose : Provide a common API for inets / lhttpc / ibrowse
%%% Created : 29 Jul 2010 by Emilio Bustos <ebustos@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% 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(http_p1).
-author('ebustos@process-one.net').
-export([start/0, stop/0, get/1, get/2, post/2, post/3,
request/3, request/4, request/5,
get_pool_size/0, set_pool_size/1]).
-include("logger.hrl").
-define(USE_INETS, 1).
% -define(USE_LHTTPC, 1).
% -define(USE_IBROWSE, 1).
% inets used as default if none specified
-ifdef(USE_IBROWSE).
start() ->
ejabberd:start_app(ibrowse).
stop() ->
application:stop(ibrowse).
request(Method, URL, Hdrs, Body, Opts) ->
TimeOut = proplists:get_value(timeout, Opts, infinity),
Options = [{inactivity_timeout, TimeOut}
| proplists:delete(timeout, Opts)],
case ibrowse:send_req(URL, Hdrs, Method, Body, Options)
of
{ok, Status, Headers, Response} ->
{ok, jlib:binary_to_integer(Status), Headers,
Response};
{error, Reason} -> {error, Reason}
end.
get_pool_size() ->
application:get_env(ibrowse, default_max_sessions, 10).
set_pool_size(Size) ->
application:set_env(ibrowse, default_max_sessions, Size).
-else.
-ifdef(USE_LHTTPC).
start() ->
ejabberd:start_app(lhttpc).
stop() ->
application:stop(lhttpc).
request(Method, URL, Hdrs, Body, Opts) ->
{[TO, SO], Rest} = proplists:split(Opts, [timeout, socket_options]),
TimeOut = proplists:get_value(timeout, TO, infinity),
SockOpt = proplists:get_value(socket_options, SO, []),
Options = [{connect_options, SockOpt} | Rest],
Result = lhttpc:request(URL, Method, Hdrs, Body, TimeOut, Options),
?DEBUG("HTTP request -> response:~n"
"** Method = ~p~n"
"** URI = ~s~n"
"** Body = ~s~n"
"** Hdrs = ~p~n"
"** Timeout = ~p~n"
"** Options = ~p~n"
"** Response = ~p",
[Method, URL, Body, Hdrs, TimeOut, Options, Result]),
case Result of
{ok, {{Status, _Reason}, Headers, Response}} ->
{ok, Status, Headers, (Response)};
{error, Reason} -> {error, Reason}
end.
get_pool_size() ->
Opts = proplists:get_value(lhttpc_manager, lhttpc_manager:list_pools()),
proplists:get_value(max_pool_size,Opts).
set_pool_size(Size) ->
lhttpc_manager:set_max_pool_size(lhttpc_manager, Size).
-else.
start() ->
ejabberd:start_app(inets).
stop() ->
application:stop(inets).
to_list(Str) when is_binary(Str) ->
binary_to_list(Str);
to_list(Str) ->
Str.
request(Method, URLRaw, HdrsRaw, Body, Opts) ->
Hdrs = lists:map(fun({N, V}) ->
{to_list(N), to_list(V)}
end, HdrsRaw),
URL = to_list(URLRaw),
Request = case Method of
get -> {URL, Hdrs};
head -> {URL, Hdrs};
delete -> {URL, Hdrs};
_ -> % post, etc.
{URL, Hdrs,
to_list(proplists:get_value(<<"content-type">>, HdrsRaw, [])),
Body}
end,
Options = case proplists:get_value(timeout, Opts,
infinity)
of
infinity -> proplists:delete(timeout, Opts);
_ -> Opts
end,
case httpc:request(Method, Request, Options, []) of
{ok, {{_, Status, _}, Headers, Response}} ->
{ok, Status, Headers, Response};
{error, Reason} -> {error, Reason}
end.
get_pool_size() ->
{ok, Size} = httpc:get_option(max_sessions),
Size.
set_pool_size(Size) ->
httpc:set_option(max_sessions, Size).
-endif.
-endif.
-type({header,
{type, 63, tuple,
[{type, 63, union,
[{type, 63, string, []}, {type, 63, atom, []}]},
{type, 63, string, []}]},
[]}).
-type({headers,
{type, 64, list, [{type, 64, header, []}]}, []}).
-type({option,
{type, 67, union,
[{type, 67, tuple,
[{atom, 67, connect_timeout}, {type, 67, timeout, []}]},
{type, 68, tuple,
[{atom, 68, timeout}, {type, 68, timeout, []}]},
{type, 70, tuple,
[{atom, 70, send_retry},
{type, 70, non_neg_integer, []}]},
{type, 71, tuple,
[{atom, 71, partial_upload},
{type, 71, union,
[{type, 71, non_neg_integer, []},
{atom, 71, infinity}]}]},
{type, 72, tuple,
[{atom, 72, partial_download}, {type, 72, pid, []},
{type, 72, union,
[{type, 72, non_neg_integer, []},
{atom, 72, infinity}]}]}]},
[]}).
-type({options,
{type, 74, list, [{type, 74, option, []}]}, []}).
-type({result,
{type, 76, union,
[{type, 76, tuple,
[{atom, 76, ok},
{type, 76, tuple,
[{type, 76, tuple,
[{type, 76, pos_integer, []}, {type, 76, string, []}]},
{type, 76, headers, []}, {type, 76, string, []}]}]},
{type, 77, tuple,
[{atom, 77, error}, {type, 77, atom, []}]}]},
[]}).
%% @spec (URL) -> Result
%% URL = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a GET request.
%% Would be the same as calling `request(get, URL, [])',
%% that is {@link request/3} with an empty header list.
%% @end
%% @see request/3
-spec get(string()) -> result().
get(URL) -> request(get, URL, []).
%% @spec (URL, Hdrs) -> Result
%% URL = string()
%% Hdrs = [{Header, Value}]
%% Header = string()
%% Value = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a GET request.
%% Would be the same as calling `request(get, URL, Hdrs)'.
%% @end
%% @see request/3
-spec get(string(), headers()) -> result().
get(URL, Hdrs) -> request(get, URL, Hdrs).
%% @spec (URL, RequestBody) -> Result
%% URL = string()
%% RequestBody = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a POST request with form data.
%% Would be the same as calling
%% `request(post, URL, [{"content-type", "x-www-form-urlencoded"}], Body)'.
%% @end
%% @see request/4
-spec post(string(), string()) -> result().
post(URL, Body) ->
request(post, URL,
[{<<"content-type">>, <<"x-www-form-urlencoded">>}],
Body).
%% @spec (URL, Hdrs, RequestBody) -> Result
%% URL = string()
%% Hdrs = [{Header, Value}]
%% Header = string()
%% Value = string()
%% RequestBody = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a POST request.
%% Would be the same as calling
%% `request(post, URL, Hdrs, Body)'.
%% @end
%% @see request/4
-spec post(string(), headers(), string()) -> result().
post(URL, Hdrs, Body) ->
NewHdrs = case [X
|| {X, _} <- Hdrs,
str:to_lower(X) == <<"content-type">>]
of
[] ->
[{<<"content-type">>, <<"x-www-form-urlencoded">>}
| Hdrs];
_ -> Hdrs
end,
request(post, URL, NewHdrs, Body).
%% @spec (Method, URL, Hdrs) -> Result
%% Method = atom()
%% URL = string()
%% Hdrs = [{Header, Value}]
%% Header = string()
%% Value = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a request without a body.
%% Would be the same as calling `request(Method, URL, Hdrs, [], [])',
%% that is {@link request/5} with an empty body.
%% @end
%% @see request/5
-spec request(atom(), string(), headers()) -> result().
request(Method, URL, Hdrs) ->
request(Method, URL, Hdrs, [], []).
%% @spec (Method, URL, Hdrs, RequestBody) -> Result
%% Method = atom()
%% URL = string()
%% Hdrs = [{Header, Value}]
%% Header = string()
%% Value = string()
%% RequestBody = string()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a request with a body.
%% Would be the same as calling
%% `request(Method, URL, Hdrs, Body, [])', that is {@link request/5}
%% with no options.
%% @end
%% @see request/5
-spec request(atom(), string(), headers(), string()) -> result().
request(Method, URL, Hdrs, Body) ->
request(Method, URL, Hdrs, Body, []).
%% @spec (Method, URL, Hdrs, RequestBody, Options) -> Result
%% Method = atom()
%% URL = string()
%% Hdrs = [{Header, Value}]
%% Header = string()
%% Value = string()
%% RequestBody = string()
%% Options = [Option]
%% Option = {timeout, Milliseconds | infinity} |
%% {connect_timeout, Milliseconds | infinity} |
%% {socket_options, [term()]} |
%% Milliseconds = integer()
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
%% | {error, Reason}
%% StatusCode = integer()
%% ResponseBody = string()
%% Reason = connection_closed | connect_timeout | timeout
%% @doc Sends a request with a body.
%% Would be the same as calling
%% `request(Method, URL, Hdrs, Body, [])', that is {@link request/5}
%% with no options.
%% @end
%% @see request/5
-spec request(atom(), string(), headers(), string(), options()) -> result().
% ibrowse {response_format, response_format()} |
% Options - [option()]
% Option - {sync, boolean()} | {stream, StreamTo} | {body_format, body_format()} | {full_result,
% boolean()} | {headers_as_is, boolean()}
%body_format() = string() | binary()
% The body_format option is only valid for the synchronous request and the default is string.
% When making an asynchronous request the body will always be received as a binary.
% lhttpc: always binary

View File

@ -176,8 +176,8 @@ get_commands_spec() ->
desc = "Check if the password hash is correct",
longdesc = "Allowed hash methods: md5, sha.",
module = ?MODULE, function = check_password_hash,
args = [{user, binary}, {host, binary}, {passwordhash, string},
{hashmethod, string}],
args = [{user, binary}, {host, binary}, {passwordhash, binary},
{hashmethod, binary}],
args_example = [<<"peter">>, <<"myserver.com">>,
<<"5ebe2294ecd0e0f08eab7690d2a6ee69">>, <<"md5">>],
args_desc = ["User name to check", "Server to check",

View File

@ -273,7 +273,7 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
fun ({Key, binary}, Acc) ->
[{Key, <<>>}|Acc];
({Key, string}, Acc) ->
[{Key, <<>>}|Acc];
[{Key, ""}|Acc];
({Key, integer}, Acc) ->
[{Key, 0}|Acc];
({Key, {list, _}}, Acc) ->
@ -406,10 +406,10 @@ format_arg(Elements, {list, ElementsDef})
format_arg(Arg, integer) when is_integer(Arg) -> Arg;
format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg);
format_arg(Arg, binary) when is_binary(Arg) -> Arg;
format_arg(Arg, string) when is_list(Arg) -> process_unicode_codepoints(Arg);
format_arg(Arg, string) when is_binary(Arg) -> Arg;
format_arg(Arg, string) when is_list(Arg) -> Arg;
format_arg(Arg, string) when is_binary(Arg) -> binary_to_list(Arg);
format_arg(undefined, binary) -> <<>>;
format_arg(undefined, string) -> <<>>;
format_arg(undefined, string) -> "";
format_arg(Arg, Format) ->
?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]),
throw({invalid_parameter,

View File

@ -56,7 +56,7 @@
-record(state,
{host, docroot, accesslog, accesslogfd,
directory_indices, custom_headers, default_content_type,
content_types = []}).
content_types = [], user_access = none}).
-define(PROCNAME, ejabberd_mod_http_fileserver).
@ -133,7 +133,8 @@ start_link(Host, Opts) ->
init([Host, Opts]) ->
try initialize(Host, Opts) of
{DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
CustomHeaders, DefaultContentType, ContentTypes} ->
CustomHeaders, DefaultContentType, ContentTypes,
UserAccess} ->
{ok, #state{host = Host,
accesslog = AccessLog,
accesslogfd = AccessLogFD,
@ -141,7 +142,8 @@ init([Host, Opts]) ->
directory_indices = DirectoryIndices,
custom_headers = CustomHeaders,
default_content_type = DefaultContentType,
content_types = ContentTypes}}
content_types = ContentTypes,
user_access = UserAccess}}
catch
throw:Reason ->
{stop, Reason}
@ -165,7 +167,15 @@ initialize(Host, Opts) ->
[]),
DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
fun iolist_to_binary/1,
?DEFAULT_CONTENT_TYPE),
?DEFAULT_CONTENT_TYPE),
UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts,
mod_opt_type(must_authenticate_with),
[]),
UserAccess = case UserAccess0 of
[] -> none;
_ ->
dict:from_list(UserAccess0)
end,
ContentTypes = build_list_content_types(
gen_mod:get_opt(content_types, Opts,
fun(L) when is_list(L) ->
@ -180,7 +190,7 @@ initialize(Host, Opts) ->
[str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
<<", ">>)]),
{DocRoot, AccessLog, AccessLogFD, DirectoryIndices,
CustomHeaders, DefaultContentType, ContentTypes}.
CustomHeaders, DefaultContentType, ContentTypes, UserAccess}.
%% @spec (AdminCTs::[CT], Default::[CT]) -> [CT]
@ -246,10 +256,11 @@ try_open_log(FN, Host) ->
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({serve, LocalPath}, _From, State) ->
Reply = serve(LocalPath, State#state.docroot, State#state.directory_indices,
handle_call({serve, LocalPath, Auth}, _From, State) ->
Reply = serve(LocalPath, Auth, State#state.docroot, State#state.directory_indices,
State#state.custom_headers,
State#state.default_content_type, State#state.content_types),
State#state.default_content_type, State#state.content_types,
State#state.user_access),
{reply, Reply, State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
@ -305,9 +316,9 @@ code_change(_OldVsn, State, _Extra) ->
%% @doc Handle an HTTP request.
%% LocalPath is the part of the requested URL path that is "local to the module".
%% Returns the page to be sent back to the client and/or HTTP status code.
process(LocalPath, Request) ->
process(LocalPath, #request{host = Host, auth = Auth} = Request) ->
?DEBUG("Requested ~p", [LocalPath]),
try gen_server:call(get_proc_name(Request#request.host), {serve, LocalPath}) of
try gen_server:call(get_proc_name(Host), {serve, LocalPath, Auth}) of
{FileSize, Code, Headers, Contents} ->
add_to_log(FileSize, Code, Request),
{Code, Headers, Contents}
@ -318,21 +329,38 @@ process(LocalPath, Request) ->
ejabberd_web:error(not_found)
end.
serve(LocalPath, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType, ContentTypes) ->
FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
case file:read_file_info(FileName) of
{error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND;
{error, enotdir} -> ?HTTP_ERR_FILE_NOT_FOUND;
{error, eacces} -> ?HTTP_ERR_FORBIDDEN;
{ok, #file_info{type = directory}} -> serve_index(FileName,
DirectoryIndices,
CustomHeaders,
DefaultContentType,
ContentTypes);
{ok, FileInfo} -> serve_file(FileInfo, FileName,
CustomHeaders,
DefaultContentType,
ContentTypes)
serve(LocalPath, Auth, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentType,
ContentTypes, UserAccess) ->
CanProceed = case {UserAccess, Auth} of
{none, _} -> true;
{_, {User, Pass}} ->
case dict:find(User, UserAccess) of
{ok, Pass} -> true;
_ -> false
end;
_ ->
false
end,
case CanProceed of
true ->
FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
case file:read_file_info(FileName) of
{error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND;
{error, enotdir} -> ?HTTP_ERR_FILE_NOT_FOUND;
{error, eacces} -> ?HTTP_ERR_FORBIDDEN;
{ok, #file_info{type = directory}} -> serve_index(FileName,
DirectoryIndices,
CustomHeaders,
DefaultContentType,
ContentTypes);
{ok, FileInfo} -> serve_file(FileInfo, FileName,
CustomHeaders,
DefaultContentType,
ContentTypes)
end;
_ ->
?HTTP_ERR_FORBIDDEN
end.
%% Troll through the directory indices attempting to find one which
@ -466,6 +494,14 @@ mod_opt_type(default_content_type) ->
mod_opt_type(directory_indices) ->
fun (L) when is_list(L) -> L end;
mod_opt_type(docroot) -> fun (A) -> A end;
mod_opt_type(must_authenticate_with) ->
fun (L) when is_list(L) ->
lists:map(fun(UP) when is_binary(UP) ->
[K, V] = binary:split(UP, <<":">>),
{K, V}
end, L)
end;
mod_opt_type(_) ->
[accesslog, content_types, custom_headers,
default_content_type, directory_indices, docroot].
default_content_type, directory_indices, docroot,
must_authenticate_with].

View File

@ -89,9 +89,9 @@ loop(_State) -> receive stop -> ok end.
%% TODO: Support comment lines starting by %
update_bl_c2s() ->
?INFO_MSG("Updating C2S Blacklist", []),
case httpc:request(?BLC2S) of
case p1_http:get(?BLC2S) of
{ok, 200, _Headers, Body} ->
IPs = str:tokens(Body, <<"\n">>),
IPs = str:tokens(iolist_to_binary(Body), <<"\n">>),
ets:delete_all_objects(bl_c2s),
lists:foreach(fun (IP) ->
ets:insert(bl_c2s,

View File

@ -3548,6 +3548,7 @@ make_opts(StateData) ->
?MAKE_CONFIG_OPT(#config.allow_voice_requests),
?MAKE_CONFIG_OPT(#config.allow_subscription),
?MAKE_CONFIG_OPT(#config.mam),
?MAKE_CONFIG_OPT(#config.presence_broadcast),
?MAKE_CONFIG_OPT(#config.voice_request_min_interval),
?MAKE_CONFIG_OPT(#config.vcard),
{captcha_whitelist,

View File

@ -93,12 +93,10 @@ mod_opt_type(auth_type) ->
end;
mod_opt_type(recbuf) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(shaper) ->
fun (A) when is_atom(A) -> A end;
mod_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
mod_opt_type(sndbuf) ->
fun (I) when is_integer(I), I > 0 -> I end;
mod_opt_type(access) ->
fun (A) when is_atom(A) -> A end;
mod_opt_type(access) -> fun acl:access_rules_validator/1;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(hostname) -> fun iolist_to_binary/1;
mod_opt_type(ip) ->

View File

@ -251,7 +251,7 @@ init([ServerHost, Opts]) ->
Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
ejabberd_router:register_route(Host, ServerHost),
Access = gen_mod:get_opt(access_createnode, Opts,
fun(A) when is_atom(A) -> A end, all),
fun acl:access_rules_validator/1, all),
PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts,
fun(A) when is_boolean(A) -> A end, true),
IQDisc = gen_mod:get_opt(iqdisc, Opts,
@ -262,7 +262,7 @@ init([ServerHost, Opts]) ->
fun(A) when is_integer(A) andalso A >= 0 -> A end, ?MAXITEMS),
MaxSubsNode = gen_mod:get_opt(max_subscriptions_node, Opts,
fun(A) when is_integer(A) andalso A >= 0 -> A end, undefined),
pubsub_index:init(Host, ServerHost, Opts),
[pubsub_index:init(Host, ServerHost, Opts) || gen_mod:db_type(ServerHost, ?MODULE)==mnesia],
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
DefaultModule = plugin(Host, hd(Plugins)),
BaseOptions = DefaultModule:options(),
@ -3899,8 +3899,7 @@ purge_offline(Host, LJID, Node) ->
Error
end.
mod_opt_type(access_createnode) ->
fun (A) when is_atom(A) -> A end;
mod_opt_type(access_createnode) -> fun acl:access_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(host) -> fun iolist_to_binary/1;
mod_opt_type(ignore_pep_from_offline) ->

View File

@ -613,14 +613,11 @@ check_ip_access(undefined, _IPAccess) ->
check_ip_access(IPAddress, IPAccess) ->
acl:match_rule(global, IPAccess, IPAddress).
mod_opt_type(access) ->
fun acl:access_rules_validator/1;
mod_opt_type(access_from) ->
fun (A) when is_atom(A) -> A end;
mod_opt_type(access) -> fun acl:access_rules_validator/1;
mod_opt_type(access_from) -> fun acl:access_rules_validator/1;
mod_opt_type(captcha_protected) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(ip_access) ->
fun acl:access_rules_validator/1;
mod_opt_type(ip_access) -> fun acl:access_rules_validator/1;
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(password_strength) ->
fun (N) when is_number(N), N >= 0 -> N end;

View File

@ -50,7 +50,7 @@
path_to_node/1, can_fetch_item/2, is_subscribed/1]).
init(_Host, _ServerHost, _Opts) ->
%pubsub_subscription:init(),
%pubsub_subscription:init(Host, ServerHost, Opts),
mnesia:create_table(pubsub_state,
[{disc_copies, [node()]},
{type, ordered_set},

View File

@ -61,7 +61,7 @@
encode_host_like/1]).
init(_Host, _ServerHost, _Opts) ->
%%pubsub_subscription_sql:init(),
%%pubsub_subscription_sql:init(Host, ServerHost, Opts),
ok.
terminate(_Host, _ServerHost) ->

View File

@ -28,7 +28,7 @@
-author("bjc@kublai.com").
%% API
-export([init/0, subscribe_node/3, unsubscribe_node/3,
-export([init/3, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4,
make_subid/0,
get_options_xform/2, parse_options_xform/1]).
@ -73,7 +73,7 @@
%%====================================================================
%% API
%%====================================================================
init() -> ok = create_table().
init(_Host, _ServerHost, _Opts) -> ok = create_table().
subscribe_node(JID, NodeId, Options) ->
case catch mnesia:sync_dirty(fun add_subscription/3, [JID, NodeId, Options])

View File

@ -28,7 +28,7 @@
-author("pablo.polvorin@process-one.net").
%% API
-export([init/0, subscribe_node/3, unsubscribe_node/3,
-export([init/3, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4,
make_subid/0,
get_options_xform/2, parse_options_xform/1]).
@ -71,7 +71,7 @@
%% API
%%====================================================================
init() -> ok = create_table().
init(_Host, _ServerHost, _Opts) -> ok = create_table().
-spec subscribe_node(_JID :: _, _NodeId :: _, Options :: [] | mod_pubsub:subOptions()) ->
{result, mod_pubsub:subId()}.

View File

@ -28,7 +28,7 @@
-behaviour(ejabberd_config).
-export([start/1, stop/1, get/2, get/3, post/4, delete/2,
request/6, with_retry/4, opt_type/1]).
put/4, patch/4, request/6, with_retry/4, opt_type/1]).
-include("logger.hrl").
@ -36,14 +36,14 @@
-define(CONNECT_TIMEOUT, 8000).
start(Host) ->
http_p1:start(),
p1_http:start(),
Pool_size =
ejabberd_config:get_option({ext_api_http_pool_size, Host},
fun(X) when is_integer(X), X > 0->
X
end,
100),
http_p1:set_pool_size(Pool_size).
p1_http:set_pool_size(Pool_size).
stop(_Host) ->
ok.
@ -71,18 +71,17 @@ delete(Server, Path) ->
request(Server, delete, Path, [], "application/json", <<>>).
post(Server, Path, Params, Content) ->
Data = case catch jiffy:encode(Content) of
{'EXIT', Reason} ->
?ERROR_MSG("HTTP content encodage failed:~n"
"** Content = ~p~n"
"** Err = ~p",
[Content, Reason]),
<<>>;
Encoded ->
Encoded
end,
Data = encode_json(Content),
request(Server, post, Path, Params, "application/json", Data).
put(Server, Path, Params, Content) ->
Data = encode_json(Content),
request(Server, put, Path, Params, "application/json", Data).
patch(Server, Path, Params, Content) ->
Data = encode_json(Content),
request(Server, patch, Path, Params, "application/json", Data).
request(Server, Method, Path, Params, Mime, Data) ->
URI = url(Server, Path, Params),
Opts = [{connect_timeout, ?CONNECT_TIMEOUT},
@ -91,7 +90,7 @@ request(Server, Method, Path, Params, Mime, Data) ->
{"content-type", Mime},
{"User-Agent", "ejabberd"}],
Begin = os:timestamp(),
Result = case catch http_p1:request(Method, URI, Hdrs, Data, Opts) of
Result = case catch p1_http:request(Method, URI, Hdrs, Data, Opts) of
{ok, Code, _, <<>>} ->
{ok, Code, []};
{ok, Code, _, <<" ">>} ->
@ -147,6 +146,18 @@ request(Server, Method, Path, Params, Mime, Data) ->
%%% HTTP helpers
%%%----------------------------------------------------------------------
encode_json(Content) ->
case catch jiffy:encode(Content) of
{'EXIT', Reason} ->
?ERROR_MSG("HTTP content encodage failed:~n"
"** Content = ~p~n"
"** Err = ~p",
[Content, Reason]),
<<>>;
Encoded ->
Encoded
end.
base_url(Server, Path) ->
Tail = case iolist_to_binary(Path) of
<<$/, Ok/binary>> -> Ok;

View File

@ -93,7 +93,10 @@ rchr(B, C) ->
-spec str(binary(), binary()) -> non_neg_integer().
str(B1, B2) ->
string:str(binary_to_list(B1), binary_to_list(B2)).
case binary:match(B1, B2) of
{R, _Len} -> R+1;
_ -> 0
end.
-spec rstr(binary(), binary()) -> non_neg_integer().
@ -113,7 +116,7 @@ cspan(B1, B2) ->
-spec copies(binary(), non_neg_integer()) -> binary().
copies(B, N) ->
iolist_to_binary(string:copies(binary_to_list(B), N)).
binary:copy(B, N).
-spec words(binary()) -> pos_integer().
@ -201,7 +204,7 @@ join(L, Sep) ->
-spec substr(binary(), pos_integer()) -> binary().
substr(B, N) ->
iolist_to_binary(string:substr(binary_to_list(B), N)).
binary_part(B, N-1, byte_size(B)-N+1).
-spec chr(binary(), char()) -> non_neg_integer().
@ -221,7 +224,7 @@ chars(C, N) ->
-spec substr(binary(), pos_integer(), non_neg_integer()) -> binary().
substr(B, S, E) ->
iolist_to_binary(string:substr(binary_to_list(B), S, E)).
binary_part(B, S-1, E).
-spec strip(binary(), both | left | right, char()) -> binary().

View File

@ -481,9 +481,22 @@ format_element(El) ->
false -> io_lib:format(" ~s~n", El)
end.
substitute_forwarded(#mam_result{sub_els = Sub} = El) ->
El#mam_result{sub_els = [substitute_forwarded(SEl) || SEl <- Sub]};
substitute_forwarded(#carbons_sent{forwarded = Sub} = El) ->
El#carbons_sent{forwarded = [substitute_forwarded(SEl) || SEl <- Sub]};
substitute_forwarded(#message{sub_els = Sub} = El) ->
El#message{sub_els = [substitute_forwarded(SEl) || SEl <- Sub]};
substitute_forwarded(#forwarded{delay = Delay, xml_els = Sub}) ->
#forwarded_decoded{delay = Delay, sub_els = [xmpp:decode(SEl) || SEl <- Sub]};
substitute_forwarded(El) ->
El.
decode(El, NS, Opts) ->
try
Pkt = xmpp:decode(El, NS, Opts),
Pkt = substitute_forwarded(xmpp:decode(El, NS, Opts)),
ct:pal("RECV:~n~s~n~s",
[format_element(El), xmpp:pp(Pkt)]),
Pkt

View File

@ -5,6 +5,9 @@
-include("mod_proxy65.hrl").
-include("xmpp_codec.hrl").
-record(forwarded_decoded, {delay :: #delay{},
sub_els = [] :: [fxml:xmlel()]}).
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).