Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F118484850
D161.1775833307.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None
D161.1775833307.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 10, 3:01 PM (1 d, 6 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18855784
Default Alt Text
D161.1775833307.diff (11 KB)
Attached To
Mode
D161: T932: Support IMAP literal continuations from the server
Attached
Detach File
Event Timeline