Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

223 lines (202 sloc) 8.802 kb
%%%-------------------------------------------------------------------
%%% File : dime.erl
%%% @author Anders Nygren <anders.nygren@gmail.com>
%%% @doc Encoding and decoding of DIME messages.
%%% The Direct Internet Message Encapsulation (DIME) specification
%%% defines a mechanism for packaging binary data with SOAP messages.
%%% http://bgp.potaroo.net/ietf/all-ids/draft-nielsen-dime-02.txt
%%% Layout of a DIME encoded message is like this
%%%<pre>
%%% 0 1 2 3
%%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | |M|M|C| | | |
%%% | VERSION |B|E|F| TYPE_T| RESRVD| OPTIONS_LENGTH |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | ID_LENGTH | TYPE_LENGTH |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | DATA_LENGTH |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | /
%%% / OPTIONS + PADDING /
%%% / |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | /
%%% / ID + PADDING /
%%% / |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | /
%%% / TYPE + PADDING /
%%% / |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%% | /
%%% / DATA + PADDING /
%%% / |
%%% +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
%%%<pre>
%%% @end
%%%
%%% Created : 7 Apr 2008 by Anders Nygren <>
%%%-------------------------------------------------------------------
-module(yaws_dime).
%% API
-export([encode/2,
decode/1,
pad_len/1]).
-include_lib("kernel/include/file.hrl").
-define(VERSION1, 1).
-define(T_UNCHANGED, 0).
-define(T_MEDIA_TYPE, 1).
-define(T_ABS_URI, 2).
-define(T_UNKNOWN, 3).
-define(T_NONE, 4).
-define(SOAP_URI, "http://schemas.xmlsoap.org/soap/envelope").
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% @spec (Req, Attachments) ->binary()
%% Description:
%%--------------------------------------------------------------------
encode(Req, []) ->
encode_part(1, 1, 0, ?T_ABS_URI, <<"">>, <<"">>, <<?SOAP_URI>>, Req);
encode(Req, As) ->
list_to_binary([encode_part(1, 0, 0, ?T_ABS_URI, <<"">>, <<"">>, <<?SOAP_URI>>, Req)|
encode_attachments(As)]).
encode_attachments([{attachment, Id, Type, File}]) ->
[encode_part(0, 1, 0, ?T_ABS_URI, <<"">>, list_to_binary(Id), list_to_binary(Type), File)];
encode_attachments([{attachment, Id, Type, File} | As]) ->
[encode_part(0, 0, 0, ?T_ABS_URI, <<"">>, list_to_binary(Id), list_to_binary(Type), File)|
encode_attachments(As)].
encode_part(MB, ME, CF, TypeT, Opts, ID, Type, Data) ->
Opts_len = size_of(Opts),
Opts_pad = pad_len(Opts_len),
Id_len = size_of(ID),
Id_pad = pad_len(Id_len),
Type_len = size_of(Type),
Type_pad = pad_len(Type_len),
Data_len = size_of(Data),
Data_pad = pad_len(Data_len),
Data1 = get_data(Data),
<<?VERSION1:5, MB:1, ME:1, CF:1, TypeT:4, 0:4,
Opts_len:1/big-integer-unit:16,
Id_len:1/big-integer-unit:16,
Type_len:1/big-integer-unit:16,
Data_len:1/big-integer-unit:32,
Opts:Opts_len/binary-unit:8, 0:Opts_pad/integer-unit:8,
ID:Id_len/binary-unit:8, 0:Id_pad/integer-unit:8,
Type:Type_len/binary-unit:8, 0:Type_pad/integer-unit:8,
Data1:Data_len/binary-unit:8, 0:Data_pad/integer-unit:8>>.
pad_len(Len) ->
case Len rem 4 of
0 ->
0;
N ->
4-N
end.
size_of(X) when is_list(X) ->
length(X);
size_of(X) when is_binary(X)->
size(X);
size_of({file, File}) ->
{ok,R} = file:read_file_info(File),
R#file_info.size.
get_data({file, File}) ->
{ok, Data} = file:read_file(File),
Data;
get_data(Data) when is_list(Data) ->
list_to_binary(Data);
get_data(Data) ->
Data.
%%--------------------------------------------------------------------
%% @spec (Msg::binary()) ->
%% @doc Decode a DIME encoded message.
%%--------------------------------------------------------------------
decode(Msg) ->
decode_recs(Msg, [], []).
decode_recs(<<>>, Acc, _Chunks) ->
Acc;
decode_recs(Msg, Acc, Chunks) ->
case {decode_rec(Msg), Chunks} of
%% A normal record
{{_ME=0, _CF=0, Opts, ID, Type, Chunk, More}, []} ->
decode_recs(More, [{Opts, ID, Type, Chunk}|Acc], []);
%% The last chunk of a block, but not the last record
{{_ME=0, _CF=0, Opts, ID, Type, Chunk, More}, Chunks} ->
Cs = lists:reverse([{Opts, ID, Type, Chunk}|Chunks]),
%% Only the first chunk has values for Opts, Id and Type
{ROpts, RID, RType, _Chunk} = hd(Cs),
Block = list_to_binary([Ch || {_Opts, _ID, _Type, Ch} <- Cs]),
error_logger:info_report([?MODULE, decode_rec, merging,
{id, ID},{type, Type},
{chunks, length(Cs)}]),
decode_recs(More, [{ROpts, RID, RType, Block}|Acc], []);
%% Last record, but not chunked
{{_ME=1, _CF=0, Opts, ID, Type, Data, _More}, []} ->
lists:reverse([{Opts, ID, Type, Data}|Acc]);
%% Last record, and last chunk of block
{{_ME=1, _CF=0, Opts, ID, Type, Chunk, _More}, Chunks} ->
Cs = lists:reverse([{Opts, ID, Type, Chunk}|Chunks]),
{ROpts, RID, RType, _Chunk} = hd(Cs),
Block = list_to_binary([Ch || {_Opts, _ID, _Type, Ch} <- Cs]),
error_logger:info_report([?MODULE, decode_rec, merging,
{id, ID},{type, Type},
{chunks, length(Cs)}]),
lists:reverse([{ROpts, RID, RType, Block}|Acc]);
%% First or intermediate chunk, but not the last
{{_ME=0, _CF=1, Opts, ID, Type, Data, More}, Chunks} ->
decode_recs(More, Acc, [{Opts, ID, Type, Data}|Chunks]);
%% Something wrong, ME=1 and CF=1
ErrorResult ->
error_logger:error_report([?MODULE, decode_recs,
{result, ErrorResult}])
end.
decode_rec(<<?VERSION1:5, _MB:1, ME:1, CF:1, _Type_T:4, _Res:4,
Opt_Len:1/big-integer-unit:16,
ID_Len:1/big-integer-unit:16,
Type_Len:1/big-integer-unit:16,
Data_Len:1/big-integer-unit:32,
Rest/binary>>=Block) ->
error_logger:info_report([?MODULE, decode_rec,
{version,1},
{mb,_MB},{me,ME},{cf,CF},{type_t,_Type_T},
{res,_Res},{opt_len,Opt_Len},{id_len,ID_Len},
{type_len,Type_Len},{date_len,Data_Len},
{total_rec_size,byte_size(Block)}]),
Opt_pad = pad_len(Opt_Len),
Id_pad = pad_len(ID_Len),
Type_pad = pad_len(Type_Len),
Data_pad = pad_len(Data_Len),
Header = Opt_Len+Opt_pad+ID_Len+Id_pad+Type_Len+Type_pad,
DataTot = Data_Len+Data_pad,
case byte_size(Rest) of
N when N >= Header+DataTot ->
<<Opts:Opt_Len/binary-unit:8, _:Opt_pad/binary-unit:8,
ID:ID_Len/binary-unit:8, _:Id_pad/binary-unit:8,
Type:Type_Len/binary-unit:8, _:Type_pad/binary-unit:8,
Data:Data_Len/binary-unit:8, _:Data_pad/binary-unit:8,
More/binary >> = Rest,
{ME, CF, Opts, ID, Type, Data, More};
N when N > Header->
<<Opts:Opt_Len/binary-unit:8, _:Opt_pad/binary-unit:8,
ID:ID_Len/binary-unit:8, _:Id_pad/binary-unit:8,
Type:Type_Len/binary-unit:8, _:Type_pad/binary-unit:8,
Data/binary>> = Rest,
error_logger:error_report([?MODULE, decode_rec, short_record,
{need, Header+DataTot},
{has, N}]),
{ME, CF, Opts, ID, Type, Data, <<>>};
N ->
error_logger:error_report([?MODULE, decode_rec, short_record,
{need, Header+DataTot},
{has, N}]),
{error, not_enough_data}
end;
decode_rec(Bin) ->
error_logger:error_report([?MODULE, decode_rec, short_record,
no_header,
{has, byte_size(Bin)}]),
{error, no_header}.
%%====================================================================
%% Internal functions
%%====================================================================
Jump to Line
Something went wrong with that request. Please try again.