Page MenuHomePhorge

D161.1775830956.diff
No OneTemporary

Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None

D161.1775830956.diff

diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
To use eimap from your imap application add the following line to your rebar
config:
- { eimap, "*", {git, "git://git.kolab.org/diffusion/EI/eimap.git" }
+ { eimap, "*", {git, "git://git.kolab.org/diffusion/EI/eimap.git" } }
There is no need to start the eimap application as it does not have any process
or startup routines related to its usage. eimap does rely on lager being avilable,
@@ -28,7 +28,7 @@
============
The eimap erlang module is the home of the primary API for the eimap library. It
is a gen_fsm and should be started as a process in the normal Erlang/OTP manner for
-use.
+use.
An eimap instance represents a single IMAP connection to a single IMAP server
and is stateful: commands that are started may change the selected folder, for
@@ -38,14 +38,14 @@
Once started, an eimap process may be directed to connect to an imap server
and then start with functions such as fetching path tokens:
- ImapServerArgs = #eimap_server_config{ host = "acme.com" },
+ ImapServerArgs = [ { host, "imap.acme.com" }, { port, 143 }, { tls, starttls } ]
{ ok, Imap } = eimap:start_link(ImapServerArgs),
eimap_imap:starttls(),
eimap_imap:login(Imap, self(), undefined, "username", "password"),
eimap_imap:connect(Imap),
eimap_imap:get_path_tokens(Imap, self(), get_path_tokens)
-The eimap_server_config record is defined in eimap.hrl and allows one to set
+The Imap server args is a simple proplist which allows one to set
host, port and TLS settings. For TLS, the following values are supported:
* true: start a TLS session when opening the socket ("implicit TLS")
diff --git a/src/eimap_command.erl b/src/eimap_command.erl
--- a/src/eimap_command.erl
+++ b/src/eimap_command.erl
@@ -38,14 +38,29 @@
process_lines(_ParseTaggedLine, _Tag, LastPartialLine, [], Acc, Module) -> { more, { LastPartialLine, Acc, Module } };
process_lines(ParseTaggedLine, Tag, LastPartialLine, [Line|MoreLines], Acc, Module) ->
- process_line(ParseTaggedLine, eimap_utils:is_tagged_response(Line, Tag), Tag, LastPartialLine, Line, MoreLines, Acc, Module).
+ { FirstLine, ContinuationBytes } = eimap_utils:num_literal_continuation_bytes(Line),
+ process_line(ContinuationBytes, ParseTaggedLine, eimap_utils:is_tagged_response(FirstLine, Tag), Tag, LastPartialLine, FirstLine, MoreLines, Acc, Module).
-process_line(ParseTaggedLine, true, Tag, _LastPartialLine, Line, _MoreLines, Acc, Module) ->
+process_line(ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, [<<>>|MoreLines], Acc, Module) ->
+ %% skip empty lines
+ process_line(ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, MoreLines, Acc, Module);
+process_line(0, ParseTaggedLine, tagged, Tag, _LastPartialLine, Line, _MoreLines, Acc, Module) ->
Checked = eimap_utils:check_response_for_failure(Line, Tag),
Module:formulate_response(Checked, parse_tagged(Checked, ParseTaggedLine, Line, Acc, Module));
-process_line(ParseTaggedLine, false, Tag, LastPartialLine, Line, MoreLines, Acc, Module) ->
- process_lines(ParseTaggedLine, Tag, LastPartialLine, MoreLines, Module:process_line(Line, Acc), Module).
+process_line(0, ParseTaggedLine, untagged, Tag, LastPartialLine, Line, MoreLines, Acc, Module) ->
+ io:format("Calling it here with ~p~n~n...", [Line]),
+ process_lines(ParseTaggedLine, Tag, LastPartialLine, MoreLines, Module:process_line(Line, Acc), Module);
+process_line(ContinuationBytes, ParseTaggedLine, _IsTagged, Tag, LastPartialLine, Line, [], Acc, Module) ->
+ %% the line was continued, but there is no more lines ... so this line is now our last partial line. more must be on its way
+ io:format("Missing lines!~p~n~n", [Acc]),
+ BytesAsBinary = integer_to_binary(ContinuationBytes),
+ process_lines(ParseTaggedLine, Tag, <<Line/binary, ${, BytesAsBinary/binary, $}, LastPartialLine/binary>>, [], Acc, Module);
+process_line(_ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, [NextLine|MoreLines], Acc, Module) ->
+ { StrippedNextLine, NextContinuationBytes } = eimap_utils:num_literal_continuation_bytes(NextLine),
+ io:format("Connected up the next line: ~p ~i~n", [StrippedNextLine, NextContinuationBytes]),
+ FullLine = <<Line/binary, StrippedNextLine/binary>>,
+ process_line(NextContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, FullLine, MoreLines, Acc, Module).
formulate_response(ok, Data) -> { fini, Data };
formulate_response({ _, Reason }, _Data) -> { error, Reason }.
diff --git a/src/eimap_utils.erl b/src/eimap_utils.erl
--- a/src/eimap_utils.erl
+++ b/src/eimap_utils.erl
@@ -24,7 +24,8 @@
ensure_binary/1,
new_imap_compressors/0,
only_full_lines/1,
- binary_to_atom/1
+ binary_to_atom/1,
+ num_literal_continuation_bytes/1
]).
%% Translate the folder name in to a fully qualified folder path such as it
@@ -83,13 +84,49 @@
split_command_into_components(Buffer) when is_binary(Buffer) ->
split_command(Buffer).
--spec is_tagged_response(Buffer :: binary(), Tag :: binary()) -> true | false.
+-spec is_tagged_response(Buffer :: binary(), Tag :: binary()) -> tagged | untagged.
is_tagged_response(Buffer, Tag) ->
TagSize = size(Tag) + 1, % The extra char is a space
BufferSize = size(Buffer),
- case (TagSize =< BufferSize) of
- true -> <<Tag/binary, " ">> =:= binary:part(Buffer, 0, TagSize);
- false -> false
+ case
+ case (TagSize =< BufferSize) of
+ true -> <<Tag/binary, " ">> =:= binary:part(Buffer, 0, TagSize);
+ _ -> false
+ end of
+
+ true -> tagged;
+ _ -> untagged
+ end.
+
+-spec num_literal_continuation_bytes(Buffer :: binary()) -> { BufferSansContinuation :: binary(), NumberBytes :: integer() }.
+num_literal_continuation_bytes(Buffer) when size(Buffer) < 4 ->
+ { Buffer, 0 };
+num_literal_continuation_bytes(Buffer) ->
+ case binary:last(Buffer) =:= $} of
+ true -> number_of_bytes_in_continuation(Buffer);
+ false -> { Buffer, 0 }
+ end.
+
+number_of_bytes_in_continuation(Buffer) ->
+ BufferSize = size(Buffer),
+ OpenBracePos = find_continuation_open_brace(Buffer, BufferSize - 3),
+ confirm_continuation(Buffer, OpenBracePos).
+
+find_continuation_open_brace(_Buffer, 0) -> -1;
+find_continuation_open_brace(Buffer, Pos) ->
+ case binary:at(Buffer, Pos) of
+ ${ -> Pos;
+ _ -> find_continuation_open_brace(Buffer, Pos - 1)
+ end.
+
+confirm_continuation(Buffer, -1) ->
+ { Buffer, 0 };
+confirm_continuation(Buffer, OpenBracePos) ->
+ BufferSize = size(Buffer),
+ try binary_to_integer(binary:part(Buffer, OpenBracePos + 1, BufferSize - OpenBracePos - 2)) of
+ Result -> { binary:part(Buffer, 0, OpenBracePos), Result }
+ catch
+ _:_ -> { Buffer, 0 }
end.
-spec remove_tag_from_response(Buffer :: binary(), Tag :: undefine | binary(), Check :: check | trust) -> Command :: binary().
diff --git a/test/eimap_command_tests.erl b/test/eimap_command_tests.erl
new file mode 100644
--- /dev/null
+++ b/test/eimap_command_tests.erl
@@ -0,0 +1,65 @@
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
+%%
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
+%%
+%% 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 3 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, see <http://www.gnu.org/licenses/>.
+
+-module(eimap_command_tests).
+-include_lib("eunit/include/eunit.hrl").
+-export([process_line/2, formulate_response/2]).
+
+% c("test/eimap_command_tests.erl"). eunit:test(eimap_command).
+
+process_line(Data, Acc) -> [{ marked, Data } | Acc].
+formulate_response(Result, Data) -> eimap_command:formulate_response(Result, lists:reverse(Data)).
+
+
+literal_continuations_test_() ->
+ Data =
+ [
+ % input, output
+ % { Binary Response, Binary Tag, Parsed Results }
+ {
+ <<"* STATUS 1 (MESSAGES 231 {14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
+ <<"abcd">>,
+ { fini, [ { marked, <<"* STATUS 1 (MESSAGES 231 UIDNEXT 44292)">> } ] }
+ },
+ {
+ <<"* STATUS 1a (MESSAGES 231 {14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation">>,
+ <<"abcd">>,
+ { more, { <<"abcd OK Begin TLS negotiation">>, [ { marked, <<"* STATUS 1a (MESSAGES 231 UIDNEXT 44292)">> } ], ?MODULE } }
+ },
+ {
+ <<"* STATUS 2 (MESSAGES 231 {14}\r\n">>,
+ <<"abcd">>,
+ { more, { <<"* STATUS 2 (MESSAGES 231 {14}">>, [], ?MODULE } }
+ },
+ {
+ <<"* STATUS 3 (MESSAGES 231 {h14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
+ <<"abcd">>,
+ { fini, [ { marked, <<"* STATUS 3 (MESSAGES 231 {h14}">> }, { marked, <<"UIDNEXT 44292)">> } ] }
+ },
+ {
+ <<"* STATUS 4 (MESSAGES 231 \r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
+ <<"abcd">>,
+ { fini, [ { marked, <<"* STATUS 4 (MESSAGES 231 ">> }, { marked, <<"UIDNEXT 44292)">> } ] }
+ },
+ {
+ <<"* STATUS 5 (MESSAGES 231 UIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
+ <<"abcd">>,
+ { fini, [ { marked, <<"* STATUS 5 (MESSAGES 231 UIDNEXT 44292)">> } ] }
+ }
+ ],
+ lists:foldl(fun({ Binary, Tag, Result }, Acc) -> [?_assertEqual(Result, eimap_command:parse_response(multiline_response, Binary, Tag, ?MODULE))|Acc] end, [], Data).
+
diff --git a/test/eimap_utils_tests.erl b/test/eimap_utils_tests.erl
--- a/test/eimap_utils_tests.erl
+++ b/test/eimap_utils_tests.erl
@@ -105,10 +105,10 @@
Tag = <<"abcd">>,
Data =
[
- { <<Tag/binary, " Indeed\r\n">>, true },
- { <<Tag/binary, " Indeed">>, true },
- { <<"one">>, false },
- { <<"* Yeah baby">>, false }
+ { <<Tag/binary, " Indeed\r\n">>, tagged },
+ { <<Tag/binary, " Indeed">>, tagged },
+ { <<"one">>, untagged },
+ { <<"* Yeah baby">>, untagged }
],
lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:is_tagged_response(Input, Tag)) | Acc] end, [], Data).
@@ -179,3 +179,20 @@
],
lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:parse_flags(Input)) | Acc] end, [], Data).
+num_literal_continuation_bytes_test_() ->
+ Data =
+ [
+ { <<"abcd">>, { <<"abcd">>, 0 } },
+ { <<"abcd{5}">>, { <<"abcd">>, 5 } },
+ { <<"abcd{100}">>, { <<"abcd">>, 100 } },
+ { <<"123abcd{100}">>, { <<"123abcd">>, 100 } },
+ { <<"ab{123abcd{100}">>, { <<"ab{123abcd">>, 100 } },
+ { <<"ab{123abcd{1{00}">>, { <<"ab{123abcd{1">>, 0 } },
+ { <<"abcd{aa0}">>, { <<"abcd{aa0}">>, 0 } },
+ { <<"abcd{10aa0}">>, { <<"abcd{10aa0}">>, 0 } },
+ { <<"abcd100}">>, { <<"abcd100}">>, 0 } },
+ { <<"abcd100}">>, { <<"abcd100}">>, 0 } }
+ ],
+ lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:num_literal_continuation_bytes(Input)) | Acc] end, [], Data).
+
+

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 10, 2:22 PM (1 d, 28 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18855784
Default Alt Text
D161.1775830956.diff (11 KB)

Event Timeline