24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-07-06 23:22:36 +02:00
xmpp.chapril.org-ejabberd/src/xmlrpc_decode.erl
2012-09-11 15:45:59 +02:00

242 lines
8.1 KiB
Erlang

%% Copyright (C) 2003 Joakim Grebenö <jocke@gleipnir.com>.
%% All rights reserved.
%%
%% Redistribution and use in source and binary forms, with or without
%% modification, are permitted provided that the following conditions
%% are met:
%%
%% 1. Redistributions of source code must retain the above copyright
%% notice, this list of conditions and the following disclaimer.
%% 2. Redistributions in binary form must reproduce the above
%% copyright notice, this list of conditions and the following
%% disclaimer in the documentation and/or other materials provided
%% with the distribution.
%%
%% THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
%% OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
%% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
%% ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
%% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
%% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
%% GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-module(xmlrpc_decode).
-author('jocke@gleipnir.com').
-export([payload/1]).
-include("log.hrl").
-include_lib("xmerl/include/xmerl.hrl").
payload(Payload) ->
?DEBUG_LOG({scanning_payload, Payload}),
case xmerl_scan:string(Payload) of
{error, Reason} ->
?DEBUG_LOG({error_scanning, Payload, Reason}),
{error, Reason};
{E, _} ->
?DEBUG_LOG({decoding_element, E}),
case catch decode_element(E) of
{'EXIT', Reason} ->
?DEBUG_LOG({error_deconding, E, Reason}), exit(Reason);
Result ->
?DEBUG_LOG({result_deconding, E, Result}), Result
end
end.
decode_element(#xmlElement{name = methodCall} =
MethodCall)
when is_record(MethodCall, xmlElement) ->
{MethodName, Rest} = match_element([methodName],
MethodCall#xmlElement.content),
TextValue =
get_text_value(MethodName#xmlElement.content),
case match_element(normal, [params], Rest) of
{error, {missing_element, _}} ->
{ok, {call, jlib:binary_to_atom(TextValue), []}};
{Params, _} ->
DecodedParams =
decode_params(Params#xmlElement.content),
{ok,
{call, jlib:binary_to_atom(TextValue), DecodedParams}}
end;
decode_element(#xmlElement{name = methodResponse} =
MethodResponse)
when is_record(MethodResponse, xmlElement) ->
case match_element([fault, params],
MethodResponse#xmlElement.content)
of
{Fault, _} when Fault#xmlElement.name == fault ->
{Value, _} = match_element([value],
Fault#xmlElement.content),
case decode(Value#xmlElement.content) of
{struct, [{faultCode, Code}, {faultString, String}]}
when is_integer(Code) ->
case xmlrpc_util:is_string(String) of
yes -> {ok, {response, {fault, Code, String}}};
no -> {error, {bad_string, String}}
end;
_ -> {error, {bad_element, MethodResponse}}
end;
{Params, _} ->
case decode_params(Params#xmlElement.content) of
[DecodedParam] -> {ok, {response, [DecodedParam]}};
DecodedParams ->
{error, {to_many_params, DecodedParams}}
end
end;
decode_element(E) -> {error, {bad_element, E}}.
match_element(NameList, Content) ->
match_element(throw, NameList, Content).
match_element(Type, NameList, []) ->
return(Type, {error, {missing_element, NameList}});
match_element(Type, NameList, [E | Rest])
when is_record(E, xmlElement) ->
case lists:member(E#xmlElement.name, NameList) of
true -> {E, Rest};
false ->
return(Type,
{error, {unexpected_element, E#xmlElement.name}})
end;
match_element(Type, NameList, [T | Rest])
when is_record(T, xmlText) ->
case only_whitespace(T#xmlText.value) of
yes -> match_element(Type, NameList, Rest);
no ->
return(Type,
{error, {unexpected_text, T#xmlText.value, NameList}})
end.
return(throw, Result) -> throw(Result);
return(normal, Result) -> Result.
only_whitespace(<<>>) -> yes;
only_whitespace(<<$\s, Rest/binary>>) ->
only_whitespace(Rest);
only_whitespace(<<$\n, Rest/binary>>) ->
only_whitespace(Rest);
only_whitespace(<<$\t, Rest/binary>>) ->
only_whitespace(Rest);
only_whitespace(_) -> no.
get_text_value([]) -> <<>>;
get_text_value([T | Rest]) when is_record(T, xmlText) ->
<<(list_to_binary(T#xmlText.value))/binary, (get_text_value(Rest))/binary>>;
get_text_value(_) -> throw({error, missing_text}).
decode_params([]) -> [];
decode_params(Content) ->
case match_element(normal, [param], Content) of
{error, {missing_element, _}} -> [];
{Param, Rest} ->
{Value, _} = match_element([value],
Param#xmlElement.content),
[decode(Value#xmlElement.content) | decode_params(Rest)]
end.
decode(Content) when is_list(Content) ->
case get_value(Content) of
{text_value, TextValue} -> TextValue;
E -> decode(E)
end;
decode(String) when is_record(String, xmlText) ->
String#xmlText.value;
decode(Struct) when Struct#xmlElement.name == struct ->
{struct, decode_members(Struct#xmlElement.content)};
decode(Array) when Array#xmlElement.name == array ->
{Data, _} = match_element([data],
Array#xmlElement.content),
{array, decode_values(Data#xmlElement.content)};
decode(Int)
when Int#xmlElement.name == int;
Int#xmlElement.name == i4 ->
TextValue = get_text_value(Int#xmlElement.content),
make_integer(TextValue);
decode(Boolean)
when Boolean#xmlElement.name == boolean ->
case get_text_value(Boolean#xmlElement.content) of
<<"1">> -> true;
<<"0">> -> false;
TextValue ->
throw({error, {invalid_boolean, TextValue}})
end;
decode(String) when String#xmlElement.name == string ->
get_text_value(String#xmlElement.content);
decode(Double) when Double#xmlElement.name == double ->
TextValue = get_text_value(Double#xmlElement.content),
make_double(TextValue);
decode(Date)
when Date#xmlElement.name == 'dateTime.iso8601' ->
TextValue = get_text_value(Date#xmlElement.content),
{date, ensure_iso8601_date(TextValue)};
decode(Base64) when Base64#xmlElement.name == base64 ->
TextValue = get_text_value(Base64#xmlElement.content),
{base64, ensure_base64(TextValue)};
decode(Value) -> throw({error, {bad_value, Value}}).
get_value(Content) ->
case any_element(Content) of
false -> {text_value, get_text_value(Content)};
true -> get_element(Content)
end.
any_element([]) -> false;
any_element([E | _]) when is_record(E, xmlElement) -> true;
any_element([_ | Rest]) -> any_element(Rest).
get_element([]) -> throw({error, missing_element});
get_element([E | _]) when is_record(E, xmlElement) -> E;
get_element([T | Rest]) when is_record(T, xmlText) ->
case only_whitespace(T#xmlText.value) of
yes -> get_element(Rest);
no -> throw({error, {unexpected_text, T#xmlText.value}})
end.
decode_members(Content) ->
case match_element(normal, [member], Content) of
{error, {missing_element, _}} -> [];
{Member, Rest} ->
{Name, Rest2} = match_element([name],
Member#xmlElement.content),
TextValue = get_text_value(Name#xmlElement.content),
{Value, _} = match_element([value], Rest2),
[{jlib:binary_to_atom(TextValue),
decode(Value#xmlElement.content)}
| decode_members(Rest)]
end.
decode_values([]) -> [];
decode_values(Content) ->
case match_element(normal, [value], Content) of
{error, {missing_element, _}} -> [];
{Value, Rest} ->
[decode(Value#xmlElement.content) | decode_values(Rest)]
end.
make_integer(Integer) ->
case catch jlib:binary_to_integer(Integer) of
{'EXIT', _Reason} ->
throw({error, {not_integer, Integer}});
Value -> Value
end.
make_double(Double) ->
case catch list_to_float(binary_to_list(Double)) of
{'EXIT', _} -> throw({error, {not_double, Double}});
Value -> Value
end.
ensure_iso8601_date(Date) ->
xmlrpc_util:is_iso8601_date(Date).
ensure_base64(Base64) ->
xmlrpc_util:is_base64(Base64).