Changes of Revision 3
erlang-eimap.spec
Changed
x
1
2
%global realname eimap
3
%global debug_package %{nil}
4
5
-Name: erlang-%{realname}
6
-Version: 0.1.5
7
-Release: 1%{?dist}
8
-Summary: Erlang IMAP client
9
-Group: Development/Libraries
10
-License: BSD
11
-URL: http://git.kolab.org/diffusion/EI/%{realname}.git
12
+Name: erlang-%{realname}
13
+Version: 0.2.4
14
+Release: 0.20160111.git%{?dist}
15
+Summary: Erlang IMAP client
16
+Group: Development/Libraries
17
+License: GPLv3+
18
+URL: http://git.kolab.org/diffusion/EI/%{realname}.git
19
%if 0%{?el7}%{?fedora}
20
-VCS: scm:git:https://git.kolab.org/diffusion/EI/%{realname}.git
21
+VCS: scm:git:https://git.kolab.org/diffusion/EI/%{realname}.git
22
%endif
23
-Source0: erlang-eimap-%{version}.tar.gz
24
+Source0: erlang-eimap-0.2.4.tar.gz
25
26
-BuildRequires: erlang-goldrush >= 0.1.6
27
-BuildRequires: erlang-lager >= 2.1.0
28
+Patch0001: 0001-there-is-only-ever-one-response.-be-strict-about-tha.patch
29
+Patch0002: 0002-the-explicit-capabilities-command-is-multiline-so-it.patch
30
+Patch0003: 0003-don-t-munge-the-incoming-response.-leave-that-to-the.patch
31
+
32
+BuildRequires: erlang-goldrush >= 0.1.7
33
+BuildRequires: erlang-lager >= 2.2.0
34
BuildRequires: erlang-rebar >= 2.5.1
35
36
-Requires: erlang-erts%{?_isa} >= R13B
37
-Requires: erlang-stdlib%{?_isa} >= R13B
38
+Requires: erlang-erts%{?_isa} >= R13B
39
+Requires: erlang-stdlib%{?_isa} >= R13B
40
41
%description
42
IMAP client library for Erlang
43
44
%prep
45
-%setup -q
46
+%setup -q -n eimap-%{version}
47
+
48
+%patch0001 -p1
49
+%patch0002 -p1
50
+%patch0003 -p1
51
52
%build
53
rebar compile -v
54
55
56
install -D -m 644 ebin/%{realname}.app %{buildroot}%{_libdir}/erlang/lib/%{realname}-%{version}/ebin/%{realname}.app
57
install -D -m 644 ebin/*.beam %{buildroot}%{_libdir}/erlang/lib/%{realname}-%{version}/ebin/.
58
-install -D -m 644 src/*.hrl %{buildroot}%{_libdir}/erlang/lib/%{realname}-%{version}/src/
59
60
%files
61
%doc README.md
62
63
%dir %{_libdir}/erlang/lib/%{realname}-%{version}/src/
64
%{_libdir}/erlang/lib/%{realname}-%{version}/ebin/%{realname}.app
65
%{_libdir}/erlang/lib/%{realname}-%{version}/ebin/*.beam
66
-%{_libdir}/erlang/lib/%{realname}-%{version}/src/*.hrl
67
68
%changelog
69
-* Mon Jan 4 2016 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 0.1.5-1
70
-- Release 0.1.5
71
-
72
+* Wed Jun 08 2016 Aaron Seigo <seigo@kolabsystems.com> - 0.2.4-1
73
+- Packaging of 0.2.4
74
+* Tue Jun 07 2016 Aaron Seigo <seigo@kolabsystems.com> - 0.2.2-1
75
+- Packaging of 0.2.2
76
* Mon Dec 21 2015 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 0.1.2-1
77
- First package
78
0001-there-is-only-ever-one-response.-be-strict-about-tha.patch
Added
29
1
2
+From 297c7097833f10c70b4b3f928dbc6166282b947b Mon Sep 17 00:00:00 2001
3
+From: Aaron Seigo <aseigo@kde.org>
4
+Date: Mon, 20 Jun 2016 16:49:55 +0200
5
+Subject: [PATCH 1/3] there is only ever one response. be strict about that.
6
+
7
+---
8
+ src/commands/eimap_command_capability.erl | 4 ++--
9
+ 1 file changed, 2 insertions(+), 2 deletions(-)
10
+
11
+diff --git a/src/commands/eimap_command_capability.erl b/src/commands/eimap_command_capability.erl
12
+index 8f9d12e..a1b50c9 100644
13
+--- a/src/commands/eimap_command_capability.erl
14
++++ b/src/commands/eimap_command_capability.erl
15
+@@ -25,8 +25,8 @@
16
+ new_command(parse_serverid) -> { <<"CAPABILITY">>, single_line_response };
17
+ new_command(_Args) -> { <<"CAPABILITY">>, multiline_response }.
18
+
19
+-process_line(<<"* CAPABILITY ", Data/binary>>, Acc) ->
20
+- [Data|Acc];
21
++process_line(<<"* CAPABILITY ", Data/binary>>, _Acc) ->
22
++ [Data];
23
+ process_line(_Data, Acc) -> Acc.
24
+
25
+ formulate_response(ok, Response) -> { fini, Response };
26
+--
27
+2.5.5
28
+
29
0002-the-explicit-capabilities-command-is-multiline-so-it.patch
Added
28
1
2
+From ee6185b256058ee5884d01ac16faf30585480fbb Mon Sep 17 00:00:00 2001
3
+From: Aaron Seigo <aseigo@kde.org>
4
+Date: Mon, 20 Jun 2016 16:55:01 +0200
5
+Subject: [PATCH 2/3] the explicit capabilities command is multiline, so it
6
+ produces a list response
7
+
8
+---
9
+ src/eimap.erl | 2 +-
10
+ 1 file changed, 1 insertion(+), 1 deletion(-)
11
+
12
+diff --git a/src/eimap.erl b/src/eimap.erl
13
+index 76f02f2..ebf3a02 100644
14
+--- a/src/eimap.erl
15
++++ b/src/eimap.erl
16
+@@ -294,7 +294,7 @@ handle_info({ { connected, Receiver, ResponseToken }, { Capabilities, ServerID }
17
+ send_hello_string(Capabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver)
18
+ end,
19
+ { next_state, idle, State#state{ current_command = undefined, server_id = ServerID } };
20
+-handle_info({ { posttls_capabilities, Receiver, ResponseToken }, Capabilities }, _StateName, #state{ server_id = ServerID, passthrough = Passthrough, passthrough_recv = PassthroughReceiver } = State) ->
21
++handle_info({ { posttls_capabilities, Receiver, ResponseToken }, [Capabilities] }, _StateName, #state{ server_id = ServerID, passthrough = Passthrough, passthrough_recv = PassthroughReceiver } = State) ->
22
+ OurCapabilities =
23
+ case binary:match(Capabilities, <<"STARTTLS">>) of
24
+ nomatch -> <<Capabilities/binary, " STARTTLS">>;
25
+--
26
+2.5.5
27
+
28
0003-don-t-munge-the-incoming-response.-leave-that-to-the.patch
Added
33
1
2
+From bc9d05a44c7ebbfb7bbbcb1006b23d2bb76d8101 Mon Sep 17 00:00:00 2001
3
+From: Aaron Seigo <aseigo@kde.org>
4
+Date: Mon, 20 Jun 2016 16:55:28 +0200
5
+Subject: [PATCH 3/3] don't munge the incoming response. leave that to the
6
+ application.
7
+
8
+---
9
+ src/eimap.erl | 7 +------
10
+ 1 file changed, 1 insertion(+), 6 deletions(-)
11
+
12
+diff --git a/src/eimap.erl b/src/eimap.erl
13
+index ebf3a02..b331d1d 100644
14
+--- a/src/eimap.erl
15
++++ b/src/eimap.erl
16
+@@ -295,12 +295,7 @@ handle_info({ { connected, Receiver, ResponseToken }, { Capabilities, ServerID }
17
+ end,
18
+ { next_state, idle, State#state{ current_command = undefined, server_id = ServerID } };
19
+ handle_info({ { posttls_capabilities, Receiver, ResponseToken }, [Capabilities] }, _StateName, #state{ server_id = ServerID, passthrough = Passthrough, passthrough_recv = PassthroughReceiver } = State) ->
20
+- OurCapabilities =
21
+- case binary:match(Capabilities, <<"STARTTLS">>) of
22
+- nomatch -> <<Capabilities/binary, " STARTTLS">>;
23
+- _ -> Capabilities
24
+- end,
25
+- send_hello_string(OurCapabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver),
26
++ send_hello_string(Capabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver),
27
+ { next_state, idle, State#state{ current_command = none } };
28
+ handle_info({ { selected, MBox }, ok }, StateName, State) ->
29
+ %%lager:info("~p Selected mbox ~p", [self(), MBox]),
30
+--
31
+2.5.5
32
+
33
debian.changelog
Added
25
1
2
+erlang-eimap (0.2.4) unstable; urgency=medium
3
+
4
+ * Package the 0.2.4 release
5
+
6
+ -- Aaron Seigo <seigo@kolabsystems.com> Wed, 08 Jun 2016 10:30:50 +0100
7
+
8
+erlang-eimap (0.2.2) unstable; urgency=medium
9
+
10
+ * Package the 0.2.2 release
11
+
12
+ -- Aaron Seigo <seigo@kolabsystems.com> Tue, 07 Jun 2016 10:30:50 +0100
13
+
14
+erlang-eimap (0.2-1+git20160111~kolab1) unstable; urgency=medium
15
+
16
+ * Bump to new git version.
17
+
18
+ -- Christoph Erhardt <kolab@sicherha.de> Thu, 11 Feb 2016 21:09:49 +0100
19
+
20
+erlang-eimap (0.1.5-1~kolab1) unstable; urgency=medium
21
+
22
+ * Initial release.
23
+
24
+ -- Christoph Erhardt <kolab@sicherha.de> Sun, 07 Feb 2016 12:37:53 +0100
25
debian.control
Added
22
1
2
+Source: erlang-eimap
3
+Maintainer: Christoph Erhardt <kolab@sicherha.de>
4
+Section: devel
5
+Priority: extra
6
+Standards-Version: 3.9.6
7
+Homepage: https://git.kolab.org/diffusion/EI/eimap.git
8
+Build-Depends: debhelper (>= 9), erlang-goldrush (>= 0.1.7), erlang-lager (>= 2.2.0), rebar (>= 2.5.1)
9
+
10
+Package: erlang-eimap
11
+Architecture: any
12
+Section: devel
13
+Priority: extra
14
+Depends: ${misc:Depends},
15
+ erlang-base | ${erlang-abi:Depends},
16
+ erlang-goldrush,
17
+ erlang-lager,
18
+ ${erlang:Depends}
19
+Enhances: erlang
20
+Description: Erlang IMAP client
21
+ IMAP client library for Erlang
22
debian.rules
Added
21
1
2
+#!/usr/bin/make -f
3
+
4
+include /usr/share/dpkg/pkg-info.mk
5
+
6
+DESTDIR = $(CURDIR)/debian/erlang-eimap
7
+
8
+%:
9
+ dh $@
10
+
11
+override_dh_auto_build:
12
+ rebar compile -v
13
+
14
+override_dh_auto_install:
15
+ mkdir -p $(DESTDIR)/usr/lib/erlang/lib/eimap-$(DEB_VERSION_UPSTREAM)/ebin \
16
+ $(DESTDIR)/usr/lib/erlang/lib/eimap-$(DEB_VERSION_UPSTREAM)/src
17
+ install -D -m 644 ebin/eimap.app \
18
+ $(DESTDIR)/usr/lib/erlang/lib/eimap-$(DEB_VERSION_UPSTREAM)/ebin/eimap.app
19
+ install -D -m 644 ebin/*.beam \
20
+ $(DESTDIR)/usr/lib/erlang/lib/eimap-$(DEB_VERSION_UPSTREAM)/ebin/
21
debian.tar.gz
Added
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_examine.erl
Deleted
35
1
2
-%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
-%%
4
-%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
-%%
6
-%% This program is free software: you can redistribute it and/or modify
7
-%% it under the terms of the GNU General Public License as published by
8
-%% the Free Software Foundation, either version 3 of the License, or
9
-%% (at your option) any later version.
10
-%%
11
-%% This program is distributed in the hope that it will be useful,
12
-%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
-%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
-%% GNU General Public License for more details.
15
-%%
16
-%% You should have received a copy of the GNU General Public License
17
-%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
-
19
--module(eimap_command_examine).
20
--behavior(eimap_command).
21
--export([new/1, parse/2]).
22
-
23
-%% https://tools.ietf.org/html/rfc3501#section-6.3.2
24
-
25
-%% Public API
26
-new(MBox) when is_binary(MBox) -> <<"EXAMINE \"", MBox/binary, "\"">>.
27
-
28
-parse(Data, Tag) ->
29
- case eimap_utils:check_response_for_failure(Data, Tag) of
30
- ok -> { fini, ok };
31
- { _, Reason } -> { error, Reason }
32
- end.
33
-
34
-%% Private API
35
erlang-eimap-0.1.5.tar.gz/src/eimap.hrl
Deleted
4
1
2
--record(eimap_server_config, { host :: string(), port = 143 :: integer(), tls = false :: boolean() }).
3
-
4
erlang-eimap-0.1.5.tar.gz/README.md -> erlang-eimap-0.2.4.tar.gz/README.md
Changed
126
1
2
To use eimap from your imap application add the following line to your rebar
3
config:
4
5
- { eimap, "*", {git, "git://git.kolab.org/diffusion/EI/eimap.git" }
6
+ { eimap, "*", {git, "git://git.kolab.org/diffusion/EI/eimap.git" } }
7
8
There is no need to start the eimap application as it does not have any process
9
or startup routines related to its usage. eimap does rely on lager being avilable,
10
11
============
12
The eimap erlang module is the home of the primary API for the eimap library. It
13
is a gen_fsm and should be started as a process in the normal Erlang/OTP manner for
14
-use.
15
+use.
16
17
An eimap instance represents a single IMAP connection to a single IMAP server
18
and is stateful: commands that are started may change the selected folder, for
19
20
Once started, an eimap process may be directed to connect to an imap server
21
and then start with functions such as fetching path tokens:
22
23
- ImapServerArgs = #eimap_server_config{ host = "acme.com" },
24
+ ImapServerArgs = [ { host, "imap.acme.com" }, { port, 143 }, { tls, starttls } ]
25
{ ok, Imap } = eimap:start_link(ImapServerArgs),
26
eimap_imap:starttls(),
27
eimap_imap:login(Imap, self(), undefined, "username", "password"),
28
eimap_imap:connect(Imap),
29
eimap_imap:get_path_tokens(Imap, self(), get_path_tokens)
30
31
-The eimap_server_config record is defined in eimap.hrl and allows one to set
32
+The Imap server args is a simple proplist which allows one to set
33
host, port and TLS settings. For TLS, the following values are supported:
34
35
* true: start a TLS session when opening the socket ("implicit TLS")
36
37
The API for commands is defined in src/eimap_command.erl as a behavior. Commands
38
are expected to provide at least two functions:
39
40
- new(Args): create a command bitstring to be passed to the imap server
41
- Args is specific to the command, and some commands ignore this
42
- parameter
43
- parse(Data, Tag): parses a response from the imap server; the Tag is the
44
- IMAP command tag sent with the command and Data is the full
45
- Data buffer being process (possibly including tagged lines).
46
-
47
-Data to be parsed may contain multiple lines, and in the case of commands with
48
-large responses may only contain part of the response.
49
+ new_command(Args) -> { Command, ResponseType }
50
+ create a command bitstring to be passed to the imap server and defines
51
+ the type of response for this command. Response types include
52
+ single_line_response, multiline_response, all_multiline_response
53
+ and blob_response.
54
+
55
+ Args is specific to the command, and some commands
56
+ ignore this parameter
57
+
58
+Single line response
59
+--------------------
60
+Commands which the IMAP server will respond to with a single line in return
61
+should use single_line_response and must implement formulate_response/2 which
62
+is passed the Data and the command Tag. This usually returns one of
63
+{ fini, Result } or { error, Reason }, though some special commands may return
64
+atoms which the eimap module responds to such as starttls.
65
+
66
+Multiline Response
67
+------------------
68
+This is the most common response type and is used when the IMAP server may
69
+respond with zero or more untagged responses and a final tagged response.
70
+
71
+Such commands must implement:
72
+
73
+ process_line/2 which is passed one line at a time (minus newlines)
74
+ and a list accumulator to add responses to
75
+
76
+ formulate_response/2 which is passed ok on success or an error tuple of
77
+ { [no|bad], Reason }. This should return either { fini, Response } or
78
+ { error, Reason } in most cases; and for that there is
79
+ eimap_command:formulate_response. Which means that for most commands
80
+ formulate_response/2 is implemented as:
81
+
82
+ formulate_response(Result, Response) -> eimap_command:formulate(Result, Response).
83
+
84
+Commands which are all_multiline_responses also must implement process_tagged_line/2
85
+which behaves exactly like process_line but which accepts the tagged response line
86
+from the IMAP server. This is useful for commands where useful information is also
87
+passed in the tagged response line. An example of this is the select/exmine
88
+commands.
89
+
90
+Unstructured (blob) responses
91
+-----------------------------
92
+Responses that do not follow the usual untagged/tagged line response pattern
93
+may use the blog_response type and implement parse/2 which will be passed
94
+the data as it arrives. The command is responsible for all buffer stitching
95
+across network packets, etc.
96
97
In the case of partial responses, parse/2 may return { more, fun/3, State }.
98
The State object allows preserving the parsing state and will be passed back to
99
100
=======
101
Tests can be run with `make tests` or `rebar eunit`.
102
103
-All new commands must be accompanied by tests in the test/ directory. Currently
104
-testing is not remotely complete in coverage (a historical accident), and this
105
-needs to be rectified over time. New tests welcome.
106
+All new commands must be accompanied by tests in the test/ directory.
107
108
Contributing
109
============
110
111
Mailing list: devel@lists.kolab.com
112
Project page: https://git.kolab.org/tag/eimap/
113
114
+This project uses the git flow workflow as described here:
115
+
116
+ http://nvie.com/posts/a-successful-git-branching-model/
117
+
118
+You can installed git flow from here:
119
+
120
+ https://github.com/nvie/gitflow
121
+
122
+Then initialize your local clone with `git flow init -d`.
123
+
124
You can find the list of open tasks on the project page's workboard. Anything
125
in the backlog is open to be worked on.
126
erlang-eimap-0.1.5.tar.gz/rebar.config -> erlang-eimap-0.2.4.tar.gz/rebar.config
Changed
26
1
2
%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
3
%% ex: ft=erlang ts=4 sw=4 et
4
5
-{deps_dir, "deps"}.
6
{deps, [
7
- { lager, ".*", { git, "https://github.com/basho/lager.git", { tag, "2.1.0" } } }
8
+ { lager, ".*", { git, "git://github.com/basho/lager.git" } }
9
]}.
10
11
-{erl_opts, [ %%no_debug_info,
12
- {parse_transform, lager_transform},
13
- {platform_define, "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'}
14
- ]}.
15
+{erl_opts, [warnings_as_errors, {parse_transform, lager_transform}]}.
16
{ erl_first_files, ["src/eimap_command.erl"] }.
17
{sub_dirs, [
18
"src",
19
"rel",
20
"tests"
21
]}.
22
-{erl_opts, [debug_info, fail_on_warning]}.
23
{cover_enabled, true}.
24
25
%%{require_otp_vsn, "17"}.
26
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_annotation.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_annotation.erl
Changed
67
1
2
3
-module(eimap_command_annotation).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
%% Public API
9
-new(Folder) when is_binary(Folder) ->
10
- <<"GETANNOTATION \"", Folder/binary, "\" \"*\" \"value.shared\"">>.
11
+new_command(Folder) when is_list(Folder) -> new_command(list_to_binary(Folder));
12
+new_command(Folder) -> { <<"GETANNOTATION \"", Folder/binary, "\" \"*\" \"value.shared\"">>, multiline_response }.
13
14
-parse(Data, Tag) when is_binary(Data) ->
15
- case eimap_utils:check_response_for_failure(Data, Tag) of
16
- ok ->
17
- Lines = binary:split(Data, <<"\r\n">>, [global]),
18
- Rv = lists:foldl(fun(Line, Acc) -> parseLine(Line, Acc, Tag) end, [], Lines),
19
- { fini, Rv };
20
- { _, Reason } ->
21
- { error, Reason }
22
- end.
23
-
24
-
25
-%% Private API
26
-parseLine(<<" ", Data/binary>>, Acc, Tag) ->
27
- parseLine(Data, Acc, Tag);
28
-parseLine(<<"* ANNOTATION ", Data/binary>>, Acc, _Tag) ->
29
+process_line(<<" ", Data/binary>>, Acc) ->
30
+ process_line(Data, Acc);
31
+process_line(<<"* ANNOTATION ", Data/binary>>, Acc) ->
32
Pieces = binary:split(Data, <<"\"">>, [global]),
33
process_pieces(Pieces, Acc);
34
-parseLine(<<>>, Acc, _Tag) ->
35
- Acc;
36
-parseLine(Data, Acc, Tag) ->
37
- case binary:match(Data, Tag) of
38
- { 0, End } -> handle_possible_end(binary:part(Data, End + 1, byte_size(Data) - End - 1), Acc);
39
- _ -> lager:warning("Unexpected response from imap server: ~p", [Data]), Acc
40
- end.
41
+process_line(<<>>, Acc) ->
42
+ Acc.
43
+
44
+formulate_response(Result, Data) -> eimap_command:formulate_response(Result, Data).
45
46
+%% Private API
47
process_pieces([MBox, Key, _, _, _, Value, _], Acc) when MBox =/= <<>> -> [ { Key, translate(Value) } | Acc ];
48
process_pieces([_, _MBox, _, Key, _, _, _, Value, _], Acc) -> [ { Key, translate(Value) } | Acc ];
49
process_pieces(_, Acc) -> Acc.
50
51
_:_ -> Value
52
end.
53
54
-handle_possible_end(<<"OK", _/binary>>, Acc) ->
55
- Acc;
56
-handle_possible_end(<<"NO ", Reason/binary>>, Acc) ->
57
- lager:warning("Annotation error from imap server: ~p", [Reason]),
58
- Acc;
59
-handle_possible_end(<<"BAD ", Reason/binary>>, Acc) ->
60
- lager:warning("Annotation error from imap server: ~p", [Reason]),
61
- Acc;
62
-handle_possible_end([_Tag, Message], Acc) ->
63
- handle_possible_end(Message, Acc);
64
-handle_possible_end(Message, Acc) ->
65
- lager:warning("Unexpected response from imap server: ~p", [Message]),
66
- Acc.
67
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_capability.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_capability.erl
Changed
47
1
2
3
-module(eimap_command_capability).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new(_Args) -> <<"CAPABILITY">>.
12
+new_command(parse_serverid) -> { <<"CAPABILITY">>, single_line_response };
13
+new_command(_Args) -> { <<"CAPABILITY">>, multiline_response }.
14
15
-parse(Data, Tag) -> formulate_reponse(eimap_utils:check_response_for_failure(Data, Tag),
16
- eimap_utils:remove_tag_from_response(Data, Tag, check)).
17
+process_line(<<"* CAPABILITY ", Data/binary>>, Acc) ->
18
+ [Data|Acc];
19
+process_line(_Data, Acc) -> Acc.
20
21
+formulate_response(ok, Response) -> { fini, Response };
22
+formulate_response({ _, Reason }, _Data) -> { error, Reason };
23
+formulate_response(Data, Tag) -> parse_oneliner(eimap_utils:check_response_for_failure(Data, Tag),
24
+ eimap_utils:remove_tag_from_response(Data, Tag, check)).
25
26
%% Private API
27
% TODO: probably way too cyrus imap specific on the responses (capitalization, etc)
28
% make generic with a nicer parser
29
-formulate_reponse(ok, <<"* OK [CAPABILITY ", Data/binary>>) ->
30
+parse_oneliner(ok, <<"* OK [CAPABILITY ", Data/binary>>) ->
31
% this is a server response on connect
32
{ End, _ } = binary:match(Data, <<"]">>),
33
{ TextEnd, _ } = binary:match(Data, <<"\r\n">>),
34
Capabilities = binary:part(Data, { 0, End }),
35
ServerID = binary:part(Data, { End + 2, TextEnd - End - 2}),
36
{ fini, { Capabilities, ServerID } };
37
-formulate_reponse(ok, <<"* CAPABILITY ", Data/binary>>) ->
38
- { End, _ } = binary:match(Data, <<"\r\n">>),
39
- Capabilities = binary:part(Data, { 0, End }),
40
- { fini, Capabilities };
41
-formulate_reponse(ok, Data) -> Data;
42
-formulate_reponse({ _, Reason }, _Data) -> { error, Reason }.
43
+parse_oneliner({ _, Reason }, _Data) -> { error, Reason };
44
+parse_oneliner(_, Data) -> { fini, Data }.
45
+
46
47
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_compress.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_compress.erl
Changed
26
1
2
3
-module(eimap_command_compress).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
+-export([new_command/1, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new(_Args) -> <<"COMPRESS DEFLATE">>.
12
+new_command(_Args) -> { <<"COMPRESS DEFLATE">>, single_line_response }.
13
14
-parse(Data, Tag) -> formulate_reponse(eimap_utils:check_response_for_failure(Data, Tag)).
15
-
16
-
17
-%% Private API
18
-formulate_reponse(ok) -> compression_active;
19
-formulate_reponse({ _, Reason }) -> { error, Reason }.
20
+formulate_response(Data, Tag) ->
21
+ case eimap_utils:check_response_for_failure(Data, Tag) of
22
+ ok -> compression_active;
23
+ { _, Reason } -> { error, Reason }
24
+ end.
25
26
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_getmetadata.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_getmetadata.erl
Changed
75
1
2
3
-module(eimap_command_getmetadata).
4
-behavior(eimap_command).
5
--export([new/1, parse/2, continue_parse/3]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new({ Folder }) -> new({ Folder, [] });
12
-new({ Folder, Attributes }) when is_list(Folder) -> new({ list_to_binary(Folder), Attributes });
13
-new({ Folder, Attributes }) ->
14
- AttributesList = format_attributes(Attributes, <<>>),
15
- Command = <<"GETMETADATA (DEPTH infinity) \"", Folder/binary, "\"", AttributesList/binary>>,
16
- Command.
17
-
18
-parse(Data, Tag) -> continue_parse(Data, Tag, { <<>>, [] }).
19
-
20
-continue_parse(Data, Tag, { LastPartialLine, Acc }) ->
21
- FullBuffer = <<LastPartialLine/binary, Data/binary>>,
22
- { FullLinesBuffer, NewLastPartialLine } = eimap_utils:only_full_lines(FullBuffer),
23
- Lines = binary:split(FullLinesBuffer, <<"\r\n">>, [global]),
24
- process_line(Tag, NewLastPartialLine, Lines, Acc).
25
+new_command({ Folder }) -> new_command({ Folder, [] });
26
+new_command({ Folder, Attributes }) -> new_command({ Folder, Attributes, infinity, nomax });
27
+
28
+%% Depth and MaxSize are both optional arguments, so we have a 4-tuple version of new_command to accomodate this
29
+%% "Depth, Maxsize" could be replaced with a proplist of the form [ { depth, Depth }, { maxsize, MaxSize } ] but
30
+%% as this is an internal (to eimap) API there is little benefit for the performance cost
31
+new_command({ Folder, Attributes, Depth, MaxSize }) when is_list(Folder) -> new_command({ list_to_binary(Folder), Attributes, Depth, MaxSize });
32
+new_command({ Folder, Attributes, Depth, MaxSize }) ->
33
+ AttributesString = format_attributes(Attributes, <<>>),
34
+ DepthString = depth_param(Depth),
35
+ MaxSizeString = maxsize_param(MaxSize),
36
+ Command = metadata_command(DepthString, MaxSizeString, Folder, AttributesString),
37
+ { Command, multiline_response }.
38
+
39
+process_line(<<"* METADATA ", Details/binary>>, Acc) ->
40
+ Results = parse_folder(Details),
41
+ [Results|Acc];
42
+process_line(_Line, Acc) -> Acc.
43
44
+formulate_response(Result, Data) -> eimap_command:formulate_response(Result, Data).
45
46
%% Private API
47
-process_line(_Tag, LastPartialLine, [], Acc) -> { more, fun ?MODULE:continue_parse/3, { LastPartialLine, Acc } };
48
-process_line(Tag, LastPartialLine, [Line|MoreLines], Acc) ->
49
- case eimap_utils:is_tagged_response(Line, Tag) of
50
- true ->
51
- formulate_response(eimap_utils:check_response_for_failure(Line, Tag), Acc);
52
- false ->
53
- process_line(Tag, LastPartialLine, MoreLines, process_metadata_response(Line, Acc))
54
- end.
55
+depth_param(infinity) -> <<"DEPTH infinity">>;
56
+depth_param(Depth) when is_integer(Depth) -> Bin = integer_to_binary(Depth), <<"DEPTH ", Bin/binary>>;
57
+depth_param(_) -> <<>>.
58
59
-formulate_response(ok, Data) -> { fini, Data };
60
-formulate_response({ _, Reason }, _Data) -> { error, Reason }.
61
+maxsize_param(Size) when is_integer(Size) -> Bin = integer_to_binary(Size), <<"MAXSIZE ", Bin/binary>>;
62
+maxsize_param(_) -> <<>>.
63
64
-process_metadata_response(<<"* METADATA ", Details/binary>>, Acc) ->
65
- Results = parse_folder(Details),
66
- [Results|Acc];
67
-process_metadata_response(_Line, Acc) -> Acc.
68
+metadata_command(<<>>, <<>>, Folder, Attributes) -> <<"GETMETADATA \"", Folder/binary, "\"", Attributes/binary>>;
69
+metadata_command(Depth, <<>>, Folder, Attributes) -> <<"GETMETADATA (", Depth/binary, ") \"", Folder/binary, "\"", Attributes/binary>>;
70
+metadata_command(<<>>, MaxSize, Folder, Attributes) -> <<"GETMETADATA (", MaxSize/binary, ") \"", Folder/binary, "\"", Attributes/binary>>;
71
+metadata_command(Depth, MaxSize, Folder, Attributes) -> <<"GETMETADATA (", Depth/binary, " ", MaxSize/binary, ") \"", Folder/binary, "\"", Attributes/binary>>.
72
73
format_attributes([], <<>>) -> <<>>;
74
format_attributes([], String) -> <<" (", String/binary, ")">>;
75
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_login.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_login.erl
Changed
27
1
2
3
-module(eimap_command_login).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new({ User, Password }) ->
12
+new_command({ User, Password }) ->
13
UserBin = eimap_utils:ensure_binary(User),
14
PasswordBin = eimap_utils:ensure_binary(Password),
15
- <<"LOGIN ", UserBin/binary, " ", PasswordBin/binary>>.
16
+ { <<"LOGIN ", UserBin/binary, " ", PasswordBin/binary>>, multiline_response }.
17
18
-parse(Data, Tag) -> formulate_reponse(eimap_utils:check_response_for_failure(Data, Tag)).
19
+process_line(_Data, Acc) -> Acc.
20
21
%% Private API
22
-formulate_reponse(ok) -> { fini, authed };
23
-formulate_reponse({ _, Reason }) -> { error, Reason }.
24
+formulate_response(ok, _Acc) -> { fini, authed };
25
+formulate_response({ _, Reason }, _Acc) -> { error, Reason }.
26
27
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_logout.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_logout.erl
Changed
24
1
2
3
-module(eimap_command_logout).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
-
7
-%% http://tools.ietf.org/html/rfc2342
8
+-export([new_command/1, process_line/2, formulate_response/2]).
9
10
%% Public API
11
-new(_Args) -> <<"LOGOUT">>.
12
-
13
-parse(Data, Tag) -> formulate_reponse(eimap_utils:check_response_for_failure(Data, Tag)).
14
+new_command(_Args) -> { <<"LOGOUT">>, multiline_response }.
15
16
+process_line(_Data, Acc) -> Acc.
17
18
-%% Private API
19
-formulate_reponse(ok) -> { close_socket, ok };
20
-formulate_reponse({ _, Reason }) -> { close_socket, { error, Reason } }.
21
+formulate_response(ok, _Data) -> { close_socket, ok };
22
+formulate_response({ _, Reason }, _Data) -> { close_socket, { error, Reason } }.
23
24
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_namespace.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_namespace.erl
Changed
51
1
2
3
-module(eimap_command_namespace).
4
-behavior(eimap_command).
5
--export([new/1, parse/2, continue_parse/3]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new(_Args) -> <<"NAMESPACE">>.
12
+new_command(_Args) -> { <<"NAMESPACE">>, multiline_response }.
13
14
-%TODO support multi-packet continuing parse
15
-parse(Data, Tag) -> continue_parse(Data, Tag, []).
16
+process_line(<<"* NAMESPACE ", Data/binary>>, Acc) -> [process_shared_prefix_parts(Data, 1)|Acc];
17
+process_line(_Line, Acc) -> Acc.
18
19
-continue_parse(Data, Tag, Acc) ->
20
- Lines = binary:split(Data, <<"\r\n">>, [global]), %%TODO partial lines
21
- process_line(Tag, Lines, Acc).
22
23
-
24
-%% Private API
25
-process_line(_Tag, [], Acc) -> { more, fun ?MODULE:continue_parse/3, Acc };
26
-process_line(Tag, [Line|MoreLines], Acc) ->
27
- case eimap_utils:is_tagged_response(Line, Tag) of
28
- true ->
29
- formulate_response(eimap_utils:check_response_for_failure(Line, Tag), Acc);
30
- false ->
31
- process_line(Tag, MoreLines, [process_shared_prefix_parts(Line)|Acc])
32
- end.
33
-
34
-%TODO: multiline!
35
formulate_response(ok, [Acc]) -> { fini, relevant_shared_prefix_parts(Acc) };
36
formulate_response({ _, Reason }, _Acc) -> { error, Reason }.
37
38
+% Private API
39
relevant_shared_prefix_parts([]) -> { none, none};
40
relevant_shared_prefix_parts([[], [], _]) -> { none, none };
41
relevant_shared_prefix_parts([[], Delim]) -> { none, Delim };
42
relevant_shared_prefix_parts([SharedPrefix, [], _]) -> { SharedPrefix, "/" };
43
relevant_shared_prefix_parts([SharedPrefix, Delim]) -> { SharedPrefix, Delim }.
44
45
-process_shared_prefix_parts(<<"* NAMESPACE ", Data/binary>>) -> process_shared_prefix_parts(Data, 1);
46
-process_shared_prefix_parts(_) -> [].
47
-
48
process_shared_prefix_parts(<<"NIL ", Data/binary>>, PartNumber) ->
49
process_shared_prefix_parts(Data, PartNumber + 1);
50
process_shared_prefix_parts(<<"NIL", Data/binary>>, PartNumber) ->
51
erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_noop.erl
Added
32
1
2
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
+%%
4
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
+%%
6
+%% This program is free software: you can redistribute it and/or modify
7
+%% it under the terms of the GNU General Public License as published by
8
+%% the Free Software Foundation, either version 3 of the License, or
9
+%% (at your option) any later version.
10
+%%
11
+%% This program is distributed in the hope that it will be useful,
12
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+%% GNU General Public License for more details.
15
+%%
16
+%% You should have received a copy of the GNU General Public License
17
+%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+-module(eimap_command_noop).
20
+-behavior(eimap_command).
21
+-export([new_command/1, process_line/2, formulate_response/2]).
22
+
23
+%% https://tools.ietf.org/html/rfc3501#section-6.3.2
24
+
25
+%% Public API
26
+new_command(_) -> { <<"NOOP">>, multiline_response }.
27
+
28
+process_line(Data, Acc) -> eimap_command:process_status_line(Data, Acc).
29
+
30
+formulate_response(Response, Acc) -> eimap_command:formulate_response(Response, Acc).
31
+
32
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_peek_message.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_peek_message.erl
Changed
20
1
2
3
-module(eimap_command_peek_message).
4
-behavior(eimap_command).
5
--export([new/1, parse/2, continue_parse/3]).
6
+-export([new_command/1, parse/2, continue_parse/3]).
7
-record(parse_state, { body_size, parts, data }).
8
-record(parts, { headers = <<"">>, body = <<"">>, flags = <<"">> }).
9
10
%% https://tools.ietf.org/html/rfc3501#section-6.4.5
11
12
%% Public API
13
-new(MessageID) when is_integer(MessageID) -> new(integer_to_binary(MessageID));
14
-new(MessageID) when is_binary(MessageID) -> <<"UID FETCH ", MessageID/binary, " (FLAGS BODY.PEEK[HEADER] BODY.PEEK[TEXT])">>.
15
+new_command(MessageID) when is_integer(MessageID) -> new_command(integer_to_binary(MessageID));
16
+new_command(MessageID) when is_binary(MessageID) -> { <<"UID FETCH ", MessageID/binary, " (FLAGS BODY.PEEK[HEADER] BODY.PEEK[TEXT])">>, blob_response }.
17
18
continue_parse(Data, _Tag, #parse_state{ body_size = Size, parts = Parts, data = PrevData }) ->
19
try_body_parse(<<PrevData/binary, Data/binary>>, Size, Parts).
20
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_starttls.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_starttls.erl
Changed
26
1
2
3
-module(eimap_command_starttls).
4
-behavior(eimap_command).
5
--export([new/1, parse/2]).
6
+-export([new_command/1, formulate_response/2]).
7
8
%% http://tools.ietf.org/html/rfc2342
9
10
%% Public API
11
-new(_Args) -> <<"STARTTLS">>.
12
+new_command(_Args) -> { <<"STARTTLS">>, single_line_response }.
13
14
-parse(Data, Tag) -> formulate_reponse(eimap_utils:check_response_for_failure(Data, Tag)).
15
-
16
-
17
-%% Private API
18
-formulate_reponse(ok) -> starttls;
19
-formulate_reponse({ _, Reason }) -> { error, Reason }.
20
+formulate_response(Data, Tag) ->
21
+ case eimap_utils:check_response_for_failure(Data, Tag) of
22
+ ok -> starttls;
23
+ { _, Reason } -> { error, Reason }
24
+ end.
25
26
erlang-eimap-0.1.5.tar.gz/src/commands/eimap_command_status.erl -> erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_status.erl
Changed
67
1
2
3
-module(eimap_command_status).
4
-behavior(eimap_command).
5
--export([new/1, parse/2, continue_parse/3]).
6
+-export([new_command/1, process_line/2, formulate_response/2]).
7
8
% https://tools.ietf.org/html/rfc5464
9
10
% supported attributes: messages, recent, uidnext, uidvalidity, unseen, annotate
11
% Public API
12
-new({ Folder, Attributes }) when is_list(Folder) -> new({ list_to_binary(Folder), Attributes });
13
-new({ <<>>, Attributes}) -> new({ <<"INBOX">>, Attributes });
14
-new({ Folder, []}) -> new({ Folder, [messages] });
15
-new({ Folder, Attributes }) when is_list(Attributes) ->
16
- AttributesString = attribute_string(Attributes, <<>>),
17
- <<"STATUS ", Folder/binary, " (", AttributesString/binary, ")">>.
18
+new_command({ Folder, Attributes }) when is_list(Folder) -> new_command({ list_to_binary(Folder), Attributes });
19
+new_command({ <<>>, Attributes}) -> new_command({ <<"INBOX">>, Attributes });
20
+new_command({ Folder, []}) -> new_command({ Folder, [messages] });
21
+new_command({ Folder, Attributes }) when is_list(Attributes) ->
22
+ AttributesString = attribute_string(Attributes, <<>>),
23
+ { <<"STATUS ", Folder/binary, " (", AttributesString/binary, ")">>, multiline_response }.
24
25
-parse(Data, Tag) -> continue_parse(Data, Tag, []).
26
-
27
-continue_parse(Data, Tag, Acc) ->
28
- Lines = binary:split(Data, <<"\r\n">>, [global]),
29
- process_line(Tag, Lines, Acc).
30
+process_line(<<"* STATUS ", Data/binary>>, Acc) ->
31
+ process_status_items(binary:match(Data, <<"(">>),
32
+ binary:match(Data, <<")">>),
33
+ Data, Acc);
34
+process_line(_, Acc) -> Acc.
35
36
+formulate_response(Result, Data) -> eimap_command:formulate_response(Result, Data).
37
38
%% Private API
39
attribute_string([], <<>>) -> attribute_string(messages);
40
41
Attr -> attribute_string(Attributes, <<String/binary, " ", Attr/binary>>)
42
end.
43
44
-process_line(_Tag, [], Acc) -> { more, fun ?MODULE:continue_parse/3, Acc };
45
-process_line(Tag, [Line|MoreLines], Acc) ->
46
- case eimap_utils:is_tagged_response(Line, Tag) of
47
- true ->
48
- formulate_response(eimap_utils:check_response_for_failure(Line, Tag), Acc);
49
- false ->
50
- process_line(Tag, MoreLines, process_status_items(Line, Acc))
51
- end.
52
-
53
-%TODO: multiline, partial lines
54
-formulate_response(ok, Acc) -> { fini, Acc };
55
-formulate_response({ _, Reason }, _Acc) -> { error, Reason }.
56
-
57
-process_status_items(<<"* STATUS ", Data/binary>>, Acc) ->
58
- process_status_items(binary:match(Data, <<"(">>),
59
- binary:match(Data, <<")">>),
60
- Data, Acc);
61
-process_status_items(_, Acc) -> Acc.
62
-
63
+% Private API
64
process_status_items(nomatch, _, _Data, Acc) -> Acc;
65
process_status_items(_, nomatch, _Data, Acc) -> Acc;
66
process_status_items({ Start, _ }, { End, _ }, Data, Acc) ->
67
erlang-eimap-0.2.4.tar.gz/src/commands/eimap_command_switch_folder.erl
Added
67
1
2
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
+%%
4
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
+%%
6
+%% This program is free software: you can redistribute it and/or modify
7
+%% it under the terms of the GNU General Public License as published by
8
+%% the Free Software Foundation, either version 3 of the License, or
9
+%% (at your option) any later version.
10
+%%
11
+%% This program is distributed in the hope that it will be useful,
12
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+%% GNU General Public License for more details.
15
+%%
16
+%% You should have received a copy of the GNU General Public License
17
+%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+-module(eimap_command_switch_folder).
20
+-behavior(eimap_command).
21
+-export([new_command/1, process_line/2, process_tagged_line/2, formulate_response/2]).
22
+
23
+%% https://tools.ietf.org/html/rfc3501#section-6.3.2
24
+
25
+%% Public API
26
+new_command({ MBox, Mechanism }) when is_list(MBox) -> new_command({ list_to_binary(MBox), Mechanism });
27
+new_command({ MBox, select }) -> new_command(MBox);
28
+new_command({ MBox, examine }) -> { <<"EXAMINE \"", MBox/binary, "\"">>, all_multiline_response };
29
+new_command(MBox) when is_list(MBox) -> new_command(list_to_binary(MBox));
30
+new_command(MBox) when is_binary(MBox) -> { <<"SELECT \"", MBox/binary, "\"">>, all_multiline_response }.
31
+
32
+%TODO: parse:
33
+% REQUIRED untagged responses: FLAGS, EXISTS, RECENT
34
+% REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, UIDNEXT, UIDVALIDITY
35
+process_line(<<"* OK [", Info/binary>>, Acc) ->
36
+ case binary:match(Info, <<"]">>) of
37
+ nomatch -> Acc;
38
+ { ClosingBracket, _ } -> process_ok_response(binary:part(Info, 0, ClosingBracket), Acc)
39
+ end;
40
+process_line(<<"* FLAGS ", FlagString/binary>>, Acc) ->
41
+ Flags = eimap_utils:parse_flags(FlagString),
42
+ [{ flags, Flags }|Acc];
43
+process_line(<<"* ", Info/binary>>, Acc) ->
44
+ case binary:split(Info, <<" ">>) of
45
+ [ Value, Key ] -> [{ eimap_utils:binary_to_atom(Key), binary_to_integer(Value) }|Acc];
46
+ _ ->Acc
47
+ end;
48
+process_line(_Data, Acc) -> Acc.
49
+
50
+process_tagged_line(Data, Acc) ->
51
+ case binary:match(Data, <<"[READ-WRITE]">>) of
52
+ nomatch -> [{ writeable, false }|Acc];
53
+ _ -> [{ writeable, true}|Acc]
54
+ end.
55
+
56
+formulate_response(Response, Acc) -> eimap_command:formulate_response(Response, Acc).
57
+
58
+%PRIVATE
59
+process_ok_response(<<"PERMANENTFLAGS ", FlagString/binary>>, Acc) -> [{ permanent_flags, eimap_utils:parse_flags(FlagString) }|Acc];
60
+process_ok_response(<<"UIDVALIDITY ", String/binary>>, Acc) -> [{ uid_validity, binary_to_integer(String) }|Acc];
61
+process_ok_response(<<"UIDNEXT ", String/binary>>, Acc) -> [{ uid_next, binary_to_integer(String) }|Acc];
62
+process_ok_response(<<"HIGHESTMODSEQ ", String/binary>>, Acc) -> [{ highest_mod_seq, binary_to_integer(String) }|Acc];
63
+process_ok_response(<<"URLMECH ", String/binary>>, Acc) -> [{ url_mech, eimap_utils:binary_to_atom(String) }|Acc]; %TODO: this is very ugly
64
+process_ok_response(<<"ANNOTATIONS ", String/binary>>, Acc) -> [{ annotations, binary_to_integer(String) }|Acc];
65
+process_ok_response(String, Acc) -> lager:warning("eimap_command_select_folder: Unknown untagged OK response ~s~n", [String]), Acc.
66
+
67
erlang-eimap-0.1.5.tar.gz/src/eimap.app.src -> erlang-eimap-0.2.4.tar.gz/src/eimap.app.src
Changed
10
1
2
{application, eimap,
3
[
4
{ description, "IMAP client implementation" },
5
- { vsn, "0.1.5" },
6
+ { vsn, "0.2.4" },
7
{ registered, [] },
8
{ applications, [
9
kernel,
10
erlang-eimap-0.1.5.tar.gz/src/eimap.erl -> erlang-eimap-0.2.4.tar.gz/src/eimap.erl
Changed
487
1
2
3
-module(eimap).
4
-behaviour(gen_fsm).
5
--include("eimap.hrl").
6
7
%% API
8
-export([start_link/1,
9
10
capabilities/3,
11
login/5, logout/3,
12
compress/1,
13
- get_server_metadata/4,
14
+ get_server_metadata/4, get_server_metadata/6,
15
get_folder_status/5,
16
- get_folder_metadata/5,
17
+ get_folder_metadata/5, get_folder_metadata/7,
18
get_folder_annotations/4,
19
- get_message_headers_and_body/5,
20
- get_path_tokens/3]).
21
+ peek_message_headers_and_body/5,
22
+ get_path_tokens/3,
23
+ noop/3]).
24
25
%% gen_fsm callbacks
26
-export([disconnected/2, idle/2, passthrough/2, wait_response/2, startingtls/2]).
27
28
%% state record definition
29
-record(state, { host, port, tls, tls_state = false, socket, server_id = <<>>,
30
command_serial = 1, command_queue = queue:new(),
31
- current_command, current_mbox, parse_state,
32
+ current_command, current_mbox,
33
passthrough = false, passthrough_recv, passthrough_send_buffer = <<>>,
34
- inflator, deflator}).
35
--record(command, { tag, mbox, message, from, response_token, parse_fun }).
36
+ inflator, deflator, process_command_queue_guard = false }).
37
+-record(command, { tag, mbox, message, from, response_type, response_token, parse_state }).
38
39
-define(SSL_UPGRADE_TIMEOUT, 5000).
40
-define(TCP_CONNECT_TIMEOUT, 5000).
41
42
--export([test/0]).
43
-test() ->
44
- ServerConfig = #eimap_server_config{ host = "192.168.56.101", port = 143, tls = false },
45
- { ok, Conn } = start_link(ServerConfig),
46
- login(Conn, self(), undefined, "doe", "doe"),
47
- get_folder_metadata(Conn, self(), undefined, "*", ["/shared/vendor/kolab/folder-type"]),
48
- logout(Conn, self(), undefined),
49
- connect(Conn).
50
-
51
-
52
%% public API
53
-start_link(ServerConfig) when is_record(ServerConfig, eimap_server_config) -> gen_fsm:start_link(?MODULE, ServerConfig, []).
54
+start_link(Options) when is_list(Options) -> gen_fsm:start_link(?MODULE, Options, []).
55
56
start_passthrough(PID, Receiver) when is_pid(Receiver) -> gen_fsm:send_all_state_event(PID, { start_passthrough, Receiver } ).
57
stop_passthrough(PID) -> gen_fsm:send_all_state_event(PID, stop_passthrough).
58
59
connect(PID, From, ResponseToken) -> gen_fsm:send_all_state_event(PID, { connect, From, ResponseToken }).
60
disconnect(PID) -> gen_fsm:send_all_state_event(PID, disconnect).
61
62
-compress(PID) when is_pid(PID) ->
63
- Command = #command{ message = eimap_command_compress:new(ok),
64
- from = PID, response_token = compress,
65
- parse_fun = fun eimap_command_compress:parse/2 },
66
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
67
+-spec compress(EImap :: pid()) -> ok.
68
+compress(EImap) when is_pid(EImap) ->
69
+ send_command_to_queue(EImap, EImap, compress, eimap_command_compress, ok).
70
71
-starttls(PID, From, ResponseToken) when is_pid(PID) ->
72
- Command = #command{ message = eimap_command_starttls:new(ok),
73
- from = From, response_token = ResponseToken,
74
- parse_fun = fun eimap_command_starttls:parse/2 },
75
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
76
+-spec starttls(EImap :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
77
+starttls(EImap, From, ResponseToken) when is_pid(EImap) ->
78
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_starttls, ok).
79
80
-capabilities(PID, From, ResponseToken) when is_pid(PID) ->
81
- Command = #command{ message = eimap_command_capability:new(noparams),
82
- from = From, response_token = ResponseToken,
83
- parse_fun = fun eimap_command_capability:parse/2 },
84
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
85
+-spec capabilities(EImap :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
86
+capabilities(EImap, From, ResponseToken) when is_pid(EImap) ->
87
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_capability, ok).
88
89
--spec login(PID :: pid(), From :: pid(), ResponseToken :: any(), User :: list() | binary(), Pass :: list() | binary()) -> ok.
90
-login(PID, From, ResponseToken, User, Pass) ->
91
- Command = #command{ message = eimap_command_login:new({ User, Pass }),
92
- from = From, response_token = ResponseToken,
93
- parse_fun = fun eimap_command_login:parse/2 },
94
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
95
+-spec login(EImap :: pid(), From :: pid(), ResponseToken :: any(), User :: list() | binary(), Pass :: list() | binary()) -> ok.
96
+login(EImap, From, ResponseToken, User, Pass) ->
97
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_login, { User, Pass }).
98
99
--spec logout(PID :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
100
-logout(PID, From, ResponseToken) ->
101
- Command = #command{ message = eimap_command_logout:new(ok),
102
- from = From, response_token = ResponseToken,
103
- parse_fun = fun eimap_command_logout:parse/2 },
104
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
105
+-spec logout(EImap :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
106
+logout(EImap, From, ResponseToken) ->
107
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_logout, ok).
108
109
-type status_property() :: messages | recent | uidnext | uidvalidity | unseen.
110
-type status_properties() :: [status_property()].
111
--spec get_folder_status(PID :: pid(), From :: pid(), ResponseToken :: any(), Folder :: list() | binary(), Properties:: status_properties()) -> ok.
112
-get_folder_status(PID, From, ResponseToken, Folder, Properties) ->
113
- Command = #command{ message = eimap_command_status:new({ Folder, Properties }),
114
- from = From, response_token = ResponseToken,
115
- parse_fun = fun eimap_command_status:parse/2 },
116
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
117
+-spec get_folder_status(EImap :: pid(), From :: pid(), ResponseToken :: any(), Folder :: list() | binary(), Properties:: status_properties()) -> ok.
118
+get_folder_status(EImap, From, ResponseToken, Folder, Properties) ->
119
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_status, { Folder, Properties }).
120
121
--spec get_folder_metadata(PID :: pid(), From :: pid(), ResponseToken :: any(), Folder :: list() | binary(), Properties:: [list() | binary()]) -> ok.
122
-get_folder_metadata(PID, From, ResponseToken, Folder, Properties) ->
123
- Command = #command{ message = eimap_command_getmetadata:new({ Folder, Properties}),
124
- from = From, response_token = ResponseToken,
125
- parse_fun = fun eimap_command_getmetadata:parse/2 },
126
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
127
+-spec get_folder_metadata(EImap :: pid(), From :: pid(), ResponseToken :: any(), Folder :: list() | binary(), Properties:: [list() | binary()]) -> ok.
128
+get_folder_metadata(EImap, From, ResponseToken, Folder, Properties) ->
129
+ get_folder_metadata(EImap, From, ResponseToken, Folder, Properties, infinity, nomax).
130
131
--spec get_server_metadata(PID :: pid(), From :: pid(), ResponseToken :: any(), Properties:: [list() | binary()]) -> ok.
132
-get_server_metadata(PID, From, ResponseToken, Properties) ->
133
- Command = #command{ message = eimap_command_getmetadata:new({ <<>>, Properties}),
134
- from = From, response_token = ResponseToken,
135
- parse_fun = fun eimap_command_getmetadata:parse/2 },
136
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
137
+-spec get_folder_metadata(EImap :: pid(), From :: pid(), ResponseToken :: any(), Folder :: list() | binary(), Properties:: [list() | binary()], Depth :: infinity | integer(), MaxSize :: nomax | integer()) -> ok.
138
+get_folder_metadata(EImap, From, ResponseToken, Folder, Properties, Depth, MaxSize) ->
139
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_getmetadata, { Folder, Properties, Depth, MaxSize }).
140
141
-get_folder_annotations(PID, From, ResponseToken, Folder) when is_list(Folder) ->
142
- get_folder_annotations(PID, From, ResponseToken, list_to_binary(Folder));
143
-get_folder_annotations(PID, From, ResponseToken, Folder) when is_binary(Folder) ->
144
- Command = #command{ message = eimap_command_annotation:new(Folder),
145
- from = From, response_token = ResponseToken,
146
- parse_fun = fun eimap_command_annotation:parse/2 },
147
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
148
+-spec get_server_metadata(EImap :: pid(), From :: pid(), ResponseToken :: any(), Properties:: [list() | binary()]) -> ok.
149
+get_server_metadata(EImap, From, ResponseToken, Properties) ->
150
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_getmetadata, { <<>>, Properties, infinity, nomax }).
151
152
-get_message_headers_and_body(PID, From, ResponseToken, Folder, MessageID) ->
153
- %%lager:info("SELECT_DEBUG: peeking message ~p ~p", [Folder, MessageID]),
154
- Command = #command{ mbox = Folder, message = eimap_command_peek_message:new(MessageID),
155
- from = From, response_token = ResponseToken,
156
- parse_fun = fun eimap_command_peek_message:parse/2 },
157
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
158
+-spec get_server_metadata(EImap :: pid(), From :: pid(), ResponseToken :: any(), Properties:: [list() | binary()], Depth :: infinity | integer(), MaxSize :: nomax | integer()) -> ok.
159
+get_server_metadata(EImap, From, ResponseToken, Properties, Depth, MaxSize) ->
160
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_getmetadata, { <<>>, Properties, Depth, MaxSize }).
161
+
162
+-spec get_folder_annotations(EImap :: pid(), From :: pid(), ResponseToken :: any(), Folder :: [list() | binary()]) -> ok.
163
+get_folder_annotations(EImap, From, ResponseToken, Folder) ->
164
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_annotation, Folder).
165
+
166
+-spec peek_message_headers_and_body(EImap :: pid(), From :: pid(), ResponseToken :: any(), Folder :: [list() | binary()], MessageID :: [integer() | binary()]) -> ok.
167
+peek_message_headers_and_body(EImap, From, ResponseToken, Folder, MessageID) ->
168
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_peek_message, MessageID, Folder).
169
+
170
+-spec get_path_tokens(EImap :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
171
+get_path_tokens(EImap, From, ResponseToken) ->
172
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_namespace, ok).
173
+
174
+-spec noop(EImap :: pid(), From :: pid(), ResponseToken :: any()) -> ok.
175
+noop(EImap, From, ResponseToken) ->
176
+ send_command_to_queue(EImap, From, ResponseToken, eimap_command_noop, ok).
177
178
-get_path_tokens(PID, From, ResponseToken) ->
179
- Command = #command{ message = eimap_command_namespace:new([]),
180
- from = From, response_token = ResponseToken,
181
- parse_fun = fun eimap_command_namespace:parse/2 },
182
- gen_fsm:send_all_state_event(PID, { ready_command, Command }).
183
184
%% gen_server API
185
-init(#eimap_server_config{ host = Host, port = Port, tls = TLS }) ->
186
+init(Options) ->
187
+ Host = proplists:get_value(host, Options, "127.0.0.1"),
188
+ Port = proplists:get_value(port, Options, 143),
189
+ TLS = proplists:get_value(tls, Options, false),
190
State = #state { host = Host, port = Port, tls = TLS },
191
{ ok, disconnected, State }.
192
193
disconnected({ connect, Receiver, ResponseToken }, #state{ command_queue = CommandQueue, host = Host, port = Port, tls = TLS, socket = undefined } = State) ->
194
%lager:debug("CONNECTING! ~p ~p", [Receiver, ResponseToken]),
195
{ {ok, Socket}, TlsState, SendCapabilitiesTo, NewCommandQueue } = create_socket(Host, Port, TLS, Receiver, ResponseToken, CommandQueue),
196
- Command = #command{ message = eimap_command_capability:new([]),
197
+ { Message, ResponseType } = eimap_command_capability:new_command(parse_serverid),
198
+ Command = #command{ message = Message, response_type = ResponseType,
199
from = SendCapabilitiesTo, response_token = { connected, Receiver, ResponseToken },
200
- parse_fun = fun eimap_command_capability:parse/2 },
201
+ parse_state = eimap_command_capability },
202
{ next_state, wait_response, State#state { socket = Socket, tls_state = TlsState, current_command = Command, command_queue = NewCommandQueue } };
203
disconnected(Command, State) when is_record(Command, command) ->
204
{ next_state, disconnected, enque_command(Command, State) }.
205
206
Receiver ! { imap_server_response, Data },
207
{ next_state, passthrough, State };
208
passthrough(Command, State) when is_record(Command, command) ->
209
- gen_fsm:send_event(self(), process_command_queue),
210
- { next_state, idle, enque_command(Command, State) }.
211
+ NewState = ensure_process_command_queue(State),
212
+ { next_state, idle, enque_command(Command, NewState) }.
213
214
idle(process_command_queue, #state{ command_queue = Queue } = State) ->
215
+ UnguardedState = State#state{ process_command_queue_guard = false },
216
case queue:out(Queue) of
217
{ { value, Command }, ModifiedQueue } when is_record(Command, command) ->
218
%lager:info("Clearing queue of ~p", [Command]),
219
- NewState = send_command(Command, State#state{ command_queue = ModifiedQueue }),
220
+ NewState = send_command(Command, UnguardedState#state{ command_queue = ModifiedQueue }),
221
{ next_state, wait_response, NewState };
222
- { empty, ModifiedQueue } ->
223
+ { empty, _ModifiedQueue } ->
224
NextState = next_state_after_emptied_queue(State),
225
- %lager:info("Queue was empty moving on to ... ~p", [NextState]),
226
- { next_state, NextState, State#state{ command_queue = ModifiedQueue } }
227
+ { next_state, NextState, UnguardedState }
228
end;
229
idle({ data, _Data }, State) ->
230
%%lager:info("Idling, server sent: ~p", [_Data]),
231
232
NewState = send_command(Command, State),
233
{ next_state, wait_response, NewState };
234
idle(_Event, State) ->
235
- { next_state, idle, State }.
236
+ { next_state, idle, ensure_process_command_queue(State) }.
237
+
238
239
next_state_after_emptied_queue(#state{ passthrough = true }) ->
240
gen_fsm:send_event(self(), flush_passthrough_buffer),
241
242
next_state_after_emptied_queue(_State) ->
243
idle.
244
245
+ensure_process_command_queue(State) ->
246
+ case State#state.process_command_queue_guard of
247
+ true ->
248
+ State;
249
+ _ ->
250
+ gen_fsm:send_event(self(), process_command_queue),
251
+ State#state{ process_command_queue_guard = true }
252
+ end.
253
254
%%TODO a variant that checks "#command{ from = undefined }" to avoid parsing responses which will go undelivered?
255
wait_response(Command, State) when is_record(Command, command) ->
256
{ next_state, wait_response, enque_command(Command, State) };
257
-wait_response({ data, _Data }, #state{ current_command = #command{ parse_fun = undefined } } = State) ->
258
- gen_fsm:send_event(self(), process_command_queue),
259
- { next_state, idle, State };
260
-wait_response({ data, Data }, #state{ current_command = #command{ parse_fun = Fun, tag = Tag } } = State) when is_function(Fun, 2) ->
261
- Response = Fun(Data, Tag),
262
- %%lager:info("Response from parser was ~p ~p, size of queue ~p", [More, Response, queue:len(State#state.command_queue)]),
263
- next_command_after_response(Response, State);
264
-wait_response({ data, Data }, #state{ parse_state = ParseState, current_command = #command{ parse_fun = Fun, tag = Tag } } = State) when is_function(Fun, 3) ->
265
- Response = Fun(Data, Tag, ParseState),
266
+wait_response({ data, _Data }, #state{ current_command = #command{ parse_state = undefined } } = State) ->
267
+ { next_state, idle, ensure_process_command_queue(State) };
268
+wait_response({ data, Data }, #state{ current_command = #command{ response_type = ResponseType, parse_state = CommandState , tag = Tag } } = State) ->
269
+ Response = eimap_command:parse_response(ResponseType, Data, Tag, CommandState),
270
%%lager:info("Response from parser was ~p ~p, size of queue ~p", [More, Response, queue:len(State#state.command_queue)]),
271
next_command_after_response(Response, State);
272
wait_response(process_command_queue, State) ->
273
274
{ next_state, startingtls, State#state{ passthrough_send_buffer = <<Buffer/binary, Data>> } };
275
startingtls(Command, State) when is_record(Command, command) ->
276
{ next_state, startingtls, enque_command(Command, State) };
277
-startingtls({ data, Data }, #state{ current_command = #command{ parse_fun = Fun, tag = Tag } } = State) when is_function(Fun, 2) ->
278
- Response = Fun(Data, Tag),
279
+startingtls({ data, Data }, #state{ current_command = #command{ response_type = ResponseType, parse_state = CommandState, tag = Tag } } = State) ->
280
+ Response = eimap_command:parse_response(ResponseType, Data, Tag, CommandState),
281
%%lager:info("Response from parser was ~p ~p, size of queue ~p", [More, Response, queue:len(State#state.command_queue)]),
282
next_command_after_response(Response, State).
283
284
285
handle_event(disconnect, _StateName, State) ->
286
close_socket(State),
287
{ next_state, disconnected, reset_state(State) };
288
+handle_event({ ready_command, Command }, StateName, State) when is_record(Command, command) ->
289
+ ?MODULE:StateName(Command, State);
290
handle_event({ start_passthrough, Receiver }, StateName, State) ->
291
{ next_state, StateName, State#state{ passthrough = true, passthrough_recv = Receiver } };
292
handle_event(stop_passthrough, StateName, State) ->
293
- NextState = case StateName of
294
- passthrough ->
295
- gen_fsm:send_event(self(), process_command_queue),
296
- idle;
297
- State -> State
298
+ { NextStateName, NewState } = case StateName of
299
+ passthrough -> { idle, ensure_process_command_queue(State) };
300
+ StateName -> { StateName, State }
301
end,
302
- { next_state, NextState, State#state{ passthrough = false } };
303
-handle_event({ ready_command, Command }, StateName, State) when is_record(Command, command) ->
304
- ?MODULE:StateName(Command, State);
305
+ { next_state, NextStateName, NewState#state{ passthrough = false } };
306
handle_event({ passthrough, Data }, passthrough, #state{ passthrough = true } = State) ->
307
?MODULE:passthrough({ passthrough, Data }, State);
308
handle_event({ passthrough, Data }, StateName, #state{ passthrough = true, passthrough_send_buffer = Buffer } = State) ->
309
NewBuffer = <<Buffer/binary, Data/binary>>,
310
+ NewState =
311
case StateName of
312
- idle -> gen_fsm:send_event(self(), process_command_queue);
313
- _ -> ok
314
+ idle -> ensure_process_command_queue(State);
315
+ _ -> State
316
end,
317
- { next_state, StateName, State#state{ passthrough_send_buffer = NewBuffer } };
318
+ { next_state, StateName, NewState#state{ passthrough_send_buffer = NewBuffer } };
319
handle_event(_Event, StateName, State) -> { next_state, StateName, State}.
320
321
handle_sync_event(_Event, _From, StateName, State) -> { next_state, StateName, State}.
322
323
%lager:info("Received from server plaintext: ~s", [Data]),
324
?MODULE:StateName({ data, Data }, State);
325
handle_info({ ssl_closed, Socket }, _StateName, #state{ socket = Socket, host = Host, port = Port } = State) ->
326
- lager:info("~p Disconnected from ~p:~p .\n", [self(), Host, Port]),
327
+ lager:debug("~p Disconnected from ~p:~p .\n", [self(), Host, Port]),
328
{ stop, normal, State };
329
handle_info({ ssl_error, Socket, _Reason }, _StateName, #state{ socket = Socket, host = Host, port = Port } = State) ->
330
lager:info("~p Disconnected due to socket error from ~p:~p .\n", [self(), Host, Port]),
331
{ stop, normal, State };
332
handle_info({ tcp_closed, Socket }, _StateName, #state{ socket = Socket, host = Host, port = Port } = State) ->
333
- lager:info("~p Disconnected from ~p:~p .\n", [self(), Host, Port]),
334
+ lager:debug("~p Disconnected from ~p:~p .\n", [self(), Host, Port]),
335
{ stop, normal, State };
336
handle_info({ tcp_error, Socket, _Reason }, _StateName, #state{ socket = Socket, host = Host, port = Port } = State) ->
337
lager:info("~p Disconnected due to socket error from ~p:~p .\n", [self(), Host, Port]),
338
339
%lager:debug("Connected, capabilities are: ~s; ServerID is ~s", [Capabilities, ServerID]),
340
send_hello_string(Capabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver)
341
end,
342
- { next_state, idle, State#state{ parse_state = none, server_id = ServerID } };
343
+ { next_state, idle, State#state{ current_command = undefined, server_id = ServerID } };
344
handle_info({ { posttls_capabilities, Receiver, ResponseToken }, Capabilities }, _StateName, #state{ server_id = ServerID, passthrough = Passthrough, passthrough_recv = PassthroughReceiver } = State) ->
345
- send_hello_string(Capabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver),
346
- { next_state, idle, State#state{ parse_state = none } };
347
+ OurCapabilities =
348
+ case binary:match(Capabilities, <<"STARTTLS">>) of
349
+ nomatch -> <<Capabilities/binary, " STARTTLS">>;
350
+ _ -> Capabilities
351
+ end,
352
+ send_hello_string(OurCapabilities, ServerID, Receiver, ResponseToken, Passthrough, PassthroughReceiver),
353
+ { next_state, idle, State#state{ current_command = none } };
354
handle_info({ { selected, MBox }, ok }, StateName, State) ->
355
%%lager:info("~p Selected mbox ~p", [self(), MBox]),
356
{ next_state, StateName, State#state{ current_mbox = MBox } };
357
358
code_change(_OldVsn, Statename, State, _Extra) -> { ok, Statename, State }.
359
360
%% private API
361
+send_command_to_queue(EImap, From, ResponseToken, Module, Args) ->
362
+ send_command_to_queue(EImap, From, ResponseToken, Module, Args, undefined).
363
+send_command_to_queue(EImap, From, ResponseToken, Module, Args, Folder) ->
364
+ { Message, ResponseType } = Module:new_command(Args),
365
+ Command = #command{ mbox = Folder, message = Message, response_type = ResponseType,
366
+ from = From, response_token = ResponseToken,
367
+ parse_state = Module },
368
+ gen_fsm:send_all_state_event(EImap, { ready_command, Command }).
369
+
370
send_hello_string(Capabilities, ServerId, Receiver, ResponseToken, Passthrough, PassthroughReceiver) ->
371
- Message = <<"* OK [CAPABILITY ", Capabilities/binary, "] ", ServerId/binary, "\r\n">>,
372
- notify_of_response(Message, Receiver, ResponseToken),
373
- passthrough_capabilities(Message, Passthrough, PassthroughReceiver).
374
+ notify_of_response([{ capabilities, Capabilities }, { server_id, ServerId } ], Receiver, ResponseToken),
375
+ passthrough_capabilities(Capabilities, ServerId, Passthrough, PassthroughReceiver).
376
377
-passthrough_capabilities(Response, true, Receiver) -> Receiver ! { imap_server_response, Response };
378
-passthrough_capabilities(_Response, _Passthrough, _Receiver) -> ok.
379
+passthrough_capabilities(Capabilities, ServerId, true, Receiver) ->
380
+ Message = <<"* OK [CAPABILITY ", Capabilities/binary, "] ", ServerId/binary, "\r\n">>,
381
+ Receiver ! { imap_server_response, Message };
382
+passthrough_capabilities(_Capabilities, _ServerId, _Passthrough, _Receiver) -> ok.
383
384
notify_of_response(none, _Command) -> ok;
385
notify_of_response(Response, #command { from = From, response_token = Token }) -> notify_of_response(Response, From, Token);
386
387
notify_of_mbox_failure_during_filter(Command, true) -> notify_of_response({ error, mailboxnotfound }, Command), false;
388
notify_of_mbox_failure_during_filter(_Command, false) -> true.
389
390
-next_command_after_response({ more, Fun, ParseState }, State) when is_function(Fun, 3) ->
391
- { next_state, wait_response, State#state{ parse_state = ParseState, current_command = State#state.current_command#command{ parse_fun = Fun } } };
392
next_command_after_response({ more, ParseState }, State) ->
393
- { next_state, wait_response, State#state{ parse_state = ParseState } };
394
+ { next_state, wait_response, State#state{ current_command = State#state.current_command#command{ parse_state = ParseState } } };
395
next_command_after_response({ error, _ } = ErrorResponse, State) ->
396
notify_of_response(ErrorResponse, State#state.current_command),
397
- gen_fsm:send_event(self(), process_command_queue),
398
- { next_state, idle, State#state{ parse_state = none } };
399
+ NewState = ensure_process_command_queue(State),
400
+ { next_state, idle, NewState#state{ current_command = undefined } };
401
next_command_after_response({ fini, Response }, State) ->
402
%lager:info("Notifying with ~p", [State#state.current_command]),
403
notify_of_response(Response, State#state.current_command),
404
- gen_fsm:send_event(self(), process_command_queue),
405
- { next_state, idle, State#state{ parse_state = none } };
406
+ NewState = ensure_process_command_queue(State),
407
+ { next_state, idle, NewState#state{ current_command = undefined } };
408
next_command_after_response(starttls, State) ->
409
{ TLSState, Socket } = upgrade_socket(State),
410
%lager:info("~p Upgraded the socket ...", [self()]),
411
- gen_fsm:send_event(self(), process_command_queue),
412
- { next_state, idle, State#state{ parse_state = none, socket = Socket, tls_state = TLSState } };
413
+ NewState = ensure_process_command_queue(State),
414
+ { next_state, idle, NewState#state{ current_command = undefined, socket = Socket, tls_state = TLSState } };
415
next_command_after_response(compression_active, State) ->
416
{ Inflator, Deflator } = eimap_utils:new_imap_compressors(),
417
- gen_fsm:send_event(self(), process_command_queue),
418
- { next_state, idle, State#state{ inflator = Inflator, deflator = Deflator } };
419
+ NewState = ensure_process_command_queue(State),
420
+ { next_state, idle, NewState#state{ current_command = undefined, inflator = Inflator, deflator = Deflator } };
421
next_command_after_response({ close_socket, Response }, State) ->
422
notify_of_response(Response, State#state.current_command),
423
{ stop, normal, State }.
424
425
%lager:debug("Setting up the tls creation with ultimate end point of ~p ~p", [Receiver, ResponseToken]),
426
% we do an implicit TLS by adding a starttls command and then a capability command so we can
427
% pretend to the user that the socket just magically opened up like this.
428
- TlsCommand = #command{ message = eimap_command_starttls:new(noparams),
429
+ %TODO: some duplicated code here; would be nice to clean this up a bit?
430
+ { TlsMessage, TlsResponseType } = eimap_command_starttls:new_command(ok),
431
+ TlsCommand = #command{ message = TlsMessage, response_type = TlsResponseType,
432
from = self(), response_token = undefined,
433
- parse_fun = fun eimap_command_starttls:parse/2 },
434
- CapabilitiesCommand = #command{ message = eimap_command_capability:new(noparams),
435
+ parse_state = eimap_command_starttls },
436
+ { CapMessage, CapResponseType } = eimap_command_capability:new_command(ok),
437
+ CapabilitiesCommand = #command{ message = CapMessage, response_type = CapResponseType,
438
from = self(), response_token = { posttls_capabilities, Receiver, ResponseToken },
439
- parse_fun = fun eimap_command_capability:parse/2 },
440
+ parse_state = eimap_command_capability },
441
% note the use of queue:in_r to _prepend_ the commands so they get run first even if the user
442
% has pre-connection queued up commands
443
NewCommandQueue = queue:in_r(TlsCommand, queue:in_r(CapabilitiesCommand, CommandQueue)),
444
445
send_command(Command, State) ->
446
send_command(fun gen_tcp:send/2, Command, State).
447
448
-send_command(Fun, #command{ mbox = undefined } = Command, State) ->
449
+send_command(SocketFun, #command{ mbox = undefined } = Command, State) ->
450
%%lager:info("~p SELECT_DEBUG issuing command without mbox: ~p", [self(), Command#command.message]),
451
- send_command_now(Fun, Command, State);
452
-send_command(Fun, #command{ mbox = MBox } = Command, #state{ current_mbox = CurrentMbox } = State) ->
453
+ send_command_now(SocketFun, Command, State);
454
+send_command(SocketFun, #command{ mbox = MBox } = Command, #state{ current_mbox = CurrentMbox } = State) ->
455
%%lager:info("~p SELECT_DEBUG issuing command with mbox ~p (current: ~p, equal -> ~p): ~p", [self(), MBox, CurrentMbox, (MBox =:= CurrentMbox), Command#command.message]),
456
- send_command_or_select_mbox(Fun, Command, State, MBox, MBox =:= CurrentMbox).
457
+ send_command_or_select_mbox(SocketFun, Command, State, MBox, MBox =:= CurrentMbox).
458
459
-send_command_or_select_mbox(Fun, Command, State, _MBox, true) ->
460
- send_command_now(Fun, Command, State);
461
-send_command_or_select_mbox(Fun, DelayedCommand, State, MBox, false) ->
462
+send_command_or_select_mbox(SocketFun, Command, State, _MBox, true) ->
463
+ send_command_now(SocketFun, Command, State);
464
+send_command_or_select_mbox(SocketFun, DelayedCommand, State, MBox, false) ->
465
NextState = reenque_command(DelayedCommand, State),
466
- SelectMessage = eimap_command_examine:new(MBox),
467
- SelectCommand = #command{ message = SelectMessage, parse_fun = fun eimap_command_examine:parse/2,
468
+ %TODO: this really should be SELECT rather than EXAMINE
469
+ { SelectMessage, ResponseType } = eimap_command_switch_folder:new_command(MBox),
470
+ SelectCommand = #command{ message = SelectMessage, response_type = ResponseType,
471
+ parse_state = eimap_command_switch_folder,
472
from = self(), response_token = { selected, MBox } },
473
%%lager:info("~p SELECT_DEBUG: Doing a select first ~p", [self(), SelectMessage]),
474
- send_command_now(Fun, SelectCommand, NextState).
475
+ send_command_now(SocketFun, SelectCommand, NextState).
476
477
-send_command_now(Fun, #command{ message = Message } = Command, #state{ command_serial = Serial, socket = Socket } = State) ->
478
+send_command_now(SocketFun, #command{ message = Message } = Command, #state{ command_serial = Serial, socket = Socket } = State) ->
479
Tag = list_to_binary(io_lib:format("EG~*..0B", [tag_field_width(Serial), Serial])),
480
Data = <<Tag/binary, " ", Message/binary, "\r\n">>,
481
%lager:info("Sending command via ~p: ~s", [Fun, Data]),
482
- Fun(Socket, deflated(Data, State)),
483
+ SocketFun(Socket, deflated(Data, State)),
484
State#state{ command_serial = Serial + 1, current_command = Command#command{ tag = Tag } }.
485
486
enque_command(Command, State) ->
487
erlang-eimap-0.1.5.tar.gz/src/eimap_command.erl -> erlang-eimap-0.2.4.tar.gz/src/eimap_command.erl
Changed
83
1
2
-module(eimap_command).
3
4
+-export([
5
+ parse_response/4,
6
+ formulate_response/2,
7
+ process_status_line/2
8
+ ]).
9
+
10
-type more_tuple() :: { more, ParseContinuation :: parse_continuation(), State :: term }.
11
-type finished_tuple() :: { fini, Results :: term }.
12
-type error_tuple() :: { error, Reason :: binary() }.
13
-type parse_continuation() :: fun((Data :: binary(), Tag :: binary(), State :: term()) ->
14
more_tuple() | finished_tuple() | error_tuple()).
15
16
--callback new(Args :: any()) -> binary().
17
--callback parse(Data :: binary(), Tag :: binary()) ->
18
- more_tuple() | finished_tuple() | error_tuple() | starttls.
19
+-callback new_command(Args :: any()) -> { binary(), single_line_response | multiline_reponse | blob_response }.
20
+% TODO:bring back when we can depend on OTP 18 which introduced optional_callback
21
+% also define parse/2 (for blob_response), formulate_response/2
22
+%-callback process_line(Data :: binary(), Acc :: any()) ->
23
+% more_tuple() | finished_tuple() | error_tuple() | starttls.
24
+
25
+parse_response(multiline_response, Data, Tag, ParseState) -> multiline_parse(false, Data, Tag, ParseState);
26
+parse_response(all_multiline_response, Data, Tag, ParseState) -> multiline_parse(parse_tagged, Data, Tag, ParseState);
27
+parse_response(single_line_response, Data, Tag, Module) -> Module:formulate_response(Data, Tag);
28
+parse_response(blob_response, Data, Tag, { Continuation, ParseState }) -> Continuation(Data, Tag, ParseState);
29
+parse_response(blob_response, Data, Tag, Module) ->
30
+ case Module:parse(Data, Tag) of
31
+ { more, Continuation, ParseState } -> { more, { Continuation, ParseState } };
32
+ Response -> Response
33
+ end.
34
+
35
+multiline_parse(ParseTaggedLine, Data, Tag, { LastPartialLine, Acc, Module }) ->
36
+ FullBuffer = <<LastPartialLine/binary, Data/binary>>,
37
+ { FullLinesBuffer, NewLastPartialLine } = eimap_utils:only_full_lines(FullBuffer),
38
+ Lines = binary:split(FullLinesBuffer, <<"\r\n">>, [global]),
39
+ process_lines(ParseTaggedLine, Tag, NewLastPartialLine, Lines, Acc, Module);
40
+multiline_parse(ParseTaggedLine, Data, Tag, Module) ->
41
+ multiline_parse(ParseTaggedLine, Data, Tag, { <<>>, [], Module }).
42
+
43
+process_lines(_ParseTaggedLine, _Tag, LastPartialLine, [], Acc, Module) -> { more, { LastPartialLine, Acc, Module } };
44
+process_lines(ParseTaggedLine, Tag, LastPartialLine, [Line|MoreLines], Acc, Module) ->
45
+ { FirstLine, ContinuationBytes } = eimap_utils:num_literal_continuation_bytes(Line),
46
+ process_line(ContinuationBytes, ParseTaggedLine, eimap_utils:is_tagged_response(FirstLine, Tag), Tag, LastPartialLine, FirstLine, MoreLines, Acc, Module).
47
+
48
+
49
+process_line(ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, [<<>>|MoreLines], Acc, Module) ->
50
+ %% skip empty lines
51
+ process_line(ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, MoreLines, Acc, Module);
52
+process_line(0, ParseTaggedLine, tagged, Tag, _LastPartialLine, Line, _MoreLines, Acc, Module) ->
53
+ Checked = eimap_utils:check_response_for_failure(Line, Tag),
54
+ Module:formulate_response(Checked, parse_tagged(Checked, ParseTaggedLine, Line, Acc, Module));
55
+process_line(0, ParseTaggedLine, untagged, Tag, LastPartialLine, Line, MoreLines, Acc, Module) ->
56
+ %io:format("Calling it here with ~p~n~n...", [Line]),
57
+ process_lines(ParseTaggedLine, Tag, LastPartialLine, MoreLines, Module:process_line(Line, Acc), Module);
58
+process_line(ContinuationBytes, ParseTaggedLine, _IsTagged, Tag, LastPartialLine, Line, [], Acc, Module) ->
59
+ %% 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
60
+ io:format("Missing lines!~p~n~n", [Acc]),
61
+ BytesAsBinary = integer_to_binary(ContinuationBytes),
62
+ process_lines(ParseTaggedLine, Tag, <<Line/binary, ${, BytesAsBinary/binary, $}, LastPartialLine/binary>>, [], Acc, Module);
63
+process_line(_ContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, Line, [NextLine|MoreLines], Acc, Module) ->
64
+ { StrippedNextLine, NextContinuationBytes } = eimap_utils:num_literal_continuation_bytes(NextLine),
65
+ io:format("Connected up the next line: ~p ~i~n", [StrippedNextLine, NextContinuationBytes]),
66
+ FullLine = <<Line/binary, StrippedNextLine/binary>>,
67
+ process_line(NextContinuationBytes, ParseTaggedLine, IsTagged, Tag, LastPartialLine, FullLine, MoreLines, Acc, Module).
68
+
69
+formulate_response(ok, Data) -> { fini, Data };
70
+formulate_response({ _, Reason }, _Data) -> { error, Reason }.
71
+
72
+parse_tagged(_, false, _Line, Acc, _Module) -> Acc; % we are not passing the tagged line forward (no content, e.g)
73
+parse_tagged(ok, _, Line, Acc, Module) -> Module:process_tagged_line(Line, Acc); % success, so pass it forward
74
+parse_tagged(_Checked, _ParsedTaggedLine, _Line, Acc, _Module) -> Acc. % error, don't bother passing it forward
75
+
76
+-spec process_status_line(Line :: binary(), Acc :: list()) -> NewAcc :: list().
77
+process_status_line(<<"* ", Rest/binary>>, Acc) -> process_matched_status_line(binary:split(Rest, <<" ">>, [global]), Acc);
78
+process_status_line(_, Acc) -> Acc.
79
80
+process_matched_status_line([Number, Key|_], Acc) -> [{ eimap_utils:binary_to_atom(Key), binary_to_integer(Number) }|Acc];
81
+process_matched_status_line(_, Acc) -> Acc.
82
83
erlang-eimap-0.1.5.tar.gz/src/eimap_uidset.erl -> erlang-eimap-0.2.4.tar.gz/src/eimap_uidset.erl
Changed
64
1
2
%% along with this program. If not, see <http://www.gnu.org/licenses/>.
3
4
-module(eimap_uidset).
5
--export([parse/1, next_uid/1]).
6
+-export([uid_list_to_binary/1, parse/1, next_uid/1]).
7
+
8
+% for internal use only
9
+-export([add_uid_as_binary_to_list/2, stitch_bin_list/2]).
10
11
%% tokens have the form: integer | { integer, integer }
12
-record(uidset, { tokens = [], current = none }).
13
14
-%% currently do not support parsing regular (list) strings, but could be added
15
+uid_list_to_binary(UidList) when is_list(UidList) ->
16
+ ListOfBins = lists:foldl(fun ?MODULE:add_uid_as_binary_to_list/2, [], UidList),
17
+ lists:foldl(fun ?MODULE:stitch_bin_list/2, <<>>, ListOfBins);
18
+uid_list_to_binary(_UidList) -> <<>>.
19
+
20
+parse(UidSet) when is_list(UidSet) -> parse(list_to_binary(UidSet));
21
parse(<<>>) -> badarg;
22
parse(UidSet) when is_binary(UidSet) ->
23
Components = binary:split(UidSet, <<",">>, [global]),
24
25
_:_ -> badarg
26
end.
27
28
+next_uid(#uidset{ tokens = Tokens, current = Current }) -> next_uid(Tokens, Current).
29
+next_uid([], _Current) -> { none, #uidset {} };
30
+next_uid([{ First, Second }|_] = FullList, Current) -> next_in_range(FullList, First, Second, Current);
31
+next_uid([Uid|UidSet], _Current) -> { Uid, #uidset{ tokens = UidSet } }.
32
+
33
+% PRIVATE API
34
+add_uid_as_binary_to_list(Uid, Acc) when is_integer(Uid), Uid >= 0 -> [integer_to_binary(Uid)|Acc];
35
+add_uid_as_binary_to_list({ StartUid, EndUid }, Acc) when is_integer(StartUid), is_integer(EndUid) ->
36
+ case add_uid_range_as_binary_to_list(StartUid, EndUid) of
37
+ error -> Acc;
38
+ { Start, End } -> [<<Start/binary, ":", End/binary>>|Acc]
39
+ end;
40
+add_uid_as_binary_to_list(_, Acc) -> Acc.
41
+
42
+add_uid_range_as_binary_to_list(Start, End) when Start < 0; End < 0 -> error;
43
+add_uid_range_as_binary_to_list(Start, End) when Start < End -> { integer_to_binary(Start), integer_to_binary(End) };
44
+add_uid_range_as_binary_to_list(Start, End) -> { integer_to_binary(End), integer_to_binary(Start) }.
45
+
46
+stitch_bin_list(Uid, <<>>) -> Uid;
47
+stitch_bin_list(Uid, Acc) -> <<Uid/binary, ",", Acc/binary>>.
48
+
49
add_component(Uid, Acc) when is_integer(Uid), Uid >= 0 -> [Uid|Acc];
50
add_component([First, Second], Acc) -> add_component(binary_to_integer(First), binary_to_integer(Second), Acc);
51
add_component([<<"">>], Acc) -> Acc;
52
53
add_range(First, Second, Acc) when First == Second -> [First|Acc];
54
add_range(First, Second, Acc) -> [{ First, Second }|Acc].
55
56
-next_uid(#uidset{ tokens = Tokens, current = Current }) -> next_uid(Tokens, Current).
57
-next_uid([], _Current) -> { none, #uidset {} };
58
-next_uid([{ First, Second }|_] = FullList, Current) -> next_in_range(FullList, First, Second, Current);
59
-next_uid([Uid|UidSet], _Current) -> { Uid, #uidset{ tokens = UidSet } }.
60
-
61
next_in_range(Tokens, First, _Second, none) -> { First, #uidset{ tokens = Tokens, current = First } };
62
next_in_range(Tokens, First, Second, Current) when First < Second, Current < Second -> Next = Current + 1, { Next, #uidset{ tokens = Tokens, current = Next } };
63
next_in_range(Tokens, First, Second, Current) when First > Second, Current > Second -> Next = Current - 1, { Next, #uidset{ tokens = Tokens, current = Next } };
64
erlang-eimap-0.1.5.tar.gz/src/eimap_utils.erl -> erlang-eimap-0.2.4.tar.gz/src/eimap_utils.erl
Changed
118
1
2
-export([
3
extract_path_from_uri/3, extract_uidset_from_uri/1,
4
split_command_into_components/1, is_tagged_response/2, remove_tag_from_response/3,
5
- header_name/1,
6
+ header_name/1, parse_flags/1,
7
check_response_for_failure/2,
8
ensure_binary/1,
9
new_imap_compressors/0,
10
- only_full_lines/1
11
+ only_full_lines/1,
12
+ binary_to_atom/1,
13
+ num_literal_continuation_bytes/1
14
]).
15
16
%% Translate the folder name in to a fully qualified folder path such as it
17
%% would be used by a cyrus administrator.
18
+-spec extract_path_from_uri(SharedPrefix :: binary(), HierarchyDelim :: binary, URI :: binary()) -> Path :: binary() | bad_uri.
19
extract_path_from_uri(SharedPrefix, HierarchyDelim, URI) when is_binary(URI) ->
20
extract_path_from_uri(SharedPrefix, HierarchyDelim, binary_to_list(URI));
21
extract_path_from_uri(SharedPrefix, HierarchyDelim, URI) when is_list(URI) ->
22
23
Error -> Error
24
end.
25
26
+-spec extract_uidset_from_uri(URI :: binary()) -> UIDSet:: binary().
27
extract_uidset_from_uri(URI) when is_binary(URI) ->
28
{ TagStart, TagEnd } = binary:match(URI, <<";UID=">>),
29
UIDStart = TagStart + TagEnd + 1,
30
31
{ Semicolon, _ } -> binary:part(URI, UIDStart - 1, Semicolon - UIDStart + 1)
32
end.
33
34
+-spec header_name(mailbox_uid | groupware_uid | groupware_uid) -> binary(); (any()) -> unknown.
35
header_name(mailbox_uid) -> <<"/vendor/cmu/cyrus-imapd/uniqueid">>;
36
header_name(groupware_type) -> <<"X-Kolab-Type">>;
37
header_name(groupware_uid) -> <<"Subject">>;
38
header_name(_) -> unknown.
39
40
+-spec parse_flags(FlagString :: binary() | list()) -> Flags :: [binary()].
41
+parse_flags(String) when is_list(String) -> parse_flags(list_to_binary(String));
42
+parse_flags(<<"(", Parened/binary>>) ->
43
+ case binary:match(Parened, <<")">>) of
44
+ nomatch -> [];
45
+ { ClosingParens, _ } -> parse_flags(binary_part(Parened, 0, ClosingParens))
46
+ end;
47
+parse_flags(<<>>) -> [];
48
+parse_flags(FlagString) when is_binary(FlagString) ->
49
+ binary:split(FlagString, <<" ">>, [global]).
50
+
51
-spec check_response_for_failure(Data :: binary(), Tag :: undefined | binary()) -> ok | { error, Reason :: binary() }.
52
check_response_for_failure(Data, undefined) when is_binary(Data) ->
53
check_response_for_failure(Data, <<>>);
54
55
split_command_into_components(Buffer) when is_binary(Buffer) ->
56
split_command(Buffer).
57
58
--spec is_tagged_response(Buffer :: binary(), Tag :: binary()) -> true | false.
59
+-spec is_tagged_response(Buffer :: binary(), Tag :: binary()) -> tagged | untagged.
60
is_tagged_response(Buffer, Tag) ->
61
TagSize = size(Tag) + 1, % The extra char is a space
62
BufferSize = size(Buffer),
63
- case (TagSize =< BufferSize) of
64
- true -> <<Tag/binary, " ">> =:= binary:part(Buffer, 0, TagSize);
65
- false -> false
66
+ case
67
+ case (TagSize =< BufferSize) of
68
+ true -> <<Tag/binary, " ">> =:= binary:part(Buffer, 0, TagSize);
69
+ _ -> false
70
+ end of
71
+
72
+ true -> tagged;
73
+ _ -> untagged
74
+ end.
75
+
76
+-spec num_literal_continuation_bytes(Buffer :: binary()) -> { BufferSansContinuation :: binary(), NumberBytes :: integer() }.
77
+num_literal_continuation_bytes(Buffer) when size(Buffer) < 4 ->
78
+ { Buffer, 0 };
79
+num_literal_continuation_bytes(Buffer) ->
80
+ case binary:last(Buffer) =:= $} of
81
+ true -> number_of_bytes_in_continuation(Buffer);
82
+ false -> { Buffer, 0 }
83
+ end.
84
+
85
+number_of_bytes_in_continuation(Buffer) ->
86
+ BufferSize = size(Buffer),
87
+ OpenBracePos = find_continuation_open_brace(Buffer, BufferSize - 3),
88
+ confirm_continuation(Buffer, OpenBracePos).
89
+
90
+find_continuation_open_brace(_Buffer, 0) -> -1;
91
+find_continuation_open_brace(Buffer, Pos) ->
92
+ case binary:at(Buffer, Pos) of
93
+ ${ -> Pos;
94
+ _ -> find_continuation_open_brace(Buffer, Pos - 1)
95
+ end.
96
+
97
+confirm_continuation(Buffer, -1) ->
98
+ { Buffer, 0 };
99
+confirm_continuation(Buffer, OpenBracePos) ->
100
+ BufferSize = size(Buffer),
101
+ try binary_to_integer(binary:part(Buffer, OpenBracePos + 1, BufferSize - OpenBracePos - 2)) of
102
+ Result -> { binary:part(Buffer, 0, OpenBracePos), Result }
103
+ catch
104
+ _:_ -> { Buffer, 0 }
105
end.
106
107
-spec remove_tag_from_response(Buffer :: binary(), Tag :: undefine | binary(), Check :: check | trust) -> Command :: binary().
108
109
110
only_full_lines(Buffer, BufferLength, $\n, Pos) when Pos =:= BufferLength -> { Buffer, <<>> };
111
only_full_lines(Buffer, BufferLength, $\n, Pos) -> { binary:part(Buffer, 0, Pos + 1), binary:part(Buffer, Pos + 1, BufferLength - Pos - 1) };
112
-only_full_lines(Buffer, BufferLength, _, 0) -> { <<>>, Buffer };
113
+only_full_lines(Buffer, _BufferLength, _, 0) -> { <<>>, Buffer };
114
only_full_lines(Buffer, BufferLength, _, Pos) -> only_full_lines(Buffer, BufferLength, binary:at(Buffer, Pos - 1), Pos - 1).
115
116
+-spec binary_to_atom(Value :: binary()) -> ValueAsAtom :: atom().
117
+binary_to_atom(Value) -> list_to_atom(string:to_lower(binary_to_list(Value))).
118
erlang-eimap-0.1.5.tar.gz/test/eimap_command_annotation_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_annotation_tests.erl
Changed
40
1
2
%% { tag, server_response, expected_parsed_results }
3
{
4
<<"EG0002">>,
5
- <<"* ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/color\" (\"value.shared\" \"32CD32\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/folder-type\" (\"value.shared\" \"event\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/x-toltec/test\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/horde/share-params\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/h-share-attr-desc\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/uniqueid\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/pxfb-readable-for\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/incidences-for\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/folder-test\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/displayname\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/activesync\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/uniqueid\" (\"value.shared\" \"b1a5bb95-2bd3-4628-a0d2-49e9bd10735e\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/squat\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/size\" (\"value.shared\" \"5726\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/sieve\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/sharedseen\" (\"value.shared\" \"false\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/pop3showafter\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/pop3newuidl\" (\"value.shared\" \"true\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/partition\" (\"value.shared\" \"default\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/news2mail\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/lastupdate\" (\"value.shared\" \"20-Mar-2015 05:17:51 +0100\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/lastpop\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/expire\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/duplicatedeliver\" (\"value.shared\" \"false\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/thread\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/specialuse\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/sort\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/comment\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/checkperiod\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/check\" (\"value.shared\" NIL)\r\n EG0002 OK Completed">>,
6
+ <<"* ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/color\" (\"value.shared\" \"32CD32\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/folder-type\" (\"value.shared\" \"event\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/x-toltec/test\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/horde/share-params\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/h-share-attr-desc\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/uniqueid\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/pxfb-readable-for\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/incidences-for\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/folder-test\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/displayname\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/kolab/activesync\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/uniqueid\" (\"value.shared\" \"b1a5bb95-2bd3-4628-a0d2-49e9bd10735e\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/squat\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/size\" (\"value.shared\" \"5726\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/sieve\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/sharedseen\" (\"value.shared\" \"false\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/pop3showafter\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/pop3newuidl\" (\"value.shared\" \"true\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/partition\" (\"value.shared\" \"default\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/news2mail\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/lastupdate\" (\"value.shared\" \"20-Mar-2015 05:17:51 +0100\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/lastpop\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/expire\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/vendor/cmu/cyrus-imapd/duplicatedeliver\" (\"value.shared\" \"false\")\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/thread\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/specialuse\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/sort\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/comment\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/checkperiod\" (\"value.shared\" NIL)\r\n * ANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"/check\" (\"value.shared\" NIL)\r\nEG0002 OK Completed\r\n">>,
7
{ fini, [
8
{<<"/vendor/cmu/cyrus-imapd/duplicatedeliver">>, false},
9
{<<"/vendor/cmu/cyrus-imapd/lastupdate">>, <<"20-Mar-2015 05:17:51 +0100">>},
10
11
},
12
{
13
<<"EG0002">>,
14
- <<"* ANNOTATION user/john.doe/Sent@example.org \"/vendor/x-toltec/test\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/horde/share-params\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/h-share-attr-desc\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/uniqueid\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/pxfb-readable-for\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/incidences-for\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/folder-type\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/folder-test\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/displayname\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/color\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/activesync\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/uniqueid\" (\"value.shared\" \"237357ec-7610-422e-9e55-0bae83caf58a\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/squat\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/size\" (\"value.shared\" \"401\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/sieve\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/sharedseen\" (\"value.shared\" \"false\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/pop3showafter\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/pop3newuidl\" (\"value.shared\" \"true\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/partition\" (\"value.shared\" \"default\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/news2mail\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/lastupdate\" (\"value.shared\" \"23-Mar-2015 15:19:29 +0100\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/lastpop\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/expire\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/duplicatedeliver\" (\"value.shared\" \"false\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/thread\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/specialuse\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/sort\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/comment\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/checkperiod\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/check\" (\"value.shared\" NIL)\r\n EG0002 OK Completed">>,
15
+ <<"* ANNOTATION user/john.doe/Sent@example.org \"/vendor/x-toltec/test\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/horde/share-params\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/h-share-attr-desc\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/uniqueid\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/pxfb-readable-for\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/incidences-for\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/folder-type\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/folder-test\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/displayname\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/color\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/kolab/activesync\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/uniqueid\" (\"value.shared\" \"237357ec-7610-422e-9e55-0bae83caf58a\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/squat\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/size\" (\"value.shared\" \"401\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/sieve\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/sharedseen\" (\"value.shared\" \"false\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/pop3showafter\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/pop3newuidl\" (\"value.shared\" \"true\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/partition\" (\"value.shared\" \"default\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/news2mail\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/lastupdate\" (\"value.shared\" \"23-Mar-2015 15:19:29 +0100\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/lastpop\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/expire\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/vendor/cmu/cyrus-imapd/duplicatedeliver\" (\"value.shared\" \"false\")\r\n * ANNOTATION user/john.doe/Sent@example.org \"/thread\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/specialuse\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/sort\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/comment\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/checkperiod\" (\"value.shared\" NIL)\r\n * ANNOTATION user/john.doe/Sent@example.org \"/check\" (\"value.shared\" NIL)\r\nEG0002 OK Completed\r\n">>,
16
{ fini, [
17
{<<"/vendor/cmu/cyrus-imapd/duplicatedeliver">>,false},
18
{<<"/vendor/cmu/cyrus-imapd/lastupdate">>,<<"23-Mar-2015 15:19:29 +0100">>},
19
20
}
21
}
22
],
23
- lists:foldl(fun({ Tag, ServerData, Expected }, Acc) -> [?_assertEqual(Expected, eimap_command_annotation:parse(ServerData, Tag))|Acc] end, [], Data).
24
+ { _Command, ResponseType } = eimap_command_annotation:new_command(<<"/my/folder">>),
25
+ lists:foldl(fun({ Tag, ServerData, Expected }, Acc) -> [?_assertEqual(Expected, eimap_command:parse_response(ResponseType, ServerData, Tag, eimap_command_annotation))|Acc] end, [], Data).
26
27
new_test_() ->
28
Data =
29
[
30
%% mailbox, command
31
- { <<"user/john.doe/Calendar/Personal Calendar@example.org">>, <<"GETANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"*\" \"value.shared\"">> }
32
+ {
33
+ <<"user/john.doe/Calendar/Personal Calendar@example.org">>,
34
+ { <<"GETANNOTATION \"user/john.doe/Calendar/Personal Calendar@example.org\" \"*\" \"value.shared\"">>, multiline_response }
35
+ }
36
],
37
- lists:foldl(fun({ Mailbox, Command }, Acc) -> [?_assertEqual(Command, eimap_command_annotation:new(Mailbox))|Acc] end, [], Data).
38
+ lists:foldl(fun({ Mailbox, Command }, Acc) -> [?_assertEqual(Command, eimap_command_annotation:new_command(Mailbox))|Acc] end, [], Data).
39
40
erlang-eimap-0.1.5.tar.gz/test/eimap_command_capability_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_capability_tests.erl
Changed
57
1
2
[
3
% { Binary Response, Binary Tag, Parsed Results }
4
{
5
+ parse_serverid,
6
<<"* OK [CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE STARTTLS LOGINDISABLED] acme.com Cyrus IMAP 2.5.5.5-Kolab-2.5.5-5.1.el6.kolab_14 server ready\r\n">>,
7
<<>>,
8
{ fini, { <<"IMAP4rev1 LITERAL+ ID ENABLE STARTTLS LOGINDISABLED">>, <<"acme.com Cyrus IMAP 2.5.5.5-Kolab-2.5.5-5.1.el6.kolab_14 server ready">> } }
9
},
10
{
11
+ ok,
12
<<"* CAPABILITY IMAP4rev1 LITERAL+ ID ENABLE STARTTLS AUTH=PLAIN AUTH=LOGIN SASL-IR\r\nabcd OK CAPABILITY COMPLETED\r\n">>,
13
<<"abcd">>,
14
- { fini, <<"IMAP4rev1 LITERAL+ ID ENABLE STARTTLS AUTH=PLAIN AUTH=LOGIN SASL-IR">> }
15
+ { fini, [<<"IMAP4rev1 LITERAL+ ID ENABLE STARTTLS AUTH=PLAIN AUTH=LOGIN SASL-IR">>] }
16
},
17
{
18
+ ok,
19
<<"other stuff\r\n">>,
20
<<"abcd">>,
21
- <<"other stuff\r\n">>
22
+ { more, { <<>>, [], eimap_command_capability } }
23
},
24
{
25
+ ok,
26
<<"abcd BAD Uh uh uh\r\n">>,
27
<<"abcd">>,
28
{ error, <<"Uh uh uh">> }
29
},
30
{
31
+ ok,
32
<<"abcd NO Uh uh uh\r\n">>,
33
<<"abcd">>,
34
{ error, <<"Uh uh uh">> }
35
}
36
],
37
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_capability:parse(Response, Tag))|Acc] end, [], Data).
38
+ lists:foldl(fun({ InitArgs, Response, Tag, Parsed }, Acc) ->
39
+ { _Command, ResponseType } = eimap_command_capability:new_command(InitArgs),
40
+ [?_assertEqual(Parsed, eimap_command:parse_response(ResponseType, Response, Tag, eimap_command_capability))|Acc] end, [], Data).
41
42
new_test_() ->
43
Data =
44
[
45
% input, output
46
- { <<>>, <<"CAPABILITY">> },
47
- { true, <<"CAPABILITY">> },
48
- { [], <<"CAPABILITY">> }
49
+ { parse_serverid, { <<"CAPABILITY">>, single_line_response } },
50
+ { <<>>, { <<"CAPABILITY">>, multiline_response } },
51
+ { true, { <<"CAPABILITY">>, multiline_response } },
52
+ { [], { <<"CAPABILITY">>, multiline_response } }
53
],
54
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_capability:new(Params))|Acc] end, [], Data).
55
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_capability:new_command(Params))|Acc] end, [], Data).
56
57
erlang-eimap-0.1.5.tar.gz/test/eimap_command_compress_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_compress_tests.erl
Changed
25
1
2
{ error, <<"Uh uh uh">> }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_compress:parse(Response, Tag))|Acc] end, [], Data).
6
+ InitArgs = ok,
7
+ { _Command, ResponseType } = eimap_command_compress:new_command(InitArgs),
8
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(ResponseType, Response, Tag, eimap_command_compress))|Acc] end, [], Data).
9
10
new_test_() ->
11
Data =
12
[
13
% input, output
14
- { <<>>, <<"COMPRESS DEFLATE">> },
15
- { true, <<"COMPRESS DEFLATE">> },
16
- { [], <<"COMPRESS DEFLATE">> }
17
+ { ok, { <<"COMPRESS DEFLATE">>, single_line_response } },
18
+ { <<>>, { <<"COMPRESS DEFLATE">>, single_line_response } },
19
+ { true, { <<"COMPRESS DEFLATE">>, single_line_response } },
20
+ { [], { <<"COMPRESS DEFLATE">>, single_line_response } }
21
],
22
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_compress:new(Params))|Acc] end, [], Data).
23
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_compress:new_command(Params))|Acc] end, [], Data).
24
25
erlang-eimap-0.1.5.tar.gz/test/eimap_command_getmetadata_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_getmetadata_tests.erl
Changed
31
1
2
{ error, <<"Uh uh uh">> }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_getmetadata:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) ->
7
+ [?_assertEqual(Parsed, eimap_command:parse_response(multiline_response, Response, Tag, eimap_command_getmetadata))|Acc] end, [], Data).
8
9
new_test_() ->
10
Data =
11
[
12
% input, output
13
- { { <<>> }, <<"GETMETADATA (DEPTH infinity) \"\"">> },
14
- { { <<>>, [<<"/shared/comment">>, "/private/comment"] }, <<"GETMETADATA (DEPTH infinity) \"\" (/shared/comment /private/comment)">> },
15
- { { <<"/my/folder">>, [<<"/shared/comment">>, "/private/comment"] }, <<"GETMETADATA (DEPTH infinity) \"/my/folder\" (/shared/comment /private/comment)">> },
16
- { { "/my/folder", [<<"/shared/comment">>, "/private/comment"] }, <<"GETMETADATA (DEPTH infinity) \"/my/folder\" (/shared/comment /private/comment)">> },
17
- { { <<"/my/folder">> }, <<"GETMETADATA (DEPTH infinity) \"/my/folder\"">> }
18
+ { { <<>> }, { <<"GETMETADATA (DEPTH infinity) \"\"">>, multiline_response } },
19
+ { { <<>>, [<<"/shared/comment">>, "/private/comment"] }, { <<"GETMETADATA (DEPTH infinity) \"\" (/shared/comment /private/comment)">>, multiline_response } },
20
+ { { <<"/my/folder">>, [<<"/shared/comment">>, "/private/comment"] }, { <<"GETMETADATA (DEPTH infinity) \"/my/folder\" (/shared/comment /private/comment)">>, multiline_response } },
21
+ { { "/my/folder", [<<"/shared/comment">>, "/private/comment"] }, { <<"GETMETADATA (DEPTH infinity) \"/my/folder\" (/shared/comment /private/comment)">>, multiline_response } },
22
+ { { <<"/my/folder">> }, { <<"GETMETADATA (DEPTH infinity) \"/my/folder\"">>, multiline_response } },
23
+ { { "/my/folder", [], 10, 100 }, { <<"GETMETADATA (DEPTH 10 MAXSIZE 100) \"/my/folder\"">>, multiline_response } },
24
+ { { <<"/my/folder">>, [], 10, 100 }, { <<"GETMETADATA (DEPTH 10 MAXSIZE 100) \"/my/folder\"">>, multiline_response } },
25
+ { { <<"/my/folder">>, [], 10, none}, { <<"GETMETADATA (DEPTH 10) \"/my/folder\"">>, multiline_response } },
26
+ { { <<"/my/folder">>, [], none, 100 }, { <<"GETMETADATA (MAXSIZE 100) \"/my/folder\"">>, multiline_response } }
27
],
28
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_getmetadata:new(Params))|Acc] end, [], Data).
29
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_getmetadata:new_command(Params))|Acc] end, [], Data).
30
31
erlang-eimap-0.1.5.tar.gz/test/eimap_command_login_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_login_tests.erl
Changed
20
1
2
{ error, <<"Uh uh uh">> }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_login:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(multiline_response, Response, Tag, eimap_command_login))|Acc] end, [], Data).
7
8
new_test_() ->
9
Data =
10
[
11
% input, output
12
- { { <<"aseigo">>, <<"oohyeah">> }, <<"LOGIN aseigo oohyeah">> },
13
- { { "aseigo", "oohyeah" }, <<"LOGIN aseigo oohyeah">> }
14
+ { { <<"aseigo">>, <<"oohyeah">> }, { <<"LOGIN aseigo oohyeah">>, multiline_response } },
15
+ { { "aseigo", "oohyeah" }, { <<"LOGIN aseigo oohyeah">>, multiline_response } }
16
],
17
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_login:new(Params))|Acc] end, [], Data).
18
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_login:new_command(Params))|Acc] end, [], Data).
19
20
erlang-eimap-0.1.5.tar.gz/test/eimap_command_logout_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_logout_tests.erl
Changed
22
1
2
{ close_socket, { error, <<"Uh uh uh">> } }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_logout:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(multiline_response, Response, Tag, eimap_command_logout))|Acc] end, [], Data).
7
8
new_test_() ->
9
Data =
10
[
11
% input, output
12
- { <<>>, <<"LOGOUT">> },
13
- { true, <<"LOGOUT">> },
14
- { [], <<"LOGOUT">> }
15
+ { <<>>, { <<"LOGOUT">>, multiline_response } },
16
+ { true, { <<"LOGOUT">>, multiline_response } },
17
+ { [], { <<"LOGOUT">>, multiline_response } }
18
],
19
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_logout:new(Params))|Acc] end, [], Data).
20
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_logout:new_command(Params))|Acc] end, [], Data).
21
22
erlang-eimap-0.1.5.tar.gz/test/eimap_command_namespace_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_namespace_tests.erl
Changed
22
1
2
{ error, <<"Uh uh uh">> }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_namespace:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(multiline_response, Response, Tag, eimap_command_namespace))|Acc] end, [], Data).
7
8
new_test_() ->
9
Data =
10
[
11
% input, output
12
- { <<>>, <<"NAMESPACE">> },
13
- { true, <<"NAMESPACE">> },
14
- { [], <<"NAMESPACE">> }
15
+ { <<>>, { <<"NAMESPACE">>, multiline_response } },
16
+ { true, { <<"NAMESPACE">>, multiline_response } },
17
+ { [], { <<"NAMESPACE">>, multiline_response } }
18
],
19
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_namespace:new(Params))|Acc] end, [], Data).
20
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_namespace:new_command(Params))|Acc] end, [], Data).
21
22
erlang-eimap-0.2.4.tar.gz/test/eimap_command_noop_tests.erl
Added
71
1
2
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
+%%
4
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
+%%
6
+%% This program is free software: you can redistribute it and/or modify
7
+%% it under the terms of the GNU General Public License as published by
8
+%% the Free Software Foundation, either version 3 of the License, or
9
+%% (at your option) any later version.
10
+%%
11
+%% This program is distributed in the hope that it will be useful,
12
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+%% GNU General Public License for more details.
15
+%%
16
+%% You should have received a copy of the GNU General Public License
17
+%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+-module(eimap_command_noop_tests).
20
+-include_lib("eunit/include/eunit.hrl").
21
+
22
+parse_test_() ->
23
+ Data =
24
+ [
25
+ % { Binary Response, Binary Tag, Parsed Results }
26
+ {
27
+ ok,
28
+ <<"* 100 EXISTS\r\n* 22 EXPUNGE\r\n* 14 FETCH (FLAGS (\\\\Answered \\\\Flagged \\\\Draft \\\\Deleted \\\\Seen))\r\n* 3 Recent\r\nabcd OK Completed\r\n">>,
29
+ <<"abcd">>,
30
+ { fini,
31
+ [
32
+ { recent, 3 },
33
+ { fetch, 14 },
34
+ { expunge, 22 },
35
+ { exists, 100 }
36
+ ]
37
+ }
38
+ },
39
+ {
40
+ ok,
41
+ <<"abcd OK Completed\r\n">>,
42
+ <<"abcd">>,
43
+ { fini, [] }
44
+ },
45
+ {
46
+ ok,
47
+ <<"abcd BAD Uh uh uh\r\n">>,
48
+ <<"abcd">>,
49
+ { error, <<"Uh uh uh">> }
50
+ },
51
+ {
52
+ ok,
53
+ <<"abcd NO Uh uh uh\r\n">>,
54
+ <<"abcd">>,
55
+ { error, <<"Uh uh uh">> }
56
+ }
57
+ ],
58
+ lists:foldl(fun({ InitArgs, ServerResponse, Tag, Parsed }, Acc) ->
59
+ { _Command, ResponseType } = eimap_command_noop:new_command(InitArgs),
60
+ [?_assertEqual(Parsed, eimap_command:parse_response(ResponseType, ServerResponse, Tag, eimap_command_noop))|Acc] end, [], Data).
61
+
62
+new_test_() ->
63
+ Data =
64
+ [
65
+ % input, output
66
+ { ok, { <<"NOOP">>, multiline_response } },
67
+ { <<>>, { <<"NOOP">>, multiline_response } }
68
+ ],
69
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_noop:new_command(Params))|Acc] end, [], Data).
70
+
71
erlang-eimap-0.1.5.tar.gz/test/eimap_command_starttls_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_starttls_tests.erl
Changed
22
1
2
{ error, <<"Uh uh uh">> }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_starttls:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(single_line_response, Response, Tag, eimap_command_starttls))|Acc] end, [], Data).
7
8
new_test_() ->
9
Data =
10
[
11
% input, output
12
- { <<>>, <<"STARTTLS">> },
13
- { true, <<"STARTTLS">> },
14
- { [], <<"STARTTLS">> }
15
+ { <<>>, { <<"STARTTLS">>, single_line_response } },
16
+ { true, { <<"STARTTLS">>, single_line_response } },
17
+ { [], { <<"STARTTLS">>, single_line_response } }
18
],
19
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_starttls:new(Params))|Acc] end, [], Data).
20
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_starttls:new_command(Params))|Acc] end, [], Data).
21
22
erlang-eimap-0.1.5.tar.gz/test/eimap_command_status_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_command_status_tests.erl
Changed
40
1
2
{ fini, [{ uidvalidity, 44292 }, { recent, 1 }] }
3
}
4
],
5
- lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command_status:parse(Response, Tag))|Acc] end, [], Data).
6
+ lists:foldl(fun({ Response, Tag, Parsed }, Acc) -> [?_assertEqual(Parsed, eimap_command:parse_response(multiline_response, Response, Tag, eimap_command_status))|Acc] end, [], Data).
7
8
new_test_() ->
9
Data =
10
[
11
% input, output
12
- { { "INBOX", [] }, <<"STATUS INBOX (MESSAGES)">> },
13
- { { <<"INBOX">>, [] }, <<"STATUS INBOX (MESSAGES)">> },
14
- { { <<>>, [messages] }, <<"STATUS INBOX (MESSAGES)">> },
15
- { { <<"">>, [messages] }, <<"STATUS INBOX (MESSAGES)">> },
16
+ { { "INBOX", [] },
17
+ { <<"STATUS INBOX (MESSAGES)">>, multiline_response } },
18
+ { { <<"INBOX">>, [] },
19
+ { <<"STATUS INBOX (MESSAGES)">>, multiline_response } },
20
+ { { <<>>, [messages] },
21
+ { <<"STATUS INBOX (MESSAGES)">>, multiline_response } },
22
+ { { <<"">>, [messages] },
23
+ { <<"STATUS INBOX (MESSAGES)">>, multiline_response } },
24
{ { <<"/my/folder">>, [messages, recent, uidnext, uidvalidity, unseen] },
25
- <<"STATUS /my/folder (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)">> },
26
+ { <<"STATUS /my/folder (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)">>, multiline_response } },
27
{ { <<"/my/folder">>, [uidnext, recent, uidvalidity, unseen] },
28
- <<"STATUS /my/folder (UIDNEXT RECENT UIDVALIDITY UNSEEN)">> },
29
+ { <<"STATUS /my/folder (UIDNEXT RECENT UIDVALIDITY UNSEEN)">>, multiline_response } },
30
{ { <<"/my/folder">>, [garbage, unseen] },
31
- <<"STATUS /my/folder (UNSEEN)">> },
32
- { { <<"/my/folder">>, [garbage] }, <<"STATUS /my/folder (MESSAGES)">> }
33
+ { <<"STATUS /my/folder (UNSEEN)">>, multiline_response } },
34
+ { { <<"/my/folder">>, [garbage] },
35
+ { <<"STATUS /my/folder (MESSAGES)">>, multiline_response } }
36
],
37
- lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_status:new(Params))|Acc] end, [], Data).
38
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_status:new_command(Params))|Acc] end, [], Data).
39
40
erlang-eimap-0.2.4.tar.gz/test/eimap_command_switch_folder_tests.erl
Added
74
1
2
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
+%%
4
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
+%%
6
+%% This program is free software: you can redistribute it and/or modify
7
+%% it under the terms of the GNU General Public License as published by
8
+%% the Free Software Foundation, either version 3 of the License, or
9
+%% (at your option) any later version.
10
+%%
11
+%% This program is distributed in the hope that it will be useful,
12
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+%% GNU General Public License for more details.
15
+%%
16
+%% You should have received a copy of the GNU General Public License
17
+%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+-module(eimap_command_switch_folder_tests).
20
+-include_lib("eunit/include/eunit.hrl").
21
+
22
+parse_test_() ->
23
+ Data =
24
+ [
25
+ % { Binary Response, Binary Tag, Parsed Results }
26
+ {
27
+ <<"Trash">>,
28
+ <<"* 0 EXISTS\r\n* 0 RECENT\r\n* FLAGS (\\\\Answered \\\\Flagged \\\\Draft \\\\Deleted \\\\Seen)\r\n* OK [PERMANENTFLAGS ()] Ok\r\n* OK [UIDVALIDITY 1447439786] Ok\r\n* OK [UIDNEXT 1] Ok\r\n* OK [HIGHESTMODSEQ 1] Ok\r\n* OK [URLMECH INTERNAL] Ok>\r\n* OK [ANNOTATIONS 65536] Ok\r\nabcd OK [READ-WRITE] SELECT completed\r\n">>,
29
+ <<"abcd">>,
30
+ { fini,
31
+ [
32
+ { writeable, true },
33
+ { annotations, 65536 },
34
+ { url_mech, internal },
35
+ { highest_mod_seq, 1 },
36
+ { uid_next, 1 },
37
+ { uid_validity, 1447439786 },
38
+ { permanent_flags, [] },
39
+ { flags, [<<"\\\\Answered">>, <<"\\\\Flagged">>, <<"\\\\Draft">>, <<"\\\\Deleted">>, <<"\\\\Seen">>] },
40
+ { recent, 0 },
41
+ { exists, 0 }
42
+ ]
43
+ }
44
+ },
45
+ {
46
+ <<"Nope">>,
47
+ <<"abcd BAD Uh uh uh\r\n">>,
48
+ <<"abcd">>,
49
+ { error, <<"Uh uh uh">> }
50
+ },
51
+ {
52
+ <<"ALsoWrong">>,
53
+ <<"abcd NO Uh uh uh\r\n">>,
54
+ <<"abcd">>,
55
+ { error, <<"Uh uh uh">> }
56
+ }
57
+ ],
58
+ lists:foldl(fun({ InitArgs, ServerResponse, Tag, Parsed }, Acc) ->
59
+ { _Command, ResponseType } = eimap_command_switch_folder:new_command(InitArgs),
60
+ [?_assertEqual(Parsed, eimap_command:parse_response(ResponseType, ServerResponse, Tag, eimap_command_switch_folder))|Acc] end, [], Data).
61
+
62
+new_test_() ->
63
+ Data =
64
+ [
65
+ % input, output
66
+ { "Trash", { <<"SELECT \"Trash\"">>, all_multiline_response } },
67
+ { <<"Trash">>, { <<"SELECT \"Trash\"">>, all_multiline_response } },
68
+ { { <<"Trash">>, select }, { <<"SELECT \"Trash\"">>, all_multiline_response } },
69
+ { { <<"Trash">>, examine }, { <<"EXAMINE \"Trash\"">>, all_multiline_response } },
70
+ { { "Trash", examine }, { <<"EXAMINE \"Trash\"">>, all_multiline_response } }
71
+ ],
72
+ lists:foldl(fun({ Params, Command }, Acc) -> [?_assertEqual(Command, eimap_command_switch_folder:new_command(Params))|Acc] end, [], Data).
73
+
74
erlang-eimap-0.2.4.tar.gz/test/eimap_command_tests.erl
Added
67
1
2
+%% Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
3
+%%
4
+%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
5
+%%
6
+%% This program is free software: you can redistribute it and/or modify
7
+%% it under the terms of the GNU General Public License as published by
8
+%% the Free Software Foundation, either version 3 of the License, or
9
+%% (at your option) any later version.
10
+%%
11
+%% This program is distributed in the hope that it will be useful,
12
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+%% GNU General Public License for more details.
15
+%%
16
+%% You should have received a copy of the GNU General Public License
17
+%% along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+-module(eimap_command_tests).
20
+-include_lib("eunit/include/eunit.hrl").
21
+-export([process_line/2, formulate_response/2]).
22
+
23
+% c("test/eimap_command_tests.erl"). eunit:test(eimap_command).
24
+
25
+process_line(Data, Acc) -> [{ marked, Data } | Acc].
26
+formulate_response(Result, Data) -> eimap_command:formulate_response(Result, lists:reverse(Data)).
27
+
28
+
29
+literal_continuations_test_() ->
30
+ Data =
31
+ [
32
+ % input, output
33
+ % { Binary Response, Binary Tag, Parsed Results }
34
+ {
35
+ <<"* STATUS 1 (MESSAGES 231 {14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
36
+ <<"abcd">>,
37
+ { fini, [ { marked, <<"* STATUS 1 (MESSAGES 231 UIDNEXT 44292)">> } ] }
38
+ },
39
+ {
40
+ <<"* STATUS 1a (MESSAGES 231 {14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation">>,
41
+ <<"abcd">>,
42
+ { more, { <<"abcd OK Begin TLS negotiation">>, [ { marked, <<"* STATUS 1a (MESSAGES 231 UIDNEXT 44292)">> } ], ?MODULE } }
43
+ },
44
+ {
45
+ <<"* STATUS 2 (MESSAGES 231 {14}\r\n">>,
46
+ <<"abcd">>,
47
+ { more, { <<"* STATUS 2 (MESSAGES 231 {14}">>, [], ?MODULE } }
48
+ },
49
+ {
50
+ <<"* STATUS 3 (MESSAGES 231 {h14}\r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
51
+ <<"abcd">>,
52
+ { fini, [ { marked, <<"* STATUS 3 (MESSAGES 231 {h14}">> }, { marked, <<"UIDNEXT 44292)">> } ] }
53
+ },
54
+ {
55
+ <<"* STATUS 4 (MESSAGES 231 \r\nUIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
56
+ <<"abcd">>,
57
+ { fini, [ { marked, <<"* STATUS 4 (MESSAGES 231 ">> }, { marked, <<"UIDNEXT 44292)">> } ] }
58
+ },
59
+ {
60
+ <<"* STATUS 5 (MESSAGES 231 UIDNEXT 44292)\r\nabcd OK Begin TLS negotiation now\r\n">>,
61
+ <<"abcd">>,
62
+ { fini, [ { marked, <<"* STATUS 5 (MESSAGES 231 UIDNEXT 44292)">> } ] }
63
+ }
64
+ ],
65
+ lists:foldl(fun({ Binary, Tag, Result }, Acc) -> [?_assertEqual(Result, eimap_command:parse_response(multiline_response, Binary, Tag, ?MODULE))|Acc] end, [], Data).
66
+
67
erlang-eimap-0.1.5.tar.gz/test/eimap_uidset_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_uidset_tests.erl
Changed
27
1
2
3
% c("test/eimap_uidset_tests.erl"). eunit:test(eimap_uidset).
4
5
+new_test_() ->
6
+ Data =
7
+ [
8
+ % input, output
9
+ { [4], <<"4">> },
10
+ { [1, 2, 3, 4], <<"1,2,3,4">> },
11
+ { [-1, 1, 2, 3, 4], <<"1,2,3,4">> },
12
+ { [1, {100, 10}, 2, 3, 4], <<"1,10:100,2,3,4">> },
13
+ { [1, {10, 100}, 2, 3, 4], <<"1,10:100,2,3,4">> },
14
+ { [1, {-10, 100}, 2, 3, 4], <<"1,2,3,4">> },
15
+ { [1, {-100, 10}, 2, 3, 4], <<"1,2,3,4">> },
16
+ { [1, {-10, 100}, 2, 3, 4], <<"1,2,3,4">> },
17
+ { [1, {"alpha", 100}, <<"random_binary">>, some_atom, 2, 3, 4], <<"1,2,3,4">> },
18
+ { <<"1,2,3,4">>, <<>> },
19
+ { other, <<>> },
20
+ { [], <<>> }
21
+ ],
22
+ lists:foldl(fun({ List, Binary }, Acc) -> [?_assertEqual(Binary, eimap_uidset:uid_list_to_binary(List))|Acc] end, [], Data).
23
+
24
single_value_test_() ->
25
[
26
?_assertEqual([1], iterate_uidset(eimap_uidset:parse(<<"1,">>))),
27
erlang-eimap-0.1.5.tar.gz/test/eimap_utils_tests.erl -> erlang-eimap-0.2.4.tar.gz/test/eimap_utils_tests.erl
Changed
56
1
2
Data =
3
[
4
{ { <<>>, <<>>, <<>> }, <<>> },
5
+ { { <<".">>, <<"LIST">>, <<"\"\" \"*\"">> }, <<". LIST \"\" \"*\"">> },
6
{ { <<"1">>, <<"STARTTLS">>, <<>> }, <<"1 STARTTLS">> },
7
{ { <<"1">>, <<"STARTTLS">>, <<>> }, <<"1 STARTTLS\r\n">> },
8
{ { <<"3">>, <<"ID">>, <<"(\"name\" \"Thunderbird\" \"version\" \"38.3.0\")">> }, <<"3 ID (\"name\" \"Thunderbird\" \"version\" \"38.3.0\")">> }
9
10
Tag = <<"abcd">>,
11
Data =
12
[
13
- { <<Tag/binary, " Indeed\r\n">>, true },
14
- { <<Tag/binary, " Indeed">>, true },
15
- { <<"one">>, false },
16
- { <<"* Yeah baby">>, false }
17
+ { <<Tag/binary, " Indeed\r\n">>, tagged },
18
+ { <<Tag/binary, " Indeed">>, tagged },
19
+ { <<"one">>, untagged },
20
+ { <<"* Yeah baby">>, untagged }
21
],
22
lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:is_tagged_response(Input, Tag)) | Acc] end, [], Data).
23
24
25
],
26
lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:only_full_lines(Input)) | Acc] end, [], Data).
27
28
+parse_flags_test_() ->
29
+ Data =
30
+ [
31
+ { <<"()">>, [] },
32
+ { <<>>, [] },
33
+ { <<"\\\\Answered \\\\Flagged \\\\Draft \\\\Deleted \\\\Seen">>, [<<"\\\\Answered">>, <<"\\\\Flagged">>, <<"\\\\Draft">>, <<"\\\\Deleted">>, <<"\\\\Seen">> ] },
34
+ { <<"(\\\\Answered \\\\Flagged \\\\Draft \\\\Deleted \\\\Seen)">>, [<<"\\\\Answered">>, <<"\\\\Flagged">>, <<"\\\\Draft">>, <<"\\\\Deleted">>, <<"\\\\Seen">> ] },
35
+ { "(\\\\Answered \\\\Flagged \\\\Draft \\\\Deleted \\\\Seen)", [<<"\\\\Answered">>, <<"\\\\Flagged">>, <<"\\\\Draft">>, <<"\\\\Deleted">>, <<"\\\\Seen">> ] }
36
+ ],
37
+ lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:parse_flags(Input)) | Acc] end, [], Data).
38
+
39
+num_literal_continuation_bytes_test_() ->
40
+ Data =
41
+ [
42
+ { <<"abcd">>, { <<"abcd">>, 0 } },
43
+ { <<"abcd{5}">>, { <<"abcd">>, 5 } },
44
+ { <<"abcd{100}">>, { <<"abcd">>, 100 } },
45
+ { <<"123abcd{100}">>, { <<"123abcd">>, 100 } },
46
+ { <<"ab{123abcd{100}">>, { <<"ab{123abcd">>, 100 } },
47
+ { <<"ab{123abcd{1{00}">>, { <<"ab{123abcd{1">>, 0 } },
48
+ { <<"abcd{aa0}">>, { <<"abcd{aa0}">>, 0 } },
49
+ { <<"abcd{10aa0}">>, { <<"abcd{10aa0}">>, 0 } },
50
+ { <<"abcd100}">>, { <<"abcd100}">>, 0 } },
51
+ { <<"abcd100}">>, { <<"abcd100}">>, 0 } }
52
+ ],
53
+ lists:foldl(fun({ Input, Output}, Acc) -> [?_assertEqual(Output, eimap_utils:num_literal_continuation_bytes(Input)) | Acc] end, [], Data).
54
+
55
+
56
erlang-eimap.dsc
Added
16
1
2
+Format: 1.0
3
+Source: erlang-eimap
4
+Binary: erlang-eimap
5
+Architecture: any
6
+Version: 0.2.4-1
7
+Maintainer: Christoph Erhardt <kolab@sicherha.de>
8
+Homepage: https://git.kolab.org/diffusion/EI/eimap.git
9
+Standards-Version: 3.9.6
10
+Build-Depends: debhelper (>= 9), erlang-goldrush (>= 0.1.7), erlang-lager (>= 2.2.0), rebar (>= 2.5.1)
11
+Package-List:
12
+ erlang-eimap deb devel extra
13
+Files:
14
+ 00000000000000000000000000000000 0 erlang-eimap-0.2.4.tar.gz
15
+ 00000000000000000000000000000000 0 debian.tar.gz
16
repack.sh
Deleted
10
1
2
-#!/bin/bash
3
-
4
-tar zxvf eimap-*.*.*.tar.gz
5
-target=$(ls -1d eimap-*.*.*/ | xargs -n 1 basename | sed -e 's/eimap/erlang-eimap/g')
6
-rm -rf eimap-*.*.*.tar.gz
7
-rm -rf ${target}
8
-mv eimap-*.*.* ${target}
9
-tar czvf ${target}.tar.gz ${target}
10