Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: adf8676f0b
Fetching contributors…

Cannot retrieve contributors at this time

212 lines (191 sloc) 7.91 kb
%% 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_lib("xmerl/include/xmerl.hrl").
payload(Payload) ->
case xmerl_scan:string(Payload) of
{E, _} when is_record(E, xmlElement) ->
case catch decode_element(E) of
{'EXIT', Reason} -> exit(Reason);
Result -> Result
end;
{error, Reason} -> {error, Reason}
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, list_to_atom(TextValue), []}};
{Params, _} ->
DecodedParams = decode_params(Params#xmlElement.content),
{ok, {call, list_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([$ |Rest]) -> only_whitespace(Rest);
only_whitespace([$\n|Rest]) -> only_whitespace(Rest);
only_whitespace([$\t|Rest]) -> only_whitespace(Rest);
only_whitespace(_) -> no.
get_text_value([]) -> [];
get_text_value([T|Rest]) when is_record(T, xmlText) ->
T#xmlText.value++get_text_value(Rest);
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(Nil) when Nil#xmlElement.name == nil ->
undefined;
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),
[{list_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 list_to_integer(Integer) of
{'EXIT', _Reason} -> throw({error, {not_integer, Integer}});
Value -> Value
end.
make_double(Double) ->
case catch list_to_float(Double) of
{'EXIT', _} -> throw({error, {not_double, Double}});
Value -> Value
end.
ensure_iso8601_date(Date) ->
case xmlrpc_util:is_iso8601_date(Date) of
no -> throw({error, {not_iso8601_date, Date}});
yes -> Date
end.
ensure_base64(Base64) ->
case xmlrpc_util:is_base64(Base64) of
no -> throw({error, {not_base64, Base64}});
yes -> Base64
end.
Jump to Line
Something went wrong with that request. Please try again.