src/smtp/rfc2047.erl

%% Encode a string according to RFC 2047 using quoted-printable.
%% Assumes UTF-8 as the character set.
%%
%% @copyright 2009 Marc Worrell

-module(rfc2047).
-author("Marc Worrell <marc@worrell.nl>").

-export([encode/1, decode/1]).


-spec encode(string()|binary()) -> binary().
encode(B) when is_list(B) ->
	encode(list_to_binary(B));
encode(<<>>) ->
	<<>>;
encode(Text) ->
    encode(Text, Text).

%% Don't escape when all characters are ASCII printable
encode(<<>>, Text) ->
    Text;
encode(<<H,T/binary>>, Text) when H >= 32 andalso H =< 126 andalso H /= $= ->
    encode(T, Text);
encode(_, Text) ->
    <<"=?UTF-8?Q?", (encode(Text, <<>>, 0))/binary, "?=">>.

encode(<<>>, Acc, _WordLen) ->
    Acc;
encode(T, Acc, WordLen) when WordLen >= 55 ->
    %% Make sure that the individual encoded words are not longer than 76 chars (including charset etc)
    encode(T, <<Acc/binary,"?=\r\n =?UTF-8?Q?">>, 0);
encode(<<C, T/binary>>, Acc, WordLen) when C > 32 andalso C < 127 andalso C /= 32
    andalso C /= $? andalso C /= $_ andalso C /= $= andalso C /= $. ->
    encode(T, <<Acc/binary, C>>, WordLen+1);
encode(<<C, T/binary>>, Acc, WordLen) ->
    C2 = hex(C rem 16),
    C1 = hex(C div 16),
    encode(T, <<Acc/binary, $=, C1, C2>>, WordLen+3).


-spec decode(string()|binary()) -> binary().
decode(B) when is_list(B) ->
    decode(list_to_binary(B));
decode(Text) ->
    decode(Text, in_text, <<>>).

decode(<<>>, _, Acc) ->
    Acc;
decode(<<"=?UTF-8?Q?", T/binary>>, in_text, Acc) ->
    decode(T, in_utf8, Acc);
decode(<<"?= \r\n", T/binary>>, in_utf8, Acc) ->
    decode(T, in_text, Acc);
decode(<<"?=", T/binary>>, in_utf8, Acc) ->
    decode(T, in_text, Acc);
decode(<<$=,C1,C2, T/binary>>, in_utf8, Acc) ->
    C = unhex(C1)*16 + unhex(C2),
    decode(T, in_utf8, <<Acc/binary, C>>);
decode(<<H,T/binary>>, State, Acc) ->
    decode(T, State, <<Acc/binary, H>>).

hex(N) when N >= 10 -> N + $A - 10;
hex(N) -> N + $0.

unhex(C) when C >= $a ->
    C - $a + 10;
unhex(C) when C >= $A ->
    C - $A + 10;
unhex(C) ->
    C - $0.