aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile4
-rw-r--r--lib/asn1/src/asn1_db.erl16
-rw-r--r--lib/asn1/src/asn1ct.erl4
-rw-r--r--lib/asn1/src/asn1ct_check.erl88
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Constraints.py1
-rw-r--r--lib/asn1/test/testConstraints.erl19
-rw-r--r--lib/common_test/doc/src/basics_chapter.xml5
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml28
-rw-r--r--lib/common_test/doc/src/ct_run.xml3
-rw-r--r--lib/common_test/doc/src/getting_started_chapter.xml7
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml12
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml87
-rw-r--r--lib/eldap/.gitignore4
-rw-r--r--lib/eldap/AUTHORS7
-rw-r--r--lib/eldap/LICENSE21
-rw-r--r--lib/eldap/Makefile39
-rw-r--r--lib/eldap/README33
-rw-r--r--lib/eldap/asn1/ELDAPv3.asn1278
-rw-r--r--lib/eldap/doc/html/.gitignore0
-rw-r--r--lib/eldap/doc/man3/.gitignore0
-rw-r--r--lib/eldap/doc/pdf/.gitignore0
-rw-r--r--lib/eldap/doc/src/Makefile115
-rw-r--r--lib/eldap/doc/src/book.xml47
-rw-r--r--lib/eldap/doc/src/eldap.xml342
-rw-r--r--lib/eldap/doc/src/fascicules.xml18
-rw-r--r--lib/eldap/doc/src/note.gifbin0 -> 1539 bytes
-rw-r--r--lib/eldap/doc/src/notes.xml38
-rw-r--r--lib/eldap/doc/src/ref_man.xml37
-rw-r--r--lib/eldap/doc/src/release_notes.xml37
-rw-r--r--lib/eldap/doc/src/usersguide.xml38
-rw-r--r--lib/eldap/doc/src/warning.gifbin0 -> 1498 bytes
-rw-r--r--lib/eldap/ebin/.gitignore0
-rw-r--r--lib/eldap/include/eldap.hrl33
-rw-r--r--lib/eldap/info2
-rw-r--r--lib/eldap/src/Makefile110
-rw-r--r--lib/eldap/src/eldap.app.src8
-rw-r--r--lib/eldap/src/eldap.appup.src6
-rw-r--r--lib/eldap/src/eldap.erl1114
-rw-r--r--lib/eldap/test/Makefile83
-rw-r--r--lib/eldap/test/eldap.spec1
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl222
-rw-r--r--lib/eldap/test/ldap_server/slapd.conf14
-rw-r--r--lib/eldap/vsn.mk1
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl7
-rw-r--r--lib/orber/doc/src/ch_install.xml18
-rw-r--r--lib/reltool/src/reltool.hrl82
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl4
-rw-r--r--lib/wx/src/gen/wxCheckListBox.erl2
-rw-r--r--lib/wx/src/gen/wxChoice.erl6
-rw-r--r--lib/wx/src/gen/wxComboBox.erl6
-rw-r--r--lib/wx/src/gen/wxControlWithItems.erl2
-rw-r--r--lib/wx/src/gen/wxFileDataObject.erl2
-rw-r--r--lib/wx/src/gen/wxFileDialog.erl4
-rw-r--r--lib/wx/src/gen/wxGridCellChoiceEditor.erl4
-rw-r--r--lib/wx/src/gen/wxListBox.erl10
-rw-r--r--lib/wx/src/gen/wxMultiChoiceDialog.erl4
-rw-r--r--lib/wx/src/gen/wxRadioBox.erl8
-rw-r--r--lib/wx/src/gen/wxSingleChoiceDialog.erl4
58 files changed, 2917 insertions, 168 deletions
diff --git a/lib/Makefile b/lib/Makefile
index aa4e074830..3753bd165b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2012. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@ ifdef BUILD_ALL
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
xmerl edoc eunit ssh inviso typer erl_docgen \
- percept dialyzer hipe
+ percept eldap dialyzer hipe
EXTRA_FILE := $(wildcard EXTRA-APPLICATIONS)
EXTRA_APPLICATIONS := $(if $(EXTRA_FILE),$(shell cat $(EXTRA_FILE)))
endif
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 0862ef6e02..843fc66c9c 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -48,11 +48,19 @@ dbstop() -> Resp = req(stop), erase(?MODULE), Resp.
%% Internal functions
req(Request) ->
- get(?MODULE) ! {self(), Request},
- receive {?MODULE, Reply} -> Reply after 5000 -> exit(db_timeout) end.
+ DbPid = get(?MODULE),
+ Ref = erlang:monitor(process,DbPid),
+ get(?MODULE) ! {{Ref, self()}, Request},
+ receive
+ {{Ref,?MODULE}, Reply} ->
+ erlang:demonitor(Ref,[flush]),
+ Reply;
+ {'DOWN',Ref,_,_,Info} ->
+ exit({db_error,Info})
+ end.
-reply(From, Response) ->
- From ! {?MODULE, Response}.
+reply({Ref,From}, Response) ->
+ From ! {{Ref,?MODULE}, Response}.
init(Parent, Includes) ->
MRef = erlang:monitor(process, Parent),
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 2d17f73a2c..99755a95a6 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -816,7 +816,9 @@ check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) ->
asn1_db:dbsave(DbFile,M#module.name),
verbose("--~p--~n",[{generated,DbFile}],Options),
{true,{M,NewM,GenTypeOrVal}}
- end
+ end;
+ ErrorList = {error,_} ->
+ {false,ErrorList}
end;
check({false,M},_,_,_,_,_,_,_) ->
{false,M}.
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index c223561f12..187339fb53 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -4399,22 +4399,22 @@ constraint_union(_S,C) ->
constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
AunionB = constraint_union_vr([A,B]),
- constraint_union1(S,Rest,AunionB++Acc);
+ constraint_union1(S,Rest,Acc ++ AunionB);
constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
AunionB = constraint_union_sv(S,[A,B]),
- constraint_union1(S,Rest,AunionB++Acc);
+ constraint_union1(S,Rest,Acc ++ AunionB);
constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) ->
AunionB = union_sv_vr(S,A,B),
- constraint_union1(S,Rest,AunionB++Acc);
+ constraint_union1(S,Rest,Acc ++ AunionB);
constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) ->
AunionB = union_sv_vr(S,B,A),
- constraint_union1(S,Rest,AunionB++Acc);
+ constraint_union1(S,Rest,Acc ++ AunionB);
constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints
constraint_union1(S,Rest,Acc);
constraint_union1(S,[A|Rest],Acc) ->
constraint_union1(S,Rest,[A|Acc]);
constraint_union1(_S,[],Acc) ->
- lists:reverse(Acc).
+ Acc.
constraint_union_sv(_S,SV) ->
Values=lists:map(fun({_,V})->V end,SV),
@@ -4467,63 +4467,33 @@ constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1->
constraint_union_vr([VR|Rest],Acc) ->
constraint_union_vr(Rest,[VR|Acc]).
-union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',VR={Lb,Ub}})
+union_sv_vr(_S,{'SingleValue',SV},VR)
when is_integer(SV) ->
- case is_int_in_vr(SV,C2) of
- true -> [C2];
- _ ->
- case VR of
- {'MIN',Ub} when SV==Ub+1 -> [{'ValueRange',{'MIN',SV}}];
- {Lb,'MAX'} when SV==Lb-1 -> [{'ValueRange',{SV,'MAX'}}];
- {Lb,Ub} when SV==Ub+1 -> [{'ValueRange',{Lb,SV}}];
- {Lb,Ub} when SV==Lb-1 -> [{'ValueRange',{SV,Ub}}];
- _ ->
- [C1,C2]
- end
- end;
-union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',{_Lb,_Ub}})
+ union_sv_vr(_S,{'SingleValue',[SV]},VR);
+union_sv_vr(_S,{'SingleValue',SV},{'ValueRange',{VLb,VUb}})
when is_list(SV) ->
- case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
- [] -> [C2];
- L ->
- case expand_vr(L,C2) of
- {[],C3} -> [C3];
- {L,C2} -> [C1,C2];
- {[Val],C3} -> [{'SingleValue',Val},C3];
- {L2,C3} -> [{'SingleValue',L2},C3]
- end
- end.
-
-expand_vr(L,VR={_,{Lb,Ub}}) ->
- case lower_Lb(L,Lb) of
- false ->
- case higher_Ub(L,Ub) of
- false ->
- {L,VR};
- {L1,UbNew} ->
- expand_vr(L1,{'ValueRange',{Lb,UbNew}})
- end;
- {L1,LbNew} ->
- expand_vr(L1,{'ValueRange',{LbNew,Ub}})
- end.
-
-lower_Lb(_,'MIN') ->
- false;
-lower_Lb(L,Lb) ->
- remove_val_from_list(Lb - 1,L).
-
-higher_Ub(_,'MAX') ->
- false;
-higher_Ub(L,Ub) ->
- remove_val_from_list(Ub + 1,L).
+ L = lists:sort(SV++[VLb,VUb]),
+ {Lb,L1} = case lists:member('MIN',L) of
+ true -> {'MIN',L--['MIN']}; % remove 'MIN' so it does not disturb
+ false -> {hd(L),tl(L)}
+ end,
+ Ub = case lists:member('MAX',L1) of
+ true -> 'MAX';
+ false -> lists:last(L1)
+ end,
+ case SV of
+ [H] -> H;
+ _ -> SV
+ end,
+ %% for now we through away the Singlevalues so that they don't disturb
+ %% in the code generating phase (the effective Valuerange is already
+ %% calculated. If we want to keep the Singlevalues as well for
+ %% use in code gen phases we need to introduce a new representation
+ %% like {'ValueRange',{Lb,Ub},[ListOfRanges|AntiValues|Singlevalues]
+ %% These could be used to generate guards which allows only the specific
+ %% values , not the full range
+ [{'ValueRange',{Lb,Ub}}].
-remove_val_from_list(Val,List) ->
- case lists:member(Val,List) of
- true ->
- {lists:delete(Val,List),Val};
- false ->
- false
- end.
%% get_constraints/2
%% Arguments are a list of constraints, which has the format {key,value},
diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py
index b18c29bd89..de48c4c2ca 100644
--- a/lib/asn1/test/asn1_SUITE_data/Constraints.py
+++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py
@@ -51,6 +51,7 @@ Thing ::= INTEGER {fred (0),fred2 (1),fred3 (2)}
AnotherThing ::= Thing (fred | fred2)
I ::= INTEGER (0|15..269) -- OTP-5457
+X1 ::= INTEGER (1..4 | 8 | 10 | 20) -- OTP-9946
-- OTP-5511
diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl
index cb15a24359..1ce68ec522 100644
--- a/lib/asn1/test/testConstraints.erl
+++ b/lib/asn1/test/testConstraints.erl
@@ -127,17 +127,26 @@ int_constraints(Rules) ->
?line {ok,0} = asn1_wrapper:decode('Constraints','I',Bytes12),
?line {ok,Bytes13} = asn1_wrapper:encode('Constraints','I',20),
?line {ok,20} = asn1_wrapper:decode('Constraints','I',Bytes13),
-
+
+ %%==========================================================
+ %% Constraint Combinations (Duboisson p. 285)
+ %% X1 ::= INTEGER (1..4|8|10|20)
+ %%==========================================================
+
+ ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','X1',1),
+ ?line {ok,1} = asn1_wrapper:decode('Constraints','X1',Bytes14),
+ ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','X1',20),
+ ?line {ok,20} = asn1_wrapper:decode('Constraints','X1',Bytes15),
%%==========================================================
%% SIZE Constraint (Duboisson p. 268)
%% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3)))
%% T2 ::= IA5String (SIZE (1|2, ..., 3))
%%==========================================================
- ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','T',"IA"),
- ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes14),
- ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','T2',"IA"),
- ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes15).
+ ?line {ok,Bytes16} = asn1_wrapper:encode('Constraints','T',"IA"),
+ ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes16),
+ ?line {ok,Bytes17} = asn1_wrapper:encode('Constraints','T2',"IA"),
+ ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes17).
refed_NNL_name(_Erule) ->
diff --git a/lib/common_test/doc/src/basics_chapter.xml b/lib/common_test/doc/src/basics_chapter.xml
index 20141d2561..d43ac44ac5 100644
--- a/lib/common_test/doc/src/basics_chapter.xml
+++ b/lib/common_test/doc/src/basics_chapter.xml
@@ -141,8 +141,9 @@
individual test case.
</p>
<p>
- The test suite module must conform to a callback interface specified
- by the CT test server. See the
+ The test suite module must conform to a
+ <seealso marker="common_test">callback interface</seealso>
+ specified by the CT test server. See the
<seealso marker="write_test_chapter#intro">Writing Test Suites</seealso> chapter
for more information.
</p>
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
index c5b4fd0073..cb06e570bd 100644
--- a/lib/common_test/doc/src/ct_hooks_chapter.xml
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -189,6 +189,22 @@
it.</p>
</section>
+ <section>
+ <title>External configuration data and Logging</title>
+ <p>It's possible in the CTH to read configuration data values
+ by calling <c>ct:get_config/1/2/3</c> (as explained in the
+ <seealso marker="config_file_chapter#require_config_data">
+ External configuration data</seealso>
+ chapter). The config variables in question must, as always, first have been
+ <c>required</c> by means of a suite-, group-, or test case info function,
+ or the <c>ct:require/1/2</c> function. Note that the latter can also be used
+ in CT hook functions.</p>
+ <p>The CT hook functions may call any of the logging functions available
+ in the <c>ct</c> interface to print information to the log files, or to
+ add comments in the suite overview page.
+ </p>
+ </section>
+
</section>
<marker id="manipulating"/>
@@ -201,11 +217,13 @@
functions for a CTH follow a common interface, this interface is
described below.</p>
- <p>It is only possible to hook into test function which exists in the test
- suite. So in order for a CTH to hook in before
- <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>,
- the <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>
- function must exist in the test suite.</p>
+ <p>Common Test will always call all available hook functions, even pre- and post
+ hooks for configuration functions that are not implemented in the suite.
+ For example, <c>pre_init_per_suite(x_SUITE, ...)</c> and
+ <c>post_init_per_suite(x_SUITE, ...)</c> will be called for test suite
+ <c>x_SUITE</c>, even if it doesn't export <c>init_per_suite/1</c>. This feature
+ makes it possible to use hooks as configuration fallbacks, or even
+ completely replace all configuration functions with hook functions.</p>
<marker id="pre"/>
<section>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index b01ab3667d..4d4521957c 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -103,6 +103,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
@@ -130,6 +131,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-repeat N [-force_stop]] |
[-duration HHMMSS [-force_stop]] |
[-until [YYMoMoDD]HHMMSS [-force_stop]]
@@ -150,6 +152,7 @@
[-no_auto_compile]
[-muliply_timetraps Multiplier]
[-scale_timetraps]
+ [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
[-basic_html]</pre>
</section>
<section>
diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml
index 7de0912036..7919edd183 100644
--- a/lib/common_test/doc/src/getting_started_chapter.xml
+++ b/lib/common_test/doc/src/getting_started_chapter.xml
@@ -90,7 +90,7 @@
<p>As you can understand from the illustration above, Common Test requires
that a test case generates a runtime error to indicate failure (e.g.
by causing a bad match error or by calling <c>exit/1</c>, preferrably
- through the <c>ct:fail/1/2</c> help function). A succesful execution is
+ through the <c>ct:fail/1,2</c> help function). A succesful execution is
indicated by means of a normal return from the test case function.
</p>
</section>
@@ -98,7 +98,8 @@
<section>
<title>A simple test suite</title>
<p>As you've seen in the basics chapter, the test suite module implements
- callback functions (mandatory or optional) for various purposes, e.g:
+ <seealso marker="common_test">callback functions</seealso>
+ (mandatory or optional) for various purposes, e.g:
<list>
<item>Init/end configuration function for the test suite</item>
<item>Init/end configuration function for a test case</item>
@@ -197,7 +198,7 @@
<pre>
1> ct:run_test([{suite, "check_log_SUITE"}]).</pre>
<p>
- The result from running our test is printed in log files on HTML format
+ The result from running our test is printed in log files in HTML format
(stored in unique log directories on different level). This illustration
shows the log file structure:
</p>
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 848f278fa6..b3ec524b21 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -466,6 +466,9 @@
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
+
+ {create_priv_dir, PrivDirOption}.
+ {create_priv_dir, NodeRefs, PrivDirOption}.
{event_handler, EventHandlers}.
{event_handler, NodeRefs, EventHandlers}.
@@ -493,6 +496,9 @@
{skip_suites, DirRef, Suites, Comment}.
{skip_suites, NodeRefs, DirRef, Suites, Comment}.
+
+ {skip_groups, DirRef, Suite, GroupNames, Comment}.
+ {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}.
{skip_cases, DirRef, Suite, Cases, Comment}.
{skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}.
@@ -512,6 +518,7 @@
DirAlias = atom()
Dir = string()
LogDir = string()
+ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc
EventHandlers = atom() | [atom()]
InitArgs = [term()]
CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]
@@ -521,8 +528,9 @@
Suites = atom() | [atom()] | all
Suite = atom()
Groups = GroupSpec | [GroupSpec] | all
- GroupSpec = Group | {Group,Properties} | {Group,Properties,GroupSpec}
- Group = atom()
+ GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec}
+ GroupName = atom()
+ GroupNames = GroupName | [GroupName]
Cases = atom() | [atom()] | all
Comment = string() | ""
</pre>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index c0ec26ddcc..09f4f8ed3b 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -29,7 +29,6 @@
<file>write_test_chapter.xml</file>
</header>
-
<section>
<marker id="intro"></marker>
<title>Support for test suite authors</title>
@@ -63,7 +62,8 @@
function in CT will not be able to locate it (at least not per default).
</p>
- <p>The <c>ct.hrl</c> header file must be included in all test suite files.
+ <p>It is also recommended that the <c>ct.hrl</c> header file is included
+ in all test suite modules.
</p>
<p>Each test suite module must export the function <c>all/0</c>
@@ -71,6 +71,12 @@
to be executed in that module.
</p>
+ <p>The callback functions that the test suite should implement, and
+ which will be described in more detail below, are
+ all listed in the <seealso marker="common_test">common_test
+ reference manual page</seealso>.
+ </p>
+
</section>
<section>
@@ -113,6 +119,14 @@
suite will be skipped automatically (so called <em>auto skipped</em>),
including <c>end_per_suite</c>.
</p>
+
+ <p>Note that if <c>init_per_suite</c> and <c>end_per_suite</c> do not exist
+ in the suite, Common Test calls dummy functions (with the same names)
+ instead, so that output generated by hook functions may be saved to the log
+ files for these dummies
+ (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test Hooks</seealso>
+ chapter for more information).
+ </p>
</section>
<marker id="per_testcase"/>
@@ -513,8 +527,7 @@
execution is immediately stopped and the rest of the cases skipped.</p>
<p>Before execution of a group begins, the configuration function
- <c>init_per_group(GroupName, Config)</c> is called (the function is
- mandatory if one or more test case groups are defined). The list of tuples
+ <c>init_per_group(GroupName, Config)</c> is called. The list of tuples
returned from this function is passed to the test cases in the usual
manner by means of the <c>Config</c> argument. <c>init_per_group/2</c>
is meant to be used for initializations common for the test cases in the
@@ -522,6 +535,14 @@
<c>end_per_group(GroupName, Config</c> function is called. This function
is meant to be used for cleaning up after <c>init_per_group/2</c>.</p>
+ <p>Whenever a group is executed, if <c>init_per_group</c> and
+ <c>end_per_group</c> do not exist in the suite, Common Test calls
+ dummy functions (with the same names) instead. Output generated by
+ hook functions will be saved to the log files for these dummies
+ (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test
+ Hooks</seealso> chapter for more information).
+ </p>
+
<note><p><c>init_per_testcase/2</c> and <c>end_per_testcase/2</c>
are always called for each individual test case, no matter if the case
belongs to a group or not.</p></note>
@@ -611,6 +632,25 @@
</section>
<section>
+ <title>Parallel test cases and IO</title>
+ <p>A parallel test case has a private IO server as its group leader.
+ (Please see the Erlang Run-Time System Application documentation for
+ a description of the group leader concept). The
+ central IO server process that handles the output from regular test
+ cases and configuration functions, does not respond to IO messages
+ during execution of parallel groups. This is important to understand
+ in order to avoid certain traps, like this one:</p>
+ <p>If a process, <c>P</c>, is spawned during execution of e.g.
+ <c>init_per_suite/1</c>, it will inherit the group leader of the
+ <c>init_per_suite</c> process. This group leader is the central IO server
+ process mentioned above. If, at a later time, <em>during parallel test case
+ execution</em>, some event triggers process <c>P</c> to call
+ <c>io:format/1/2</c>, that call will never return (since the group leader
+ is in a non-responsive state) and cause <c>P</c> to hang.
+ </p>
+ </section>
+
+ <section>
<title>Repeated groups</title>
<marker id="repeated_groups"></marker>
<p>A test case group may be repeated a certain number of times
@@ -745,7 +785,7 @@
<marker id="data_priv_dir"></marker>
<title>Data and Private Directories</title>
- <p>The data directory (<c>data_dir</c>) is the directory where the
+ <p>The data directory, <c>data_dir</c>, is the directory where the
test module has its own files needed for the testing. The name
of the <c>data_dir</c> is the the name of the test suite followed
by <c>"_data"</c>. For example,
@@ -769,12 +809,39 @@
</p>
-->
<p>
- The <c>priv_dir</c> is the test suite's private directory. This
- directory should be used when a test case needs to write to
- files. The name of the private directory is generated by the test
- server, which also creates the directory.
+ <c>priv_dir</c> is the private directory for the test cases.
+ This directory may be used whenever a test case (or configuration function)
+ needs to write something to file. The name of the private directory is
+ generated by Common Test, which also creates the directory.
</p>
-
+ <p>By default, Common Test creates one central private directory
+ per test run that all test cases share. This may not always be suitable,
+ especially if the same test cases are executed multiple times during
+ a test run (e.g. if they belong to a test case group with repeat
+ property), and there's a risk that files in the private directory get
+ overwritten. Under these circumstances, it's possible to configure
+ Common Test to create one dedicated private directory per
+ test case and execution instead. This is accomplished by means of
+ the flag/option: <c>create_priv_dir</c> (to be used with the
+ <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or
+ as test specification term). There are three possible values
+ for this option:
+ <list>
+ <item><c>auto_per_run</c></item>
+ <item><c>auto_per_tc</c></item>
+ <item><c>manual_per_tc</c></item>
+ </list>
+ The first value indicates the default priv_dir behaviour, i.e.
+ one private directory created per test run. The two latter
+ values tell Common Test to generate a unique test directory name
+ per test case and execution. If the auto version is used, <em>all</em>
+ private directories will be created automatically. This can obviously
+ become very inefficient for test runs with many test cases and/or
+ repetitions. Therefore, in case the manual version is instead used, the
+ test case must tell Common Test to create priv_dir when it needs it.
+ It does this by calling the function <c>ct:make_priv_dir/0</c>.
+ </p>
+
<note><p>You should not depend on current working directory for
reading and writing data files since this is not portable. All
scratch files are to be written in the <c>priv_dir</c> and all
diff --git a/lib/eldap/.gitignore b/lib/eldap/.gitignore
new file mode 100644
index 0000000000..5585418186
--- /dev/null
+++ b/lib/eldap/.gitignore
@@ -0,0 +1,4 @@
+*.beam
+*.asn1db
+src/ELDAPv3.hrl
+src/ELDAPv3.erl
diff --git a/lib/eldap/AUTHORS b/lib/eldap/AUTHORS
new file mode 100644
index 0000000000..8f1e399306
--- /dev/null
+++ b/lib/eldap/AUTHORS
@@ -0,0 +1,7 @@
+Original author:
+ Torbjorn Tornkvist
+
+With patches from:
+ Simon MacMullen
+ Manuel Durán Aguete
+ voluntas \ No newline at end of file
diff --git a/lib/eldap/LICENSE b/lib/eldap/LICENSE
new file mode 100644
index 0000000000..1f6200918f
--- /dev/null
+++ b/lib/eldap/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) 2010, Torbjorn Tornkvist
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/lib/eldap/Makefile b/lib/eldap/Makefile
new file mode 100644
index 0000000000..3635ec759d
--- /dev/null
+++ b/lib/eldap/Makefile
@@ -0,0 +1,39 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Macros
+# ----------------------------------------------------
+
+SUB_DIRECTORIES = src doc/src
+
+include vsn.mk
+VSN = $(ELDAP_VSN)
+
+SPECIAL_TARGETS =
+
+# ----------------------------------------------------
+# Default Subdir Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_subdir.mk
+
diff --git a/lib/eldap/README b/lib/eldap/README
new file mode 100644
index 0000000000..e1bde9d658
--- /dev/null
+++ b/lib/eldap/README
@@ -0,0 +1,33 @@
+Hi,
+
+This is 'eldap', the Erlang LDAP library.
+
+It exports an API that can do all possible operations
+you may want to do against an LDAP server. The code has
+been tested to work at some point, but only the bind
+and search operations are running daily in our products,
+so there may be bugs lurking in some parts of the code.
+
+To just use eldap for doing authentication, do like in:
+
+ {ok,X} = eldap:open(["ldap.mycorp.com"], []).
+ eldap:simple_bind(X, "uid=tobbe,ou=People,dc=mycorp,dc=com", "passwd").
+
+In the doc/README.example you'll find a trace from a
+Erlang shell session as an example on how to setup a
+connection, authenticate (bind) and perform a search.
+Note that by using the option {ssl, true}, you should
+be able to setup an SSL tunnel (LDAPS) if your Erlang
+system has been configured with SSL.
+
+In the test directory there are some hints and examples
+on how to test the code and how to setup and populate
+an OpenLDAP server. The 'eldap' code has been tested
+agains OpenLDAP, IPlanet and ActiveDirectory servers.
+
+If you plan to incorporate this code into your system
+I suggest that you build a server/supervisor harnesk
+that uses 'eldap' (as we have done in our products).
+
+Good luck !
+/Tobbe
diff --git a/lib/eldap/asn1/ELDAPv3.asn1 b/lib/eldap/asn1/ELDAPv3.asn1
new file mode 100644
index 0000000000..72b87d7221
--- /dev/null
+++ b/lib/eldap/asn1/ELDAPv3.asn1
@@ -0,0 +1,278 @@
+-- Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
+-- Copyright (C) The Internet Society (2006). This version of
+-- this ASN.1 module is part of RFC 4511; see the RFC itself
+-- for full legal notices.
+ELDAPv3 DEFINITIONS
+IMPLICIT TAGS
+EXTENSIBILITY IMPLIED ::=
+
+BEGIN
+
+LDAPMessage ::= SEQUENCE {
+ messageID MessageID,
+ protocolOp CHOICE {
+ bindRequest BindRequest,
+ bindResponse BindResponse,
+ unbindRequest UnbindRequest,
+ searchRequest SearchRequest,
+ searchResEntry SearchResultEntry,
+ searchResDone SearchResultDone,
+ searchResRef SearchResultReference,
+ modifyRequest ModifyRequest,
+ modifyResponse ModifyResponse,
+ addRequest AddRequest,
+ addResponse AddResponse,
+ delRequest DelRequest,
+ delResponse DelResponse,
+ modDNRequest ModifyDNRequest,
+ modDNResponse ModifyDNResponse,
+ compareRequest CompareRequest,
+ compareResponse CompareResponse,
+ abandonRequest AbandonRequest,
+ extendedReq ExtendedRequest,
+ extendedResp ExtendedResponse,
+ ...,
+ intermediateResponse IntermediateResponse },
+ controls [0] Controls OPTIONAL }
+
+MessageID ::= INTEGER (0 .. maxInt)
+
+maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
+
+LDAPString ::= OCTET STRING -- UTF-8 encoded,
+ -- [ISO10646] characters
+
+LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
+ -- [RFC4512]
+
+LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
+ -- [RFC4514]
+
+RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
+ -- [RFC4514]
+
+AttributeDescription ::= LDAPString
+ -- Constrained to <attributedescription>
+ -- [RFC4512]
+
+AttributeValue ::= OCTET STRING
+
+AttributeValueAssertion ::= SEQUENCE {
+ attributeDesc AttributeDescription,
+ assertionValue AssertionValue }
+
+AssertionValue ::= OCTET STRING
+
+PartialAttribute ::= SEQUENCE {
+ type AttributeDescription,
+ vals SET OF value AttributeValue }
+
+Attribute ::= PartialAttribute(WITH COMPONENTS {
+ ...,
+ vals (SIZE(1..MAX))})
+
+MatchingRuleId ::= LDAPString
+
+LDAPResult ::= SEQUENCE {
+ resultCode ENUMERATED {
+ success (0),
+ operationsError (1),
+ protocolError (2),
+ timeLimitExceeded (3),
+ sizeLimitExceeded (4),
+ compareFalse (5),
+ compareTrue (6),
+ authMethodNotSupported (7),
+ strongerAuthRequired (8),
+ -- 9 reserved --
+ referral (10),
+ adminLimitExceeded (11),
+ unavailableCriticalExtension (12),
+ confidentialityRequired (13),
+ saslBindInProgress (14),
+
+ noSuchAttribute (16),
+ undefinedAttributeType (17),
+ inappropriateMatching (18),
+ constraintViolation (19),
+ attributeOrValueExists (20),
+ invalidAttributeSyntax (21),
+ -- 22-31 unused --
+ noSuchObject (32),
+ aliasProblem (33),
+ invalidDNSyntax (34),
+ -- 35 reserved for undefined isLeaf --
+ aliasDereferencingProblem (36),
+ -- 37-47 unused --
+ inappropriateAuthentication (48),
+ invalidCredentials (49),
+ insufficientAccessRights (50),
+ busy (51),
+ unavailable (52),
+ unwillingToPerform (53),
+ loopDetect (54),
+ -- 55-63 unused --
+ namingViolation (64),
+ objectClassViolation (65),
+ notAllowedOnNonLeaf (66),
+ notAllowedOnRDN (67),
+ entryAlreadyExists (68),
+ objectClassModsProhibited (69),
+ -- 70 reserved for CLDAP --
+ affectsMultipleDSAs (71),
+ -- 72-79 unused --
+ other (80),
+ ... },
+ matchedDN LDAPDN,
+ diagnosticMessage LDAPString,
+ referral [3] Referral OPTIONAL }
+
+Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
+
+URI ::= LDAPString -- limited to characters permitted in
+ -- URIs
+
+Controls ::= SEQUENCE OF control Control
+
+Control ::= SEQUENCE {
+ controlType LDAPOID,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue OCTET STRING OPTIONAL }
+
+BindRequest ::= [APPLICATION 0] SEQUENCE {
+ version INTEGER (1 .. 127),
+ name LDAPDN,
+ authentication AuthenticationChoice }
+
+AuthenticationChoice ::= CHOICE {
+ simple [0] OCTET STRING,
+ -- 1 and 2 reserved
+ sasl [3] SaslCredentials,
+ ... }
+
+SaslCredentials ::= SEQUENCE {
+ mechanism LDAPString,
+ credentials OCTET STRING OPTIONAL }
+
+BindResponse ::= [APPLICATION 1] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ serverSaslCreds [7] OCTET STRING OPTIONAL }
+
+UnbindRequest ::= [APPLICATION 2] NULL
+
+SearchRequest ::= [APPLICATION 3] SEQUENCE {
+ baseObject LDAPDN,
+ scope ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2),
+ ... },
+ derefAliases ENUMERATED {
+ neverDerefAliases (0),
+ derefInSearching (1),
+ derefFindingBaseObj (2),
+ derefAlways (3) },
+ sizeLimit INTEGER (0 .. maxInt),
+ timeLimit INTEGER (0 .. maxInt),
+ typesOnly BOOLEAN,
+ filter Filter,
+ attributes AttributeSelection }
+
+AttributeSelection ::= SEQUENCE OF selector LDAPString
+ -- The LDAPString is constrained to
+ -- <attributeSelector> in Section 4.5.1.8
+
+Filter ::= CHOICE {
+ and [0] SET SIZE (1..MAX) OF filter Filter,
+ or [1] SET SIZE (1..MAX) OF filter Filter,
+ not [2] Filter,
+ equalityMatch [3] AttributeValueAssertion,
+ substrings [4] SubstringFilter,
+ greaterOrEqual [5] AttributeValueAssertion,
+ lessOrEqual [6] AttributeValueAssertion,
+ present [7] AttributeDescription,
+ approxMatch [8] AttributeValueAssertion,
+ extensibleMatch [9] MatchingRuleAssertion,
+ ... }
+
+SubstringFilter ::= SEQUENCE {
+ type AttributeDescription,
+ substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ initial [0] AssertionValue, -- can occur at most once
+ any [1] AssertionValue,
+ final [2] AssertionValue } -- can occur at most once
+ }
+
+MatchingRuleAssertion ::= SEQUENCE {
+ matchingRule [1] MatchingRuleId OPTIONAL,
+ type [2] AttributeDescription OPTIONAL,
+ matchValue [3] AssertionValue,
+ dnAttributes [4] BOOLEAN DEFAULT FALSE }
+
+SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
+ objectName LDAPDN,
+ attributes PartialAttributeList }
+
+PartialAttributeList ::= SEQUENCE OF
+ partialAttribute PartialAttribute
+
+SearchResultReference ::= [APPLICATION 19] SEQUENCE
+ SIZE (1..MAX) OF uri URI
+
+SearchResultDone ::= [APPLICATION 5] LDAPResult
+
+ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ object LDAPDN,
+ changes SEQUENCE OF change SEQUENCE {
+ operation ENUMERATED {
+ add (0),
+ delete (1),
+ replace (2),
+ ... },
+ modification PartialAttribute } }
+
+ModifyResponse ::= [APPLICATION 7] LDAPResult
+
+AddRequest ::= [APPLICATION 8] SEQUENCE {
+ entry LDAPDN,
+ attributes AttributeList }
+
+AttributeList ::= SEQUENCE OF attribute Attribute
+
+AddResponse ::= [APPLICATION 9] LDAPResult
+
+DelRequest ::= [APPLICATION 10] LDAPDN
+
+DelResponse ::= [APPLICATION 11] LDAPResult
+
+ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
+ entry LDAPDN,
+ newrdn RelativeLDAPDN,
+ deleteoldrdn BOOLEAN,
+ newSuperior [0] LDAPDN OPTIONAL }
+
+ModifyDNResponse ::= [APPLICATION 13] LDAPResult
+
+CompareRequest ::= [APPLICATION 14] SEQUENCE {
+ entry LDAPDN,
+ ava AttributeValueAssertion }
+
+CompareResponse ::= [APPLICATION 15] LDAPResult
+
+AbandonRequest ::= [APPLICATION 16] MessageID
+
+ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ requestName [0] LDAPOID,
+ requestValue [1] OCTET STRING OPTIONAL }
+
+ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ responseName [10] LDAPOID OPTIONAL,
+ responseValue [11] OCTET STRING OPTIONAL }
+
+IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
+ responseName [0] LDAPOID OPTIONAL,
+ responseValue [1] OCTET STRING OPTIONAL }
+
+END
+
diff --git a/lib/eldap/doc/html/.gitignore b/lib/eldap/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eldap/doc/html/.gitignore
diff --git a/lib/eldap/doc/man3/.gitignore b/lib/eldap/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eldap/doc/man3/.gitignore
diff --git a/lib/eldap/doc/pdf/.gitignore b/lib/eldap/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eldap/doc/pdf/.gitignore
diff --git a/lib/eldap/doc/src/Makefile b/lib/eldap/doc/src/Makefile
new file mode 100644
index 0000000000..4c827319b4
--- /dev/null
+++ b/lib/eldap/doc/src/Makefile
@@ -0,0 +1,115 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(ELDAP_VSN)
+APPLICATION=eldap
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES = eldap.xml
+
+XML_PART_FILES = release_notes.xml usersguide.xml
+XML_CHAPTER_FILES = notes.xml
+
+BOOK_FILES = book.xml
+
+XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \
+ $(XML_PART_FILES) $(XML_CHAPTER_FILES)
+
+GIF_FILES =
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+man: $(MAN3_FILES) $(MAN6_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt valgrind:
+
+clean clean_docs clean_tex:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(MAN6DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+# $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
+# $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6
+
+
+release_spec:
+
diff --git a/lib/eldap/doc/src/book.xml b/lib/eldap/doc/src/book.xml
new file mode 100644
index 0000000000..74f1c37cdc
--- /dev/null
+++ b/lib/eldap/doc/src/book.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>eldap</title>
+ <prepared>OTP team</prepared>
+ <docno></docno>
+ <date>1999-01-21</date>
+ <rev>A</rev>
+ <file>book.xml</file>
+ </header>
+ <pagetext>eldap</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="usersguide.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
new file mode 100644
index 0000000000..04dad2eee7
--- /dev/null
+++ b/lib/eldap/doc/src/eldap.xml
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>eldap</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2000-06-20</date>
+ <rev>B</rev>
+ </header>
+ <module>eldap</module>
+ <modulesummary>Eldap Functions</modulesummary>
+ <description>
+ <p>This module provides a client api to the Lightweight Directory Access Protocol (LDAP).
+ </p>
+ <p>References:</p>
+ <list type="bulleted">
+ <item> <p>RFC 4510 - RFC 4519</p> </item>
+ </list>
+ <p>The above publications can be found at <url href="http://www.ietf.org">IETF</url>.
+ </p>
+ <p><em>Types</em></p>
+ <pre>
+handle() Connection handle
+attribute() {Type = string(), Values=[string()]}
+modify_op() See mod_add/2, mod_delete/2, mod_replace/2
+scope() See baseObject/0, singleLevel/0, wholeSubtree/0
+dereference() See neverDerefAliases/0, derefInSearching/0, derefFindingBaseObj/0, derefAlways/0
+filter() See present/1, substrings/2,
+ equalityMatch/2, greaterOrEqual/2, lessOrEqual/2,
+ approxMatch/2,
+ 'and'/1, 'or'/1, 'not'/1.
+ </pre>
+ <p></p>
+ </description>
+ <funcs>
+ <func>
+ <name>open([Host]) -> {ok, Handle} | {error, Reason}</name>
+ <fsummary>Open a connection to an LDAP server.</fsummary>
+ <type>
+ <v>Handle = handle()</v>
+ </type>
+ <desc>
+ <p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order. </p>
+ </desc>
+ </func>
+ <func>
+ <name>open([Host], [Option]) -> {ok, Handle} | {error, Reason}</name>
+ <fsummary>Open a connection to an LDAP server.</fsummary>
+ <type>
+ <v>Handle = handle()</v>
+ <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()}</v>
+ </type>
+ <desc>
+ <p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p>
+ <p>The log function takes three arguments, <c>fun(Level, FormatString, [FormatArg]) end</c>.</p>
+ <p>Timeout set the maximum time in milliseconds that each server request may take.</p>
+ </desc>
+ </func>
+ <func>
+ <name>close(Handle) -> ok</name>
+ <fsummary>Shutdown the connection.</fsummary>
+ <type>
+ <v>Handle = handle()</v>
+ </type>
+ <desc>
+ <p>Shutdown the connection.</p>
+ </desc>
+ </func>
+ <func>
+ <name>simple_bind(Handle, Dn, Password) -> ok | {error, Reason}</name>
+ <fsummary>Authenticate the connection.</fsummary>
+ <type>
+ <v>Handle = handle()</v>
+ <v>Dn = string()</v>
+ <v>Password = string()</v>
+ </type>
+ <desc>
+ <p>Authenticate the connection using simple authentication.</p>
+ </desc>
+ </func>
+ <func>
+ <name>add(Handle, Dn, [Attribute]) -> ok | {error, Reason}</name>
+ <fsummary>Add an entry.</fsummary>
+ <type>
+ <v>Handle = handle()</v>
+ <v>Dn = string()</v>
+ <v>Attribute = attribute()</v>
+ </type>
+ <desc>
+ <p> Add an entry. The entry must not exist.</p>
+ <pre>
+ add(Handle,
+ "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com",
+ [{"objectclass", ["person"]},
+ {"cn", ["Bill Valentine"]},
+ {"sn", ["Valentine"]},
+ {"telephoneNumber", ["545 555 00"]}]
+ )
+ </pre>
+ </desc>
+ </func>
+ <func>
+ <name>delete(Handle, Dn) -> ok | {error, Reason}</name>
+ <fsummary>Delete an entry.</fsummary>
+ <type>
+ <v>Dn = string()</v>
+ </type>
+ <desc>
+ <p> Delete an entry.</p>
+ <pre>
+ delete(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com")
+ </pre>
+ </desc>
+ </func>
+
+ <func>
+ <name>mod_add(Type, [Value]) -> modify_op()</name>
+ <fsummary>Create a modification operation.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p> Create an add modification operation.</p> </desc>
+ </func>
+ <func>
+ <name>mod_delete(Type, [Value]) -> modify_op()</name>
+ <fsummary>Create a modification operation.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p> Create a delete modification operation.</p> </desc>
+ </func>
+ <func>
+ <name>mod_replace(Type, [Value]) -> modify_op()</name>
+ <fsummary>Create a modification operation.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p> Create a replace modification operation.</p> </desc>
+ </func>
+
+ <func>
+ <name>modify(Handle, Dn, [ModifyOp]) -> ok | {error, Reason}</name>
+ <fsummary>Modify an entry.</fsummary>
+ <type>
+ <v>Dn = string()</v>
+ <v>ModifyOp = modify_op()</v>
+ </type>
+ <desc>
+ <p> Modify an entry.</p>
+ <pre>
+ modify(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com",
+ [eldap:mod_replace("telephoneNumber", ["555 555 00"]),
+ eldap:mod_add("description", ["LDAP Hacker"]) ])
+ </pre>
+ </desc>
+ </func>
+ <func>
+ <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason}</name>
+ <fsummary>Modify the DN of an entry.</fsummary>
+ <type>
+ <v>Dn = string()</v>
+ <v>NewRDN = string()</v>
+ <v>DeleteOldRDN = boolean()</v>
+ <v>NewSupDN = string()</v>
+ </type>
+ <desc>
+ <p> Modify the DN of an entry. <c>DeleteOldRDN</c> indicates
+ whether the current RDN should be removed after operation.
+ <c>NewSupDN</c> should be "" if the RDN should not be moved or the new parent which
+ the RDN will be moved to.</p>
+ <pre>
+ modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ",
+ "cn=Bill Jr Valentine", true, "")
+ </pre>
+ </desc>
+ </func>
+ <func>
+ <name>search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {error, Reason}</name>
+ <fsummary>Search the Directory</fsummary>
+ <type>
+ <v>SearchOptions = #eldap_search{} | [SearchOption]</v>
+ <v>SearchOption = {base, string()} | {filter, filter()} | {scope, scope()}
+ | {attributes, [string()]} | {deref, dereference()} |
+ | {types_only, boolean()} | {timeout, integer()}
+ </v>
+ </type>
+ <desc>
+ <p>Search the directory with the supplied the SearchOptions.
+ The base and filter options must be supplied.
+ Default values: scope is <c>wholeSubtree()</c>, deref is <c>derefAlways()</c>,
+ types_only is <c>false</c> and timeout is <c>0</c> (meaning infinity).
+ </p>
+ <pre>
+ Filter = eldap:substrings("cn", [{any,"V"}]),
+ search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]),
+ </pre>
+ </desc>
+ </func>
+
+ <func>
+ <name>baseObject() -> scope()</name>
+ <fsummary>Create search scope.</fsummary>
+ <desc> <p> Search baseobject only.</p> </desc>
+ </func>
+ <func>
+ <name>singleLevel() -> scope()</name>
+ <fsummary>Create search scope.</fsummary>
+ <desc> <p> Search the specified level only, i.e. do not recurse.</p> </desc>
+ </func>
+ <func>
+ <name>wholeSubtree() -> scope()</name>
+ <fsummary>Create search scope.</fsummary>
+ <desc> <p> Search the entire subtree.</p> </desc>
+ </func>
+
+ <func>
+ <name>neverDerefAliases() -> dereference()</name>
+ <fsummary>Create search option.</fsummary>
+ <desc> <p>Never derefrence aliases, treat aliases as entries.</p> </desc>
+ </func>
+ <func>
+ <name>derefAlways() -> dereference()</name>
+ <fsummary>Create search option.</fsummary>
+ <desc> <p>Always derefrence aliases.</p> </desc>
+ </func>
+ <func>
+ <name>derefInSearching() -> dereference()</name>
+ <fsummary>Create search option.</fsummary>
+ <desc> <p>Derefrence aliases only when searching.</p> </desc>
+ </func>
+ <func>
+ <name>derefFindingBaseObj() -> dereference()</name>
+ <fsummary>Create search option.</fsummary>
+ <desc> <p>Derefrence aliases only in finding the base.</p> </desc>
+ </func>
+
+ <func>
+ <name>present(Type) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ </type>
+ <desc> <p>Create a filter which filters on attribute type presence.</p> </desc>
+ </func>
+ <func>
+ <name>substrings(Type, [SubString]) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>SubString = {StringPart, string()}</v>
+ <v>StringPart = initial | any | final</v>
+ </type>
+ <desc> <p>Create a filter which filters on substrings.</p> </desc>
+ </func>
+ <func>
+ <name>equalityMatch(Type, Value) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p>Create a equality filter.</p> </desc>
+ </func>
+ <func>
+ <name>greaterOrEqual(Type, Value) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p>Create a greater or equal filter.</p> </desc>
+ </func>
+ <func>
+ <name>lessOrEqual(Type, Value) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p>Create a less or equal filter.</p> </desc>
+ </func>
+ <func>
+ <name>approxMatch(Type, Value) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Type = string()</v>
+ <v>Value = string()</v>
+ </type>
+ <desc> <p>Create a approximation match filter.</p> </desc>
+ </func>
+ <func>
+ <name>'and'([Filter]) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Filter = filter()</v>
+ </type>
+ <desc> <p>Creates a filter where all <c>Filter</c> must be true.</p> </desc>
+ </func>
+ <func>
+ <name>'or'([Filter]) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Filter = filter()</v>
+ </type>
+ <desc> <p>Create a filter where at least one of the <c>Filter</c> must be true.</p> </desc>
+ </func>
+ <func>
+ <name>'not'(Filter) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>Filter = filter()</v>
+ </type>
+ <desc> <p>Negate a filter.</p> </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
+
diff --git a/lib/eldap/doc/src/fascicules.xml b/lib/eldap/doc/src/fascicules.xml
new file mode 100644
index 0000000000..8fc250bc75
--- /dev/null
+++ b/lib/eldap/doc/src/fascicules.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="release_notes" href="release_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/eldap/doc/src/note.gif b/lib/eldap/doc/src/note.gif
new file mode 100644
index 0000000000..6fffe30419
--- /dev/null
+++ b/lib/eldap/doc/src/note.gif
Binary files differ
diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml
new file mode 100644
index 0000000000..d9b11875de
--- /dev/null
+++ b/lib/eldap/doc/src/notes.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Eldap Release Notes</title>
+ <prepared>OTP team</prepared>
+ <docno></docno>
+ <date>2003-06-06</date>
+ <rev>B</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the Eldap application.</p>
+
+ <section>
+ <title>Eldap 1.0</title>
+ <p>New application. </p>
+ </section>
+</chapter>
+
diff --git a/lib/eldap/doc/src/ref_man.xml b/lib/eldap/doc/src/ref_man.xml
new file mode 100644
index 0000000000..70c4cee16d
--- /dev/null
+++ b/lib/eldap/doc/src/ref_man.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Eldap Reference Manual</title>
+ <prepared>OTP team</prepared>
+ <docno></docno>
+ <date>2003-06-01</date>
+ <rev>B</rev>
+ <file>refman.sgml</file>
+ </header>
+ <description>
+ <p>The <em>Eldap</em> application provides an api for accessing an LDAP server.</p>
+ <p>The original code was developed by Torbj&ouml;rn T&ouml;rnkvist.</p>
+ </description>
+ <xi:include href="eldap.xml"/>
+</application>
+
diff --git a/lib/eldap/doc/src/release_notes.xml b/lib/eldap/doc/src/release_notes.xml
new file mode 100644
index 0000000000..778a49b894
--- /dev/null
+++ b/lib/eldap/doc/src/release_notes.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part>
+ <header>
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>Eldap Release Notes</title>
+ <prepared>OTP team</prepared>
+ <docno></docno>
+ <date>2003-06-01</date>
+ <rev>B</rev>
+ <file>release_notes.xml</file>
+ </header>
+ <description>
+ <p></p>
+ </description>
+ <include file="notes"></include>
+</part>
+
diff --git a/lib/eldap/doc/src/usersguide.xml b/lib/eldap/doc/src/usersguide.xml
new file mode 100644
index 0000000000..828588e88e
--- /dev/null
+++ b/lib/eldap/doc/src/usersguide.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2012</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Eldap User's Guide</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>2003-06-01</date>
+ <rev>C</rev>
+ <file>usersguide.xml</file>
+ </header>
+ <description>
+ <p>The <em>Eldap</em> application provides an api for accessing an LDAP server.</p>
+ <p>The original code was developed by Torbj&ouml;rn T&ouml;rnkvist.</p>
+ </description>
+
+
+</part>
+
diff --git a/lib/eldap/doc/src/warning.gif b/lib/eldap/doc/src/warning.gif
new file mode 100644
index 0000000000..96af52360e
--- /dev/null
+++ b/lib/eldap/doc/src/warning.gif
Binary files differ
diff --git a/lib/eldap/ebin/.gitignore b/lib/eldap/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eldap/ebin/.gitignore
diff --git a/lib/eldap/include/eldap.hrl b/lib/eldap/include/eldap.hrl
new file mode 100644
index 0000000000..7c12cd4f2b
--- /dev/null
+++ b/lib/eldap/include/eldap.hrl
@@ -0,0 +1,33 @@
+-ifndef( _ELDAP_HRL ).
+-define( _ELDAP_HRL , 1 ).
+
+%%%
+%%% Search input parameters
+%%%
+-record(eldap_search, {
+ base = [], % Baseobject
+ filter = [], % Search conditions
+ scope=wholeSubtree, % Search scope
+ deref=derefAlways, % Dereference
+ attributes = [], % Attributes to be returned
+ types_only = false, % Return types+values or types
+ timeout = 0 % Timelimit for search
+ }).
+
+%%%
+%%% Returned search result
+%%%
+-record(eldap_search_result, {
+ entries = [], % List of #eldap_entry{} records
+ referrals = [] % List of referrals
+ }).
+
+%%%
+%%% LDAP entry
+%%%
+-record(eldap_entry, {
+ object_name = "", % The DN for the entry
+ attributes = [] % List of {Attribute, Value} pairs
+ }).
+
+-endif.
diff --git a/lib/eldap/info b/lib/eldap/info
new file mode 100644
index 0000000000..463f9af6ea
--- /dev/null
+++ b/lib/eldap/info
@@ -0,0 +1,2 @@
+group: comm
+short: eldap - Erlang LDAP library
diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile
new file mode 100644
index 0000000000..4ddb8082d7
--- /dev/null
+++ b/lib/eldap/src/Makefile
@@ -0,0 +1,110 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/eldap-$(ELDAP_VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= eldap
+
+ASN1_FILES = ELDAPv3.asn1
+ASN1_HRL = $(EBIN)/$(ASN1_FILES:%.asn1=%.hrl)
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ASN1_FILES:%.asn1=$(EBIN)/%.$(EMULATOR))
+
+EXTERNAL_HRL_FILES = ../include/eldap.hrl
+
+HRL_FILES = $(EXTERNAL_HRL_FILES) $(ASN1_HRL)
+
+APPUP_FILE = eldap.appup
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+APP_FILE = eldap.app
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -I../include -I../ebin
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+
+debug:
+ @${MAKE} TYPE=debug opt
+
+clean:
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(ASN1_FILES:%.asn1=$(EBIN)/%.*)
+ rm -f errs core *~
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@
+
+docs:
+
+$(TARGET_FILES): $(HRL_FILES)
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+$(ASN1_HRL): ../asn1/$(ASN1_FILES)
+ $(ERLC) -o $(EBIN) -bber_bin +optimize +nif $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/asn1
+ $(INSTALL_DATA) ../asn1/$(ASN1_FILES) $(RELSYSDIR)/asn1
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
+
diff --git a/lib/eldap/src/eldap.app.src b/lib/eldap/src/eldap.app.src
new file mode 100644
index 0000000000..8215328910
--- /dev/null
+++ b/lib/eldap/src/eldap.app.src
@@ -0,0 +1,8 @@
+{application, eldap,
+ [{description, "Ldap api"},
+ {vsn, "%VSN%"},
+ {modules, [eldap, 'ELDAPv3']},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {env, []}
+]}.
diff --git a/lib/eldap/src/eldap.appup.src b/lib/eldap/src/eldap.appup.src
new file mode 100644
index 0000000000..8d33482f11
--- /dev/null
+++ b/lib/eldap/src/eldap.appup.src
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+{"%VSN%",
+ [
+ ],
+ [
+ ]}.
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
new file mode 100644
index 0000000000..699dfc8791
--- /dev/null
+++ b/lib/eldap/src/eldap.erl
@@ -0,0 +1,1114 @@
+-module(eldap).
+%%% --------------------------------------------------------------------
+%%% Created: 12 Oct 2000 by Tobbe <[email protected]>
+%%% Function: Erlang client LDAP implementation according RFC 2251,2253
+%%% and 2255. The interface is based on RFC 1823, and
+%%% draft-ietf-asid-ldap-c-api-00.txt
+%%%
+%%% Copyright (c) 2010 Torbjorn Tornkvist
+%%% See MIT-LICENSE at the top dir for licensing information.
+%%% --------------------------------------------------------------------
+-vc('$Id$ ').
+-export([open/1,open/2,simple_bind/3,controlling_process/2,
+ baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
+ equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
+ approxMatch/2,search/2,substrings/2,present/1,
+ 'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
+ mod_replace/2, add/3, delete/2, modify_dn/5,parse_dn/1,
+ parse_ldap_url/1]).
+
+-export([neverDerefAliases/0, derefInSearching/0,
+ derefFindingBaseObj/0, derefAlways/0]).
+
+%% for upgrades
+-export([loop/2]).
+
+-import(lists,[concat/1]).
+
+-include("ELDAPv3.hrl").
+-include("eldap.hrl").
+
+-define(LDAP_VERSION, 3).
+-define(LDAP_PORT, 389).
+-define(LDAPS_PORT, 636).
+
+-record(eldap, {version = ?LDAP_VERSION,
+ host, % Host running LDAP server
+ port = ?LDAP_PORT, % The LDAP server port
+ fd, % Socket filedescriptor.
+ binddn = "", % Name of the entry to bind as
+ passwd, % Password for (above) entry
+ id = 0, % LDAP Request ID
+ log, % User provided log function
+ timeout = infinity, % Request timeout
+ anon_auth = false, % Allow anonymous authentication
+ use_tls = false % LDAP/LDAPS
+ }).
+
+%%% For debug purposes
+%%-define(PRINT(S, A), io:fwrite("~w(~w): " ++ S, [?MODULE,?LINE|A])).
+-define(PRINT(S, A), true).
+
+-define(elog(S, A), error_logger:info_msg("~w(~w): "++S,[?MODULE,?LINE|A])).
+
+%%% ====================================================================
+%%% Exported interface
+%%% ====================================================================
+
+%%% --------------------------------------------------------------------
+%%% open(Hosts [,Opts] )
+%%% --------------------
+%%% Setup a connection to on of the Hosts in the argument
+%%% list. Stop at the first successful connection attempt.
+%%% Valid Opts are: Where:
+%%%
+%%% {port, Port} - Port is the port number
+%%% {log, F} - F(LogLevel, FormatString, ListOfArgs)
+%%% {timeout, milliSec} - Server request timeout
+%%%
+%%% --------------------------------------------------------------------
+open(Hosts) ->
+ open(Hosts, []).
+
+open(Hosts, Opts) when is_list(Hosts), is_list(Opts) ->
+ Self = self(),
+ Pid = spawn_link(fun() -> init(Hosts, Opts, Self) end),
+ recv(Pid).
+
+%%% --------------------------------------------------------------------
+%%% Shutdown connection (and process) asynchronous.
+%%% --------------------------------------------------------------------
+
+close(Handle) when is_pid(Handle) ->
+ send(Handle, close).
+
+%%% --------------------------------------------------------------------
+%%% Set who we should link ourselves to
+%%% --------------------------------------------------------------------
+
+controlling_process(Handle, Pid) when is_pid(Handle), is_pid(Pid) ->
+ link(Pid),
+ send(Handle, {cnt_proc, Pid}),
+ recv(Handle).
+
+%%% --------------------------------------------------------------------
+%%% Authenticate ourselves to the Directory
+%%% using simple authentication.
+%%%
+%%% Dn - The name of the entry to bind as
+%%% Passwd - The password to be used
+%%%
+%%% Returns: ok | {error, Error}
+%%% --------------------------------------------------------------------
+simple_bind(Handle, Dn, Passwd) when is_pid(Handle) ->
+ send(Handle, {simple_bind, Dn, Passwd}),
+ recv(Handle).
+
+%%% --------------------------------------------------------------------
+%%% Add an entry. The entry field MUST NOT exist for the AddRequest
+%%% to succeed. The parent of the entry MUST exist.
+%%% Example:
+%%%
+%%% add(Handle,
+%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
+%%% [{"objectclass", ["person"]},
+%%% {"cn", ["Bill Valentine"]},
+%%% {"sn", ["Valentine"]},
+%%% {"telephoneNumber", ["545 555 00"]}]
+%%% )
+%%% --------------------------------------------------------------------
+add(Handle, Entry, Attributes) when is_pid(Handle),is_list(Entry),is_list(Attributes) ->
+ send(Handle, {add, Entry, add_attrs(Attributes)}),
+ recv(Handle).
+
+%%% Do sanity check !
+add_attrs(Attrs) ->
+ F = fun({Type,Vals}) when is_list(Type),is_list(Vals) ->
+ %% Confused ? Me too... :-/
+ {'AddRequest_attributes',Type, Vals}
+ end,
+ case catch lists:map(F, Attrs) of
+ {'EXIT', _} -> throw({error, attribute_values});
+ Else -> Else
+ end.
+
+%%% --------------------------------------------------------------------
+%%% Delete an entry. The entry consists of the DN of
+%%% the entry to be deleted.
+%%% Example:
+%%%
+%%% delete(Handle,
+%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com"
+%%% )
+%%% --------------------------------------------------------------------
+delete(Handle, Entry) when is_pid(Handle), is_list(Entry) ->
+ send(Handle, {delete, Entry}),
+ recv(Handle).
+
+%%% --------------------------------------------------------------------
+%%% Modify an entry. Given an entry a number of modification
+%%% operations can be performed as one atomic operation.
+%%% Example:
+%%%
+%%% modify(Handle,
+%%% "cn=Torbjorn Tornkvist, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
+%%% [mod_replace("telephoneNumber", ["555 555 00"]),
+%%% mod_add("description", ["LDAP hacker"])]
+%%% )
+%%% --------------------------------------------------------------------
+modify(Handle, Object, Mods) when is_pid(Handle), is_list(Object), is_list(Mods) ->
+ send(Handle, {modify, Object, Mods}),
+ recv(Handle).
+
+%%%
+%%% Modification operations.
+%%% Example:
+%%% mod_replace("telephoneNumber", ["555 555 00"])
+%%%
+mod_add(Type, Values) when is_list(Type), is_list(Values) -> m(add, Type, Values).
+mod_delete(Type, Values) when is_list(Type), is_list(Values) -> m(delete, Type, Values).
+mod_replace(Type, Values) when is_list(Type), is_list(Values) -> m(replace, Type, Values).
+
+m(Operation, Type, Values) ->
+ #'ModifyRequest_changes_SEQOF'{
+ operation = Operation,
+ modification = #'PartialAttribute'{
+ type = Type,
+ vals = Values}}.
+
+%%% --------------------------------------------------------------------
+%%% Modify an entry. Given an entry a number of modification
+%%% operations can be performed as one atomic operation.
+%%% Example:
+%%%
+%%% modify_dn(Handle,
+%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com",
+%%% "cn=Ben Emerson",
+%%% true,
+%%% ""
+%%% )
+%%% --------------------------------------------------------------------
+modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
+ when is_pid(Handle),is_list(Entry),is_list(NewRDN),is_atom(DelOldRDN),is_list(NewSup) ->
+ send(Handle, {modify_dn, Entry, NewRDN,
+ bool_p(DelOldRDN), optional(NewSup)}),
+ recv(Handle).
+
+%%% Sanity checks !
+
+bool_p(Bool) when Bool==true;Bool==false -> Bool.
+
+optional([]) -> asn1_NOVALUE;
+optional(Value) -> Value.
+
+%%% --------------------------------------------------------------------
+%%% Synchronous search of the Directory returning a
+%%% requested set of attributes.
+%%%
+%%% Example:
+%%%
+%%% Filter = eldap:substrings("cn", [{any,"o"}]),
+%%% eldap:search(S, [{base, "dc=bluetail, dc=com"},
+%%% {filter, Filter},
+%%% {attributes,["cn"]}])),
+%%%
+%%% Returned result: {ok, #eldap_search_result{}}
+%%%
+%%% Example:
+%%%
+%%% {ok,{eldap_search_result,
+%%% [{eldap_entry,
+%%% "cn=Magnus Froberg, dc=bluetail, dc=com",
+%%% [{"cn",["Magnus Froberg"]}]},
+%%% {eldap_entry,
+%%% "cn=Torbjorn Tornkvist, dc=bluetail, dc=com",
+%%% [{"cn",["Torbjorn Tornkvist"]}]}],
+%%% []}}
+%%%
+%%% --------------------------------------------------------------------
+search(Handle, A) when is_pid(Handle), is_record(A, eldap_search) ->
+ call_search(Handle, A);
+search(Handle, L) when is_pid(Handle), is_list(L) ->
+ case catch parse_search_args(L) of
+ {error, Emsg} -> {error, Emsg};
+ A when is_record(A, eldap_search) -> call_search(Handle, A)
+ end.
+
+call_search(Handle, A) ->
+ send(Handle, {search, A}),
+ recv(Handle).
+
+parse_search_args(Args) ->
+ parse_search_args(Args,
+ #eldap_search{scope = wholeSubtree,
+ deref = derefAlways}).
+
+parse_search_args([{base, Base}|T],A) ->
+ parse_search_args(T,A#eldap_search{base = Base});
+parse_search_args([{filter, Filter}|T],A) ->
+ parse_search_args(T,A#eldap_search{filter = Filter});
+parse_search_args([{scope, Scope}|T],A) ->
+ parse_search_args(T,A#eldap_search{scope = Scope});
+parse_search_args([{deref, Deref}|T],A) ->
+ parse_search_args(T,A#eldap_search{deref = Deref});
+parse_search_args([{attributes, Attrs}|T],A) ->
+ parse_search_args(T,A#eldap_search{attributes = Attrs});
+parse_search_args([{types_only, TypesOnly}|T],A) ->
+ parse_search_args(T,A#eldap_search{types_only = TypesOnly});
+parse_search_args([{timeout, Timeout}|T],A) when is_integer(Timeout) ->
+ parse_search_args(T,A#eldap_search{timeout = Timeout});
+parse_search_args([H|_],_) ->
+ throw({error,{unknown_arg, H}});
+parse_search_args([],A) ->
+ A.
+
+%%%
+%%% The Scope parameter
+%%%
+baseObject() -> baseObject.
+singleLevel() -> singleLevel.
+wholeSubtree() -> wholeSubtree.
+
+%%
+%% The derefAliases parameter
+%%
+neverDerefAliases() -> neverDerefAliases.
+derefInSearching() -> derefInSearching.
+derefFindingBaseObj() -> derefFindingBaseObj.
+derefAlways() -> derefAlways.
+
+%%%
+%%% Boolean filter operations
+%%%
+'and'(ListOfFilters) when is_list(ListOfFilters) -> {'and',ListOfFilters}.
+'or'(ListOfFilters) when is_list(ListOfFilters) -> {'or', ListOfFilters}.
+'not'(Filter) when is_tuple(Filter) -> {'not',Filter}.
+
+%%%
+%%% The following Filter parameters consist of an attribute
+%%% and an attribute value. Example: F("uid","tobbe")
+%%%
+equalityMatch(Desc, Value) -> {equalityMatch, av_assert(Desc, Value)}.
+greaterOrEqual(Desc, Value) -> {greaterOrEqual, av_assert(Desc, Value)}.
+lessOrEqual(Desc, Value) -> {lessOrEqual, av_assert(Desc, Value)}.
+approxMatch(Desc, Value) -> {approxMatch, av_assert(Desc, Value)}.
+
+av_assert(Desc, Value) ->
+ #'AttributeValueAssertion'{attributeDesc = Desc,
+ assertionValue = Value}.
+
+%%%
+%%% Filter to check for the presence of an attribute
+%%%
+present(Attribute) when is_list(Attribute) ->
+ {present, Attribute}.
+
+
+%%%
+%%% A substring filter seem to be based on a pattern:
+%%%
+%%% InitValue*AnyValue*FinalValue
+%%%
+%%% where all three parts seem to be optional (at least when
+%%% talking with an OpenLDAP server). Thus, the arguments
+%%% to substrings/2 looks like this:
+%%%
+%%% Type ::= string( <attribute> )
+%%% SubStr ::= listof( {initial,Value} | {any,Value}, {final,Value})
+%%%
+%%% Example: substrings("sn",[{initial,"To"},{any,"kv"},{final,"st"}])
+%%% will match entries containing: 'sn: Tornkvist'
+%%%
+substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
+ Ss = {'SubstringFilter_substrings',v_substr(SubStr)},
+ {substrings,#'SubstringFilter'{type = Type,
+ substrings = Ss}}.
+
+%%% --------------------------------------------------------------------
+%%% Worker process. We keep track of a controlling process to
+%%% be able to terminate together with it.
+%%% --------------------------------------------------------------------
+
+init(Hosts, Opts, Cpid) ->
+ Data = parse_args(Opts, Cpid, #eldap{}),
+ case try_connect(Hosts, Data) of
+ {ok,Data2} ->
+ send(Cpid, {ok,self()}),
+ ?MODULE:loop(Cpid, Data2);
+ Else ->
+ send(Cpid, Else),
+ unlink(Cpid),
+ exit(Else)
+ end.
+
+parse_args([{port, Port}|T], Cpid, Data) when is_integer(Port) ->
+ parse_args(T, Cpid, Data#eldap{port = Port});
+parse_args([{timeout, Timeout}|T], Cpid, Data) when is_integer(Timeout),Timeout>0 ->
+ parse_args(T, Cpid, Data#eldap{timeout = Timeout});
+parse_args([{anon_auth, true}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data#eldap{anon_auth = false});
+parse_args([{anon_auth, _}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data);
+parse_args([{ssl, true}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data#eldap{use_tls = true});
+parse_args([{ssl, _}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data);
+parse_args([{log, F}|T], Cpid, Data) when is_function(F) ->
+ parse_args(T, Cpid, Data#eldap{log = F});
+parse_args([{log, _}|T], Cpid, Data) ->
+ parse_args(T, Cpid, Data);
+parse_args([H|_], Cpid, _) ->
+ send(Cpid, {error,{wrong_option,H}}),
+ exit(wrong_option);
+parse_args([], _, Data) ->
+ Data.
+
+%%% Try to connect to the hosts in the listed order,
+%%% and stop with the first one to which a successful
+%%% connection is made.
+
+try_connect([Host|Hosts], Data) ->
+ TcpOpts = [{packet, asn1}, {active,false}],
+ try do_connect(Host, Data, TcpOpts) of
+ {ok,Fd} -> {ok,Data#eldap{host = Host, fd = Fd}};
+ Err ->
+ log2(Data, "Connect: ~p failed ~p~n",[Host, Err]),
+ try_connect(Hosts, Data)
+ catch _:Err ->
+ log2(Data, "Connect: ~p failed ~p~n",[Host, Err]),
+ try_connect(Hosts, Data)
+ end;
+try_connect([],_) ->
+ {error,"connect failed"}.
+
+do_connect(Host, Data, Opts) when Data#eldap.use_tls == false ->
+ gen_tcp:connect(Host, Data#eldap.port, Opts, Data#eldap.timeout);
+do_connect(Host, Data, Opts) when Data#eldap.use_tls == true ->
+ ssl:connect(Host, Data#eldap.port, [{verify,0}|Opts]).
+
+
+loop(Cpid, Data) ->
+ receive
+
+ {From, {search, A}} ->
+ {Res,NewData} = do_search(Data, A),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {modify, Obj, Mod}} ->
+ {Res,NewData} = do_modify(Data, Obj, Mod),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {modify_dn, Obj, NewRDN, DelOldRDN, NewSup}} ->
+ {Res,NewData} = do_modify_dn(Data, Obj, NewRDN, DelOldRDN, NewSup),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {add, Entry, Attrs}} ->
+ {Res,NewData} = do_add(Data, Entry, Attrs),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {delete, Entry}} ->
+ {Res,NewData} = do_delete(Data, Entry),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {simple_bind, Dn, Passwd}} ->
+ {Res,NewData} = do_simple_bind(Data, Dn, Passwd),
+ send(From,Res),
+ ?MODULE:loop(Cpid, NewData);
+
+ {From, {cnt_proc, NewCpid}} ->
+ unlink(Cpid),
+ send(From,ok),
+ ?PRINT("New Cpid is: ~p~n",[NewCpid]),
+ ?MODULE:loop(NewCpid, Data);
+
+ {_From, close} ->
+ unlink(Cpid),
+ exit(closed);
+
+ {Cpid, 'EXIT', Reason} ->
+ ?PRINT("Got EXIT from Cpid, reason=~p~n",[Reason]),
+ exit(Reason);
+
+ _XX ->
+ ?PRINT("loop got: ~p~n",[_XX]),
+ ?MODULE:loop(Cpid, Data)
+
+ end.
+
+%%% --------------------------------------------------------------------
+%%% bindRequest
+%%% --------------------------------------------------------------------
+
+%%% Authenticate ourselves to the directory using
+%%% simple authentication.
+
+do_simple_bind(Data, anon, anon) -> %% For testing
+ do_the_simple_bind(Data, "", "");
+do_simple_bind(Data, Dn, _Passwd) when Dn=="",Data#eldap.anon_auth==false ->
+ {{error,anonymous_auth},Data};
+do_simple_bind(Data, _Dn, Passwd) when Passwd=="",Data#eldap.anon_auth==false ->
+ {{error,anonymous_auth},Data};
+do_simple_bind(Data, Dn, Passwd) ->
+ do_the_simple_bind(Data, Dn, Passwd).
+
+do_the_simple_bind(Data, Dn, Passwd) ->
+ case catch exec_simple_bind(Data#eldap{binddn = Dn,
+ passwd = Passwd,
+ id = bump_id(Data)}) of
+ {ok,NewData} -> {ok,NewData};
+ {error,Emsg} -> {{error,Emsg},Data};
+ Else -> {{error,Else},Data}
+ end.
+
+exec_simple_bind(Data) ->
+ Req = #'BindRequest'{version = Data#eldap.version,
+ name = Data#eldap.binddn,
+ authentication = {simple, Data#eldap.passwd}},
+ log2(Data, "bind request = ~p~n", [Req]),
+ Reply = request(Data#eldap.fd, Data, Data#eldap.id, {bindRequest, Req}),
+ log2(Data, "bind reply = ~p~n", [Reply]),
+ exec_simple_bind_reply(Data, Reply).
+
+exec_simple_bind_reply(Data, {ok,Msg}) when
+ Msg#'LDAPMessage'.messageID == Data#eldap.id ->
+ case Msg#'LDAPMessage'.protocolOp of
+ {bindResponse, Result} ->
+ case Result#'BindResponse'.resultCode of
+ success -> {ok,Data};
+ Error -> {error, Error}
+ end;
+ Other -> {error, Other}
+ end;
+exec_simple_bind_reply(_, Error) ->
+ {error, Error}.
+
+
+%%% --------------------------------------------------------------------
+%%% searchRequest
+%%% --------------------------------------------------------------------
+
+do_search(Data, A) ->
+ case catch do_search_0(Data, A) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,Res,Ref,NewData} -> {{ok,polish(Res, Ref)},NewData};
+ {{error,Reason},NewData} -> {{error,Reason},NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+%%%
+%%% Polish the returned search result
+%%%
+
+polish(Res, Ref) ->
+ R = polish_result(Res),
+ %%% No special treatment of referrals at the moment.
+ #eldap_search_result{entries = R,
+ referrals = Ref}.
+
+polish_result([H|T]) when is_record(H, 'SearchResultEntry') ->
+ ObjectName = H#'SearchResultEntry'.objectName,
+ F = fun({_,A,V}) -> {A,V} end,
+ Attrs = lists:map(F, H#'SearchResultEntry'.attributes),
+ [#eldap_entry{object_name = ObjectName,
+ attributes = Attrs}|
+ polish_result(T)];
+polish_result([]) ->
+ [].
+
+do_search_0(Data, A) ->
+ Req = #'SearchRequest'{baseObject = A#eldap_search.base,
+ scope = v_scope(A#eldap_search.scope),
+ derefAliases = v_deref(A#eldap_search.deref),
+ sizeLimit = 0, % no size limit
+ timeLimit = v_timeout(A#eldap_search.timeout),
+ typesOnly = v_bool(A#eldap_search.types_only),
+ filter = v_filter(A#eldap_search.filter),
+ attributes = v_attributes(A#eldap_search.attributes)
+ },
+ Id = bump_id(Data),
+ collect_search_responses(Data#eldap{id=Id}, Req, Id).
+
+%%% The returned answers cames in one packet per entry
+%%% mixed with possible referals
+
+collect_search_responses(Data, Req, ID) ->
+ S = Data#eldap.fd,
+ log2(Data, "search request = ~p~n", [Req]),
+ send_request(S, Data, ID, {searchRequest, Req}),
+ Resp = recv_response(S, Data),
+ log2(Data, "search reply = ~p~n", [Resp]),
+ collect_search_responses(Data, S, ID, Resp, [], []).
+
+collect_search_responses(Data, S, ID, {ok,Msg}, Acc, Ref)
+ when is_record(Msg,'LDAPMessage') ->
+ case Msg#'LDAPMessage'.protocolOp of
+ {'searchResDone',R} ->
+ case R#'LDAPResult'.resultCode of
+ success ->
+ log2(Data, "search reply = searchResDone ~n", []),
+ {ok,Acc,Ref,Data};
+ Reason ->
+ {{error,Reason},Data}
+ end;
+ {'searchResEntry',R} when is_record(R,'SearchResultEntry') ->
+ Resp = recv_response(S, Data),
+ log2(Data, "search reply = ~p~n", [Resp]),
+ collect_search_responses(Data, S, ID, Resp, [R|Acc], Ref);
+ {'searchResRef',R} ->
+ %% At the moment we don't do anyting sensible here since
+ %% I haven't been able to trigger the server to generate
+ %% a response like this.
+ Resp = recv_response(S, Data),
+ log2(Data, "search reply = ~p~n", [Resp]),
+ collect_search_responses(Data, S, ID, Resp, Acc, [R|Ref]);
+ Else ->
+ throw({error,Else})
+ end;
+collect_search_responses(_, _, _, Else, _, _) ->
+ throw({error,Else}).
+
+%%% --------------------------------------------------------------------
+%%% addRequest
+%%% --------------------------------------------------------------------
+
+do_add(Data, Entry, Attrs) ->
+ case catch do_add_0(Data, Entry, Attrs) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,NewData} -> {ok,NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+do_add_0(Data, Entry, Attrs) ->
+ Req = #'AddRequest'{entry = Entry,
+ attributes = Attrs},
+ S = Data#eldap.fd,
+ Id = bump_id(Data),
+ log2(Data, "add request = ~p~n", [Req]),
+ Resp = request(S, Data, Id, {addRequest, Req}),
+ log2(Data, "add reply = ~p~n", [Resp]),
+ check_reply(Data#eldap{id = Id}, Resp, addResponse).
+
+
+%%% --------------------------------------------------------------------
+%%% deleteRequest
+%%% --------------------------------------------------------------------
+
+do_delete(Data, Entry) ->
+ case catch do_delete_0(Data, Entry) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,NewData} -> {ok,NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+do_delete_0(Data, Entry) ->
+ S = Data#eldap.fd,
+ Id = bump_id(Data),
+ log2(Data, "del request = ~p~n", [Entry]),
+ Resp = request(S, Data, Id, {delRequest, Entry}),
+ log2(Data, "del reply = ~p~n", [Resp]),
+ check_reply(Data#eldap{id = Id}, Resp, delResponse).
+
+
+%%% --------------------------------------------------------------------
+%%% modifyRequest
+%%% --------------------------------------------------------------------
+
+do_modify(Data, Obj, Mod) ->
+ case catch do_modify_0(Data, Obj, Mod) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,NewData} -> {ok,NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+do_modify_0(Data, Obj, Mod) ->
+ v_modifications(Mod),
+ Req = #'ModifyRequest'{object = Obj,
+ changes = Mod},
+ S = Data#eldap.fd,
+ Id = bump_id(Data),
+ log2(Data, "modify request = ~p~n", [Req]),
+ Resp = request(S, Data, Id, {modifyRequest, Req}),
+ log2(Data, "modify reply = ~p~n", [Resp]),
+ check_reply(Data#eldap{id = Id}, Resp, modifyResponse).
+
+%%% --------------------------------------------------------------------
+%%% modifyDNRequest
+%%% --------------------------------------------------------------------
+
+do_modify_dn(Data, Entry, NewRDN, DelOldRDN, NewSup) ->
+ case catch do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) of
+ {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
+ {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {ok,NewData} -> {ok,NewData};
+ Else -> {ldap_closed_p(Data, Else),Data}
+ end.
+
+do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) ->
+ Req = #'ModifyDNRequest'{entry = Entry,
+ newrdn = NewRDN,
+ deleteoldrdn = DelOldRDN,
+ newSuperior = NewSup},
+ S = Data#eldap.fd,
+ Id = bump_id(Data),
+ log2(Data, "modify DN request = ~p~n", [Req]),
+ Resp = request(S, Data, Id, {modDNRequest, Req}),
+ log2(Data, "modify DN reply = ~p~n", [Resp]),
+ check_reply(Data#eldap{id = Id}, Resp, modDNResponse).
+
+%%% --------------------------------------------------------------------
+%%% Send an LDAP request and receive the answer
+%%% --------------------------------------------------------------------
+
+request(S, Data, ID, Request) ->
+ send_request(S, Data, ID, Request),
+ recv_response(S, Data).
+
+send_request(S, Data, ID, Request) ->
+ Message = #'LDAPMessage'{messageID = ID,
+ protocolOp = Request},
+ {ok,Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message),
+ case do_send(S, Data, Bytes) of
+ {error,Reason} -> throw({gen_tcp_error,Reason});
+ Else -> Else
+ end.
+
+do_send(S, Data, Bytes) when Data#eldap.use_tls == false ->
+ gen_tcp:send(S, Bytes);
+do_send(S, Data, Bytes) when Data#eldap.use_tls == true ->
+ ssl:send(S, Bytes).
+
+do_recv(S, #eldap{use_tls=false, timeout=Timeout}, Len) ->
+ gen_tcp:recv(S, Len, Timeout);
+do_recv(S, #eldap{use_tls=true, timeout=Timeout}, Len) ->
+ ssl:recv(S, Len, Timeout).
+
+recv_response(S, Data) ->
+ case do_recv(S, Data, 0) of
+ {ok, Packet} ->
+ check_tag(Packet),
+ case asn1rt:decode('ELDAPv3', 'LDAPMessage', Packet) of
+ {ok,Resp} -> {ok,Resp};
+ Error -> throw(Error)
+ end;
+ {error,Reason} ->
+ throw({gen_tcp_error, Reason});
+ Error ->
+ throw(Error)
+ end.
+
+%%% Sanity check of received packet
+check_tag(Data) ->
+ case asn1rt_ber_bin:decode_tag(l2b(Data)) of
+ {_Tag, Data1, _Rb} ->
+ case asn1rt_ber_bin:decode_length(l2b(Data1)) of
+ {{_Len, _Data2}, _Rb2} -> ok;
+ _ -> throw({error,decoded_tag_length})
+ end;
+ _ -> throw({error,decoded_tag})
+ end.
+
+%%% Check for expected kind of reply
+check_reply(Data, {ok,Msg}, Op) when
+ Msg#'LDAPMessage'.messageID == Data#eldap.id ->
+ case Msg#'LDAPMessage'.protocolOp of
+ {Op, Result} ->
+ case Result#'LDAPResult'.resultCode of
+ success -> {ok,Data};
+ Error -> {error, Error}
+ end;
+ Other -> {error, Other}
+ end;
+check_reply(_, Error, _) ->
+ {error, Error}.
+
+
+%%% --------------------------------------------------------------------
+%%% Verify the input data
+%%% --------------------------------------------------------------------
+
+v_filter({'and',L}) -> {'and',L};
+v_filter({'or', L}) -> {'or',L};
+v_filter({'not',L}) -> {'not',L};
+v_filter({equalityMatch,AV}) -> {equalityMatch,AV};
+v_filter({greaterOrEqual,AV}) -> {greaterOrEqual,AV};
+v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};
+v_filter({approxMatch,AV}) -> {approxMatch,AV};
+v_filter({present,A}) -> {present,A};
+v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S};
+v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).
+
+v_modifications(Mods) ->
+ F = fun({_,Op,_}) ->
+ case lists:member(Op,[add,delete,replace]) of
+ true -> true;
+ _ -> throw({error,{mod_operation,Op}})
+ end
+ end,
+ lists:foreach(F, Mods).
+
+v_substr([{Key,Str}|T]) when is_list(Str),Key==initial;Key==any;Key==final ->
+ [{Key,Str}|v_substr(T)];
+v_substr([H|_]) ->
+ throw({error,{substring_arg,H}});
+v_substr([]) ->
+ [].
+v_scope(baseObject) -> baseObject;
+v_scope(singleLevel) -> singleLevel;
+v_scope(wholeSubtree) -> wholeSubtree;
+v_scope(_Scope) -> throw({error,concat(["unknown scope: ",_Scope])}).
+
+v_deref(DR = neverDerefAliases) -> DR;
+v_deref(DR = derefInSearching) -> DR;
+v_deref(DR = derefFindingBaseObj) -> DR;
+v_deref(DR = derefAlways ) -> DR.
+
+v_bool(true) -> true;
+v_bool(false) -> false;
+v_bool(_Bool) -> throw({error,concat(["not Boolean: ",_Bool])}).
+
+v_timeout(I) when is_integer(I), I>=0 -> I;
+v_timeout(_I) -> throw({error,concat(["timeout not positive integer: ",_I])}).
+
+v_attributes(Attrs) ->
+ F = fun(A) when is_list(A) -> A;
+ (A) -> throw({error,concat(["attribute not String: ",A])})
+ end,
+ lists:map(F,Attrs).
+
+
+%%% --------------------------------------------------------------------
+%%% Log routines. Call a user provided log routine F.
+%%% --------------------------------------------------------------------
+
+%log1(Data, Str, Args) -> log(Data, Str, Args, 1).
+log2(Data, Str, Args) -> log(Data, Str, Args, 2).
+
+log(Data, Str, Args, Level) when is_function(Data#eldap.log) ->
+ catch (Data#eldap.log)(Level, Str, Args);
+log(_, _, _, _) ->
+ ok.
+
+
+%%% --------------------------------------------------------------------
+%%% Misc. routines
+%%% --------------------------------------------------------------------
+
+send(To,Msg) -> To ! {self(),Msg}.
+recv(From) ->
+ receive
+ {From,Msg} -> Msg;
+ {'EXIT', From, Reason} ->
+ {error, {internal_error, Reason}}
+ end.
+
+ldap_closed_p(Data, Emsg) when Data#eldap.use_tls == true ->
+ %% Check if the SSL socket seems to be alive or not
+ case catch ssl:sockname(Data#eldap.fd) of
+ {error, _} ->
+ ssl:close(Data#eldap.fd),
+ {error, ldap_closed};
+ {ok, _} ->
+ {error, Emsg};
+ _ ->
+ %% sockname crashes if the socket pid is not alive
+ {error, ldap_closed}
+ end;
+ldap_closed_p(Data, Emsg) ->
+ %% non-SSL socket
+ case inet:port(Data#eldap.fd) of
+ {error,_} -> {error, ldap_closed};
+ _ -> {error,Emsg}
+ end.
+
+bump_id(Data) -> Data#eldap.id + 1.
+
+
+%%% --------------------------------------------------------------------
+%%% parse_dn/1 - Implementation of RFC 2253:
+%%%
+%%% "UTF-8 String Representation of Distinguished Names"
+%%%
+%%% Test cases:
+%%%
+%%% The simplest case:
+%%%
+%%% 1> eldap:parse_dn("CN=Steve Kille,O=Isode Limited,C=GB").
+%%% {ok,[[{attribute_type_and_value,"CN","Steve Kille"}],
+%%% [{attribute_type_and_value,"O","Isode Limited"}],
+%%% [{attribute_type_and_value,"C","GB"}]]}
+%%%
+%%% The first RDN is multi-valued:
+%%%
+%%% 2> eldap:parse_dn("OU=Sales+CN=J. Smith,O=Widget Inc.,C=US").
+%%% {ok,[[{attribute_type_and_value,"OU","Sales"},
+%%% {attribute_type_and_value,"CN","J. Smith"}],
+%%% [{attribute_type_and_value,"O","Widget Inc."}],
+%%% [{attribute_type_and_value,"C","US"}]]}
+%%%
+%%% Quoting a comma:
+%%%
+%%% 3> eldap:parse_dn("CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB").
+%%% {ok,[[{attribute_type_and_value,"CN","L. Eagle"}],
+%%% [{attribute_type_and_value,"O","Sue\\, Grabbit and Runn"}],
+%%% [{attribute_type_and_value,"C","GB"}]]}
+%%%
+%%% A value contains a carriage return:
+%%%
+%%% 4> eldap:parse_dn("CN=Before
+%%% 4> After,O=Test,C=GB").
+%%% {ok,[[{attribute_type_and_value,"CN","Before\nAfter"}],
+%%% [{attribute_type_and_value,"O","Test"}],
+%%% [{attribute_type_and_value,"C","GB"}]]}
+%%%
+%%% 5> eldap:parse_dn("CN=Before\\0DAfter,O=Test,C=GB").
+%%% {ok,[[{attribute_type_and_value,"CN","Before\\0DAfter"}],
+%%% [{attribute_type_and_value,"O","Test"}],
+%%% [{attribute_type_and_value,"C","GB"}]]}
+%%%
+%%% An RDN in OID form:
+%%%
+%%% 6> eldap:parse_dn("1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB").
+%%% {ok,[[{attribute_type_and_value,"1.3.6.1.4.1.1466.0","#04024869"}],
+%%% [{attribute_type_and_value,"O","Test"}],
+%%% [{attribute_type_and_value,"C","GB"}]]}
+%%%
+%%%
+%%% --------------------------------------------------------------------
+
+parse_dn("") -> % empty DN string
+ {ok,[]};
+parse_dn([H|_] = Str) when H=/=$, -> % 1:st name-component !
+ case catch parse_name(Str,[]) of
+ {'EXIT',Reason} -> {parse_error,internal_error,Reason};
+ Else -> Else
+ end.
+
+parse_name("",Acc) ->
+ {ok,lists:reverse(Acc)};
+parse_name([$,|T],Acc) -> % N:th name-component !
+ parse_name(T,Acc);
+parse_name(Str,Acc) ->
+ {Rest,NameComponent} = parse_name_component(Str),
+ parse_name(Rest,[NameComponent|Acc]).
+
+parse_name_component(Str) ->
+ parse_name_component(Str,[]).
+
+parse_name_component(Str,Acc) ->
+ case parse_attribute_type_and_value(Str) of
+ {[$+|Rest], ATV} ->
+ parse_name_component(Rest,[ATV|Acc]);
+ {Rest,ATV} ->
+ {Rest,lists:reverse([ATV|Acc])}
+ end.
+
+parse_attribute_type_and_value(Str) ->
+ case parse_attribute_type(Str) of
+ {_Rest,[]} ->
+ parse_error(expecting_attribute_type,Str);
+ {Rest,Type} ->
+ Rest2 = parse_equal_sign(Rest),
+ {Rest3,Value} = parse_attribute_value(Rest2),
+ {Rest3,{attribute_type_and_value,Type,Value}}
+ end.
+
+-define(IS_ALPHA(X) , X>=$a,X=<$z;X>=$A,X=<$Z ).
+-define(IS_DIGIT(X) , X>=$0,X=<$9 ).
+-define(IS_SPECIAL(X) , X==$,;X==$=;X==$+;X==$<;X==$>;X==$#;X==$; ).
+-define(IS_QUOTECHAR(X) , X=/=$\\,X=/=$" ).
+-define(IS_STRINGCHAR(X) ,
+ X=/=$,,X=/=$=,X=/=$+,X=/=$<,X=/=$>,X=/=$#,X=/=$;,?IS_QUOTECHAR(X) ).
+-define(IS_HEXCHAR(X) , ?IS_DIGIT(X);X>=$a,X=<$f;X>=$A,X=<$F ).
+
+parse_attribute_type([H|T]) when ?IS_ALPHA(H) ->
+ %% NB: It must be an error in the RFC in the definition
+ %% of 'attributeType', should be: (ALPHA *keychar)
+ {Rest,KeyChars} = parse_keychars(T),
+ {Rest,[H|KeyChars]};
+parse_attribute_type([H|_] = Str) when ?IS_DIGIT(H) ->
+ parse_oid(Str);
+parse_attribute_type(Str) ->
+ parse_error(invalid_attribute_type,Str).
+
+
+
+%%% Is a hexstring !
+parse_attribute_value([$#,X,Y|T]) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) ->
+ {Rest,HexString} = parse_hexstring(T),
+ {Rest,[$#,X,Y|HexString]};
+%%% Is a "quotation-sequence" !
+parse_attribute_value([$"|T]) ->
+ {Rest,Quotation} = parse_quotation(T),
+ {Rest,[$"|Quotation]};
+%%% Is a stringchar , pair or Empty !
+parse_attribute_value(Str) ->
+ parse_string(Str).
+
+parse_hexstring(Str) ->
+ parse_hexstring(Str,[]).
+
+parse_hexstring([X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) ->
+ parse_hexstring(T,[Y,X|Acc]);
+parse_hexstring(T,Acc) ->
+ {T,lists:reverse(Acc)}.
+
+parse_quotation([$"|T]) -> % an empty: "" is ok !
+ {T,[$"]};
+parse_quotation(Str) ->
+ parse_quotation(Str,[]).
+
+%%% Parse to end of quotation
+parse_quotation([$"|T],Acc) ->
+ {T,lists:reverse([$"|Acc])};
+parse_quotation([X|T],Acc) when ?IS_QUOTECHAR(X) ->
+ parse_quotation(T,[X|Acc]);
+parse_quotation([$\\,X|T],Acc) when ?IS_SPECIAL(X) ->
+ parse_quotation(T,[X,$\\|Acc]);
+parse_quotation([$\\,$\\|T],Acc) ->
+ parse_quotation(T,[$\\,$\\|Acc]);
+parse_quotation([$\\,$"|T],Acc) ->
+ parse_quotation(T,[$",$\\|Acc]);
+parse_quotation([$\\,X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) ->
+ parse_quotation(T,[Y,X,$\\|Acc]);
+parse_quotation(T,_) ->
+ parse_error(expecting_double_quote_mark,T).
+
+parse_string(Str) ->
+ parse_string(Str,[]).
+
+parse_string("",Acc) ->
+ {"",lists:reverse(Acc)};
+parse_string([H|T],Acc) when ?IS_STRINGCHAR(H) ->
+ parse_string(T,[H|Acc]);
+parse_string([$\\,X|T],Acc) when ?IS_SPECIAL(X) -> % is a pair !
+ parse_string(T,[X,$\\|Acc]);
+parse_string([$\\,$\\|T],Acc) -> % is a pair !
+ parse_string(T,[$\\,$\\|Acc]);
+parse_string([$\\,$" |T],Acc) -> % is a pair !
+ parse_string(T,[$" ,$\\|Acc]);
+parse_string([$\\,X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) -> % is a pair!
+ parse_string(T,[Y,X,$\\|Acc]);
+parse_string(T,Acc) ->
+ {T,lists:reverse(Acc)}.
+
+parse_equal_sign([$=|T]) -> T;
+parse_equal_sign(T) -> parse_error(expecting_equal_sign,T).
+
+parse_keychars(Str) -> parse_keychars(Str,[]).
+
+parse_keychars([H|T],Acc) when ?IS_ALPHA(H) -> parse_keychars(T,[H|Acc]);
+parse_keychars([H|T],Acc) when ?IS_DIGIT(H) -> parse_keychars(T,[H|Acc]);
+parse_keychars([$-|T],Acc) -> parse_keychars(T,[$-|Acc]);
+parse_keychars(T,Acc) -> {T,lists:reverse(Acc)}.
+
+parse_oid(Str) -> parse_oid(Str,[]).
+
+parse_oid([H,$.|T], Acc) when ?IS_DIGIT(H) ->
+ parse_oid(T,[$.,H|Acc]);
+parse_oid([H|T], Acc) when ?IS_DIGIT(H) ->
+ parse_oid(T,[H|Acc]);
+parse_oid(T, Acc) ->
+ {T,lists:reverse(Acc)}.
+
+parse_error(Emsg,Rest) ->
+ throw({parse_error,Emsg,Rest}).
+
+
+%%% --------------------------------------------------------------------
+%%% Parse LDAP url according to RFC 2255
+%%%
+%%% Test case:
+%%%
+%%% 2> eldap:parse_ldap_url("ldap://10.42.126.33:389/cn=Administrative%20CA,o=Post%20Danmark,c=DK?certificateRevokationList;binary").
+%%% {ok,{{10,42,126,33},389},
+%%% [[{attribute_type_and_value,"cn","Administrative%20CA"}],
+%%% [{attribute_type_and_value,"o","Post%20Danmark"}],
+%%% [{attribute_type_and_value,"c","DK"}]],
+%%% {attributes,["certificateRevokationList;binary"]}}
+%%%
+%%% --------------------------------------------------------------------
+
+parse_ldap_url("ldap://" ++ Rest1 = Str) ->
+ {Rest2,HostPort} = parse_hostport(Rest1),
+ %% Split the string into DN and Attributes+etc
+ {Sdn,Rest3} = split_string(rm_leading_slash(Rest2),$?),
+ case parse_dn(Sdn) of
+ {parse_error,internal_error,_Reason} ->
+ {parse_error,internal_error,{Str,[]}};
+ {parse_error,Emsg,Tail} ->
+ Head = get_head(Str,Tail),
+ {parse_error,Emsg,{Head,Tail}};
+ {ok,DN} ->
+ %% We stop parsing here for now and leave
+ %% 'scope', 'filter' and 'extensions' to
+ %% be implemented later if needed.
+ {_Rest4,Attributes} = parse_attributes(Rest3),
+ {ok,HostPort,DN,Attributes}
+ end.
+
+rm_leading_slash([$/|Tail]) -> Tail;
+rm_leading_slash(Tail) -> Tail.
+
+parse_attributes([$?|Tail]) ->
+ case split_string(Tail,$?) of
+ {[],Attributes} ->
+ {[],{attributes,string:tokens(Attributes,",")}};
+ {Attributes,Rest} ->
+ {Rest,{attributes,string:tokens(Attributes,",")}}
+ end.
+
+parse_hostport(Str) ->
+ {HostPort,Rest} = split_string(Str,$/),
+ case split_string(HostPort,$:) of
+ {Shost,[]} ->
+ {Rest,{parse_host(Rest,Shost),?LDAP_PORT}};
+ {Shost,[$:|Sport]} ->
+ {Rest,{parse_host(Rest,Shost),
+ parse_port(Rest,Sport)}}
+ end.
+
+parse_port(Rest,Sport) ->
+ try list_to_integer(Sport)
+ catch _:_ -> parse_error(parsing_port,Rest)
+ end.
+
+parse_host(Rest,Shost) ->
+ case catch validate_host(Shost) of
+ {parse_error,Emsg,_} -> parse_error(Emsg,Rest);
+ Host -> Host
+ end.
+
+validate_host(Shost) ->
+ case inet_parse:address(Shost) of
+ {ok,Host} -> Host;
+ _ ->
+ case inet_parse:domain(Shost) of
+ true -> Shost;
+ _ -> parse_error(parsing_host,Shost)
+ end
+ end.
+
+
+split_string(Str,Key) ->
+ Pred = fun(X) when X==Key -> false; (_) -> true end,
+ lists:splitwith(Pred, Str).
+
+get_head(Str,Tail) ->
+ get_head(Str,Tail,[]).
+
+%%% Should always succeed !
+get_head([H|Tail],Tail,Rhead) -> lists:reverse([H|Rhead]);
+get_head([H|Rest],Tail,Rhead) -> get_head(Rest,Tail,[H|Rhead]).
+
+l2b(B) when is_binary(B) -> B;
+l2b(L) when is_list(L) -> list_to_binary(L).
+
diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile
new file mode 100644
index 0000000000..a17d4f56b2
--- /dev/null
+++ b/lib/eldap/test/Makefile
@@ -0,0 +1,83 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2012. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+
+INCLUDES= -I. -I ../include
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ eldap_basic_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+HRL_FILES=
+
+TARGET_FILES= \
+ $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+SPEC_FILES = eldap.spec
+
+# COVER_FILE = eldap.cover
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/eldap_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += $(INCLUDES)
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(TARGET_FILES)
+
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
+# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+release_docs_spec:
diff --git a/lib/eldap/test/eldap.spec b/lib/eldap/test/eldap.spec
new file mode 100644
index 0000000000..b14e9a655a
--- /dev/null
+++ b/lib/eldap/test/eldap.spec
@@ -0,0 +1 @@
+{suites,"../eldap_test",all}.
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
new file mode 100644
index 0000000000..c7e3052b29
--- /dev/null
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -0,0 +1,222 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(eldap_basic_SUITE).
+
+-compile(export_all).
+
+%%-include_lib("common_test/include/ct.hrl").
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("eldap/include/eldap.hrl").
+
+-define(TIMEOUT, 120000). % 2 min
+
+init_per_suite(Config0) ->
+ {{EldapHost,Port}, Config1} =
+ case catch ct:get_config(eldap_server, undefined) of
+ undefined -> %% Dev test only
+ Server = {"localhost", 9876},
+ {Server, [{eldap_server, {"localhost", 9876}}|Config0]};
+ {'EXIT', _} -> %% Dev test only
+ Server = {"localhost", 9876},
+ {Server, [{eldap_server, {"localhost", 9876}}|Config0]};
+ Server ->
+ {Server, [{eldap_server, Server}|Config0]}
+ end,
+ %% Add path for this test run
+ try
+ {ok, Handle} = eldap:open([EldapHost], [{port, Port}]),
+ ok = eldap:simple_bind(Handle, "cn=Manager,dc=ericsson,dc=se", "hejsan"),
+ {ok, MyHost} = inet:gethostname(),
+ Path = "dc="++MyHost++",dc=ericsson,dc=se",
+ Config = [{eldap_path,Path}|Config1],
+ eldap:add(Handle,"dc=ericsson,dc=se",
+ [{"objectclass", ["dcObject", "organization"]},
+ {"dc", ["ericsson"]}, {"o", ["Testing"]}]),
+ eldap:add(Handle,Path,
+ [{"objectclass", ["dcObject", "organization"]},
+ {"dc", [MyHost]}, {"o", ["Test machine"]}]),
+ Config
+ catch error:{badmatch,Error} ->
+ io:format("Eldap init error ~p~n ~p~n",[Error, erlang:get_stacktrace()]),
+ {skip, lists:flatten(io_lib:format("Ldap init failed with host ~p", [EldapHost]))}
+ end.
+end_per_suite(Config) ->
+ %% Cleanup everything
+ {EHost, Port} = proplists:get_value(eldap_server, Config),
+ Path = proplists:get_value(eldap_path, Config),
+ {ok, H} = eldap:open([EHost], [{port, Port}]),
+ ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"),
+ case eldap:search(H, [{base, Path},
+ {filter, eldap:present("objectclass")},
+ {scope, eldap:wholeSubtree()}])
+ of
+ {ok, {eldap_search_result, Entries, _}} ->
+ [ok = eldap:delete(H, Entry) || {eldap_entry, Entry, _} <- Entries];
+ _ -> ignore
+ end,
+ ok.
+
+init_per_testcase(_TestCase, Config) -> Config.
+end_per_testcase(_TestCase, _Config) -> ok.
+
+%% suite() ->
+
+all() ->
+ [app,
+ api].
+
+app(doc) -> "Test that the eldap app file is ok";
+app(suite) -> [];
+app(Config) when is_list(Config) ->
+ ok = test_server:app_test(public_key).
+
+api(doc) -> "Basic test that all api functions works as expected";
+api(suite) -> [];
+api(Config) ->
+ {Host,Port} = proplists:get_value(eldap_server, Config),
+ {ok, H} = eldap:open([Host], [{port,Port}]),
+ %% {ok, H} = eldap:open([Host], [{port,Port+1}, {ssl, true}]),
+ BasePath = proplists:get_value(eldap_path, Config),
+ All = fun(Where) ->
+ eldap:search(H, #eldap_search{base=Where,
+ filter=eldap:present("objectclass"),
+ scope= eldap:wholeSubtree()})
+ end,
+ Search = fun(Filter) ->
+ eldap:search(H, #eldap_search{base=BasePath,
+ filter=Filter,
+ scope=eldap:singleLevel()})
+ end,
+ {ok, #eldap_search_result{entries=[_]}} = All(BasePath),
+ {error, noSuchObject} = All("cn=Bar,"++BasePath),
+
+ {error, _} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath,
+ [{"objectclass", ["person"]},
+ {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]),
+ eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"),
+
+ %% Add
+ ok = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath,
+ [{"objectclass", ["person"]},
+ {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]),
+ ok = eldap:add(H, "cn=Foo Bar," ++ BasePath,
+ [{"objectclass", ["person"]},
+ {"cn", ["Foo Bar"]}, {"sn", ["Bar"]}, {"telephoneNumber", ["555-1232", "555-5432"]}]),
+ ok = eldap:add(H, "ou=Team," ++ BasePath,
+ [{"objectclass", ["organizationalUnit"]},
+ {"ou", ["Team"]}]),
+
+ %% Search
+ JJSR = {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:equalityMatch("sn", "Jonsson")),
+ JJSR = Search(eldap:substrings("sn", [{any, "ss"}])),
+ FBSR = {ok, #eldap_search_result{entries=[#eldap_entry{object_name=FB}]}} =
+ Search(eldap:substrings("sn", [{any, "a"}])),
+ FBSR = Search(eldap:substrings("sn", [{initial, "B"}])),
+ FBSR = Search(eldap:substrings("sn", [{final, "r"}])),
+
+ F_AND = eldap:'and'([eldap:present("objectclass"), eldap:present("ou")]),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(F_AND),
+ F_NOT = eldap:'and'([eldap:present("objectclass"), eldap:'not'(eldap:present("ou"))]),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}, #eldap_entry{}]}} = Search(F_NOT),
+
+ %% MODIFY
+ Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]),
+ eldap:mod_add("description", ["Nice guy"])],
+ %% io:format("MOD ~p ~p ~n",[FB, Mod]),
+ ok = eldap:modify(H, FB, Mod),
+ %% DELETE ATTR
+ ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]),
+
+ %% DELETE
+ {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath,
+ [{"objectclass", ["person"]},
+ {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]),
+ ok = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath),
+ {error, noSuchObject} = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath),
+
+ %% MODIFY_DN
+ ok = eldap:modify_dn(H, FB, "cn=Niclas Andre", true, ""),
+ %%io:format("Res ~p~n ~p~n",[R, All(BasePath)]),
+
+ eldap:close(H),
+ ok.
+
+add(H, Attr, Value, Path0, Attrs, Class) ->
+ Path = case Path0 of
+ [] -> Attr ++ "=" ++ Value;
+ _ -> Attr ++ "=" ++ Value ++ "," ++ Path0
+ end,
+ case eldap:add(H, Path, [{"objectclass", Class}, {Attr, [Value]}] ++ Attrs)
+ of
+ ok -> {ok, Path};
+ {error, E = entryAlreadyExists} -> {E, Path};
+ R = {error, Reason} ->
+ io:format("~p:~p: ~s,~s =>~n ~p~n",
+ [?MODULE,?LINE, Attr, Value, R]),
+ exit({ldap, add, Reason})
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Develop
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+test() ->
+ run().
+
+run() ->
+ Cases = all(),
+ run(Cases).
+
+run(Case) when is_atom(Case) ->
+ run([Case]);
+run(Cases) when is_list(Cases) ->
+ Run = fun(Test, Config0) ->
+ Config = init_per_testcase(Test, Config0),
+ try
+ io:format("~nTest ~p ... ",[Test]),
+ ?MODULE:Test(Config),
+ end_per_testcase(Test, Config),
+ io:format("ok~n",[])
+ catch _:Reason ->
+ io:format("~n FAIL (~p): ~p~n ~p~n",
+ [Test, Reason, erlang:get_stacktrace()])
+ end
+ end,
+ process_flag(trap_exit, true),
+ Pid = spawn_link(fun() ->
+ case init_per_suite([]) of
+ {skip, Reason} -> io:format("Skip ~s~n",[Reason]);
+ Config ->
+ try
+ [Run(Test, Config) || Test <- Cases]
+ catch _:Err ->
+ io:format("Error ~p in ~p~n",[Err, erlang:get_stacktrace()])
+ end,
+ end_per_suite(Config)
+ end
+ end),
+ receive
+ {'EXIT', Pid, normal} -> ok;
+ Msg -> io:format("Received ~p (~p)~n",[Msg, Pid])
+ after 100 -> ok end,
+ process_flag(trap_exit, false),
+ ok.
diff --git a/lib/eldap/test/ldap_server/slapd.conf b/lib/eldap/test/ldap_server/slapd.conf
new file mode 100644
index 0000000000..87be676d9f
--- /dev/null
+++ b/lib/eldap/test/ldap_server/slapd.conf
@@ -0,0 +1,14 @@
+include /etc/ldap/schema/core.schema
+pidfile /tmp/openldap-data/slapd.pid
+argsfile /tmp/openldap-data/slapd.args
+
+database bdb
+suffix "dc=ericsson,dc=se"
+rootdn "cn=Manager,dc=ericsson,dc=se"
+rootpw hejsan
+# The database must exist before running slapd
+directory /tmp/openldap-data
+# Indices to maintain
+index objectClass eq
+# URI "ldap://0.0.0.0:9876 ldaps://0.0.0.0:9870"
+# servers/slapd/slapd -d 255 -h "ldap://0.0.0.0:9876 ldaps://0.0.0.0:9870" -f /ldisk/dgud/src/otp/lib/eldap/test/ldap_server/slapd.conf \ No newline at end of file
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
new file mode 100644
index 0000000000..c9d6e4e324
--- /dev/null
+++ b/lib/eldap/vsn.mk
@@ -0,0 +1 @@
+ELDAP_VSN = 1.0
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 79ece7edf5..5bbce9d076 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -318,7 +318,7 @@ browse_file(Port,File) ->
%% The page where a filename can be entered
title(Port,"read_file_frame","Read File"),
-
+
%% Load a file
Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file",
Html = request_sync(post,{Url,[],[],"path="++File}),
@@ -711,6 +711,7 @@ rel_opt(Rel) ->
r11b -> [{erl,[{release,"r11b_patched"}]}];
r12b -> [{erl,[{release,"r12b_patched"}]}];
r13b -> [{erl,[{release,"r13b_patched"}]}];
+ r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed
current -> []
end.
@@ -734,6 +735,6 @@ compat_rel(Rel) ->
r11b -> "+R11 ";
r12b -> "+R12 ";
r13b -> "+R13 ";
- r14b -> "+R13 ";
+ r14b -> "+R14 ";
current -> ""
end.
diff --git a/lib/orber/doc/src/ch_install.xml b/lib/orber/doc/src/ch_install.xml
index de9c0e3a9d..e5af2a4a27 100644
--- a/lib/orber/doc/src/ch_install.xml
+++ b/lib/orber/doc/src/ch_install.xml
@@ -396,13 +396,13 @@ nodeB@hostB> orber:start().
<row>
<cell align="left" valign="middle">ssl_server_options</cell>
<cell align="left" valign="middle">list()</cell>
- <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL</seealso> application
+ <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL application</seealso>
for valid options.</cell>
</row>
<row>
<cell align="left" valign="middle">ssl_client_options</cell>
<cell align="left" valign="middle">list()</cell>
- <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL</seealso> application
+ <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL application</seealso>
for valid options.</cell>
</row>
<row>
@@ -631,9 +631,19 @@ nodeB@hostB> orber:start().
<c>{local, DefaultNATPort, [{Port, NATPort}]}</c>. See also
<seealso marker="ch_install#firewall">Firewall Configuration</seealso>.</item>
<tag><em>ssl_server_options</em></tag>
- <item>the file path to a server side CA certificate.</item>
+ <item>A list of the SSL options when Orber is the server.
+ In general it's just to remove the 'ssl_server_' prefix from the old options,
+ i.e. <c>ssl_server_verify</c> will just be <c>verify</c> in this option list.
+ See the <seealso marker="ssl:ssl">SSL application</seealso> for the correct list of possible
+ options and their values.
+ </item>
<tag><em>ssl_client_options</em></tag>
- <item>The path to a file containing a chain of PEM encoded certificates.</item>
+ <item>A list of the SSL options when Orber is the client.
+ In general it's just to remove the <c>ssl_client_</c> prefix from the old options,
+ i.e. <c>ssl_client_depth</c> will just be <c>depth</c> in this option list.
+ See the <seealso marker="ssl:ssl">SSL application</seealso> for the correct list of possible
+ options and their values.
+ </item>
<tag><em>iiop_ssl_out_keepalive</em></tag>
<item>Enables periodic transmission on a connected socket, when no other
data is being exchanged. If the other end does not respond, the
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 71d3b60a2b..26dcd82447 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -131,21 +131,22 @@
trap_exit :: boolean()
}).
+%% Types '$1','$2' and '_' are needed for match specs to ets:select
-record(mod,
{ %% Static
- name :: mod_name(),
- app_name :: app_name(),
- incl_cond :: incl_cond() | undefined,
- debug_info :: debug_info() | undefined,
- is_app_mod :: boolean(),
- is_ebin_mod :: boolean(),
- uses_mods :: [mod_name()],
- exists :: boolean(),
+ name :: '$1' | mod_name(),
+ app_name :: '_' | app_name(),
+ incl_cond :: '_' | incl_cond() | undefined,
+ debug_info :: '_' | debug_info() | undefined,
+ is_app_mod :: '_' | boolean(),
+ is_ebin_mod :: '_' | boolean(),
+ uses_mods :: '$2' | [mod_name()],
+ exists :: '_' | boolean(),
%% Dynamic
- status :: status(),
- used_by_mods :: [mod_name()],
- is_pre_included :: boolean() | undefined,
- is_included :: boolean() | undefined
+ status :: '_' | status(),
+ used_by_mods :: '_' | [mod_name()],
+ is_pre_included :: '_' | boolean() | undefined,
+ is_included :: '_' | boolean() | undefined
}).
-record(app_info,
@@ -166,41 +167,42 @@
-record(regexp, {source, compiled}).
+%% Types '$1','$2' and '_' are needed for match specs to ets:select
-record(app,
{ %% Static info
- name :: app_name(),
- is_escript :: boolean() | {inlined, escript_app_name()},
- use_selected_vsn :: vsn | dir | undefined,
- active_dir :: dir(),
- sorted_dirs :: [dir()],
- vsn :: app_vsn(),
- label :: app_label(),
- info :: #app_info{} | undefined,
- mods :: [#mod{}],
+ name :: '_' | app_name(),
+ is_escript :: '_' | boolean() | {inlined, escript_app_name()},
+ use_selected_vsn :: '_' | vsn | dir | undefined,
+ active_dir :: '_' | dir(),
+ sorted_dirs :: '_' | [dir()],
+ vsn :: '_' | app_vsn(),
+ label :: '_' | app_label(),
+ info :: '_' | #app_info{} | undefined,
+ mods :: '_' | [#mod{}],
%% Static source cond
- mod_cond :: mod_cond() | undefined,
- incl_cond :: incl_cond() | undefined,
+ mod_cond :: '_' | mod_cond() | undefined,
+ incl_cond :: '_' | incl_cond() | undefined,
%% Static target cond
- debug_info :: debug_info() | undefined,
- app_file :: app_file() | undefined,
- app_type :: app_type() | undefined,
- incl_app_filters :: [#regexp{}],
- excl_app_filters :: [#regexp{}],
- incl_archive_filters :: [#regexp{}],
- excl_archive_filters :: [#regexp{}],
- archive_opts :: [archive_opt()],
+ debug_info :: '_' | debug_info() | undefined,
+ app_file :: '_' | app_file() | undefined,
+ app_type :: '_' | app_type() | undefined,
+ incl_app_filters :: '_' | [#regexp{}],
+ excl_app_filters :: '_' | [#regexp{}],
+ incl_archive_filters :: '_' | [#regexp{}],
+ excl_archive_filters :: '_' | [#regexp{}],
+ archive_opts :: '_' | [archive_opt()],
%% Dynamic
- status :: status(),
- uses_mods :: [mod_name()],
- used_by_mods :: [mod_name()],
- uses_apps :: [app_name()],
- used_by_apps :: [app_name()],
- is_pre_included :: boolean() | undefined,
- is_included :: boolean() | undefined,
- rels :: [rel_name()]
+ status :: '_' | status(),
+ uses_mods :: '_' | [mod_name()],
+ used_by_mods :: '_' | [mod_name()],
+ uses_apps :: '_' | [app_name()],
+ used_by_apps :: '_' | [app_name()],
+ is_pre_included :: '_' | '$2' | boolean() | undefined,
+ is_included :: '_' | '$1' | boolean() | undefined,
+ rels :: '_' | [rel_name()]
}).
-record(rel_app,
@@ -224,7 +226,7 @@
escripts :: [file()],
mod_cond :: mod_cond(),
incl_cond :: incl_cond(),
- apps :: [#app{}],
+ apps :: [#app{}] | undefined,
%% Target cond
boot_rel :: boot_rel(),
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index 6159b7c4bd..a8f23575f3 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -763,8 +763,8 @@ doc_arg_type3(#type{base=string}, in) -> "unicode:chardata()";
doc_arg_type3(#type{base=string}, out) -> "unicode:charlist()";
doc_arg_type3(#type{name="wxChar", single=S},in) when S =/= true -> "unicode:chardata()";
doc_arg_type3(#type{name="wxChar", single=S},out) when S =/= true -> "unicode:charlist()";
-doc_arg_type3(#type{name="wxArrayString"},in) -> "[unicode:chardata()]";
-doc_arg_type3(#type{name="wxArrayString"},out) -> "[unicode:charlist()]";
+doc_arg_type3(#type{name="wxArrayString"},in) -> "unicode:chardata()";
+doc_arg_type3(#type{name="wxArrayString"},out) -> "unicode:charlist()";
doc_arg_type3(#type{name="wxDateTime"}, _) -> "wx:wx_datetime()";
doc_arg_type3(#type{name="wxArtClient"}, _) -> "unicode:chardata()";
doc_arg_type3(#type{base=int}, _) -> "integer()";
diff --git a/lib/wx/src/gen/wxCheckListBox.erl b/lib/wx/src/gen/wxCheckListBox.erl
index 083a9e32f4..382345abfa 100644
--- a/lib/wx/src/gen/wxCheckListBox.erl
+++ b/lib/wx/src/gen/wxCheckListBox.erl
@@ -104,7 +104,7 @@ new(Parent,Id)
Parent::wxWindow:wxWindow(), Id::integer(),
Option :: {pos, {X::integer(), Y::integer()}}
| {size, {W::integer(), H::integer()}}
- | {choices, [[unicode:chardata()]]}
+ | {choices, [unicode:chardata()]}
| {style, integer()}
| {validator, wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
diff --git a/lib/wx/src/gen/wxChoice.erl b/lib/wx/src/gen/wxChoice.erl
index 2a2b2688fe..92b094036e 100644
--- a/lib/wx/src/gen/wxChoice.erl
+++ b/lib/wx/src/gen/wxChoice.erl
@@ -102,7 +102,7 @@ new(Parent,Id)
Parent::wxWindow:wxWindow(), Id::integer(),
Option :: {pos, {X::integer(), Y::integer()}}
| {size, {W::integer(), H::integer()}}
- | {choices, [[unicode:chardata()]]}
+ | {choices, [unicode:chardata()]}
| {style, integer()}
| {validator, wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
%% @equiv create(This,Parent,Id,Pos,Size,Choices, [])
-spec create(This, Parent, Id, Pos, Size, Choices) -> boolean() when
- This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]].
+ This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
@@ -128,7 +128,7 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxchoice.html#wxchoicecreate">external documentation</a>.
-spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when
- This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]],
+ This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()],
Option :: {style, integer()}
| {validator, wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options)
diff --git a/lib/wx/src/gen/wxComboBox.erl b/lib/wx/src/gen/wxComboBox.erl
index c67ff82bbc..4e6b247f67 100644
--- a/lib/wx/src/gen/wxComboBox.erl
+++ b/lib/wx/src/gen/wxComboBox.erl
@@ -105,7 +105,7 @@ new(Parent,Id)
Option :: {value, unicode:chardata()}
| {pos, {X::integer(), Y::integer()}}
| {size, {W::integer(), H::integer()}}
- | {choices, [[unicode:chardata()]]}
+ | {choices, [unicode:chardata()]}
| {style, integer()}
| {validator, wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
@@ -124,7 +124,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
%% @equiv create(This,Parent,Id,Value,Pos,Size,Choices, [])
-spec create(This, Parent, Id, Value, Pos, Size, Choices) -> boolean() when
- This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]].
+ This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
@@ -132,7 +132,7 @@ create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxcombobox.html#wxcomboboxcreate">external documentation</a>.
-spec create(This, Parent, Id, Value, Pos, Size, Choices, [Option]) -> boolean() when
- This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]],
+ This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()],
Option :: {style, integer()}
| {validator, wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,{PosX,PosY},{SizeW,SizeH},Choices, Options)
diff --git a/lib/wx/src/gen/wxControlWithItems.erl b/lib/wx/src/gen/wxControlWithItems.erl
index 1e2f89dbf0..92632a1c31 100644
--- a/lib/wx/src/gen/wxControlWithItems.erl
+++ b/lib/wx/src/gen/wxControlWithItems.erl
@@ -103,7 +103,7 @@ append(#wx_ref{type=ThisT,ref=ThisRef},Item,ClientData)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxcontrolwithitems.html#wxcontrolwithitemsappend">external documentation</a>.
-spec appendStrings(This, Strings) -> ok when
- This::wxControlWithItems(), Strings::[[unicode:chardata()]].
+ This::wxControlWithItems(), Strings::[unicode:chardata()].
appendStrings(#wx_ref{type=ThisT,ref=ThisRef},Strings)
when is_list(Strings) ->
?CLASS(ThisT,wxControlWithItems),
diff --git a/lib/wx/src/gen/wxFileDataObject.erl b/lib/wx/src/gen/wxFileDataObject.erl
index bcb30a5ece..435c795cdc 100644
--- a/lib/wx/src/gen/wxFileDataObject.erl
+++ b/lib/wx/src/gen/wxFileDataObject.erl
@@ -55,7 +55,7 @@ addFile(#wx_ref{type=ThisT,ref=ThisRef},Filename)
<<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledataobject.html#wxfiledataobjectgetfilenames">external documentation</a>.
--spec getFilenames(This) -> [[unicode:charlist()]] when
+-spec getFilenames(This) -> [unicode:charlist()] when
This::wxFileDataObject().
getFilenames(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxFileDataObject),
diff --git a/lib/wx/src/gen/wxFileDialog.erl b/lib/wx/src/gen/wxFileDialog.erl
index 3d65c92758..a257905795 100644
--- a/lib/wx/src/gen/wxFileDialog.erl
+++ b/lib/wx/src/gen/wxFileDialog.erl
@@ -138,7 +138,7 @@ getFilename(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledialog.html#wxfiledialoggetfilenames">external documentation</a>.
--spec getFilenames(This) -> [[unicode:charlist()]] when
+-spec getFilenames(This) -> [unicode:charlist()] when
This::wxFileDialog().
getFilenames(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxFileDialog),
@@ -170,7 +170,7 @@ getPath(#wx_ref{type=ThisT,ref=ThisRef}) ->
<<ThisRef:32/?UI>>).
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledialog.html#wxfiledialoggetpaths">external documentation</a>.
--spec getPaths(This) -> [[unicode:charlist()]] when
+-spec getPaths(This) -> [unicode:charlist()] when
This::wxFileDialog().
getPaths(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxFileDialog),
diff --git a/lib/wx/src/gen/wxGridCellChoiceEditor.erl b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
index 6f7d7ceffb..6b037e01b3 100644
--- a/lib/wx/src/gen/wxGridCellChoiceEditor.erl
+++ b/lib/wx/src/gen/wxGridCellChoiceEditor.erl
@@ -41,7 +41,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxGridCellChoiceEditor() :: wx:wx_object().
%% @equiv new(Choices, [])
-spec new(Choices) -> wxGridCellChoiceEditor() when
- Choices::[[unicode:chardata()]].
+ Choices::[unicode:chardata()].
new(Choices)
when is_list(Choices) ->
@@ -49,7 +49,7 @@ new(Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgridcellchoiceeditor.html#wxgridcellchoiceeditorwxgridcellchoiceeditor">external documentation</a>.
-spec new(Choices, [Option]) -> wxGridCellChoiceEditor() when
- Choices::[[unicode:chardata()]],
+ Choices::[unicode:chardata()],
Option :: {allowOthers, boolean()}.
new(Choices, Options)
when is_list(Choices),is_list(Options) ->
diff --git a/lib/wx/src/gen/wxListBox.erl b/lib/wx/src/gen/wxListBox.erl
index 3b41de9ffc..fa4cbd01d5 100644
--- a/lib/wx/src/gen/wxListBox.erl
+++ b/lib/wx/src/gen/wxListBox.erl
@@ -102,7 +102,7 @@ new(Parent,Id)
Parent::wxWindow:wxWindow(), Id::integer(),
Option :: {pos, {X::integer(), Y::integer()}}
| {size, {W::integer(), H::integer()}}
- | {choices, [[unicode:chardata()]]}
+ | {choices, [unicode:chardata()]}
| {style, integer()}
| {validator, wx:wx_object()}.
new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
@@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options)
%% @equiv create(This,Parent,Id,Pos,Size,Choices, [])
-spec create(This, Parent, Id, Pos, Size, Choices) -> boolean() when
- This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]].
+ This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
@@ -128,7 +128,7 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxcreate">external documentation</a>.
-spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when
- This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]],
+ This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()],
Option :: {style, integer()}
| {validator, wx:wx_object()}.
create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options)
@@ -164,7 +164,7 @@ getSelections(#wx_ref{type=ThisT,ref=ThisRef}) ->
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxinsertitems">external documentation</a>.
-spec insertItems(This, Items, Pos) -> ok when
- This::wxListBox(), Items::[[unicode:chardata()]], Pos::integer().
+ This::wxListBox(), Items::[unicode:chardata()], Pos::integer().
insertItems(#wx_ref{type=ThisT,ref=ThisRef},Items,Pos)
when is_list(Items),is_integer(Pos) ->
?CLASS(ThisT,wxListBox),
@@ -184,7 +184,7 @@ isSelected(#wx_ref{type=ThisT,ref=ThisRef},N)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxset">external documentation</a>.
-spec set(This, Items) -> ok when
- This::wxListBox(), Items::[[unicode:chardata()]].
+ This::wxListBox(), Items::[unicode:chardata()].
set(#wx_ref{type=ThisT,ref=ThisRef},Items)
when is_list(Items) ->
?CLASS(ThisT,wxListBox),
diff --git a/lib/wx/src/gen/wxMultiChoiceDialog.erl b/lib/wx/src/gen/wxMultiChoiceDialog.erl
index 04cbb832ea..6a6a6b833a 100644
--- a/lib/wx/src/gen/wxMultiChoiceDialog.erl
+++ b/lib/wx/src/gen/wxMultiChoiceDialog.erl
@@ -94,7 +94,7 @@ new() ->
%% @equiv new(Parent,Message,Caption,Choices, [])
-spec new(Parent, Message, Caption, Choices) -> wxMultiChoiceDialog() when
- Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]].
+ Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()].
new(Parent,Message,Caption,Choices)
when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) ->
@@ -102,7 +102,7 @@ new(Parent,Message,Caption,Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxmultichoicedialog.html#wxmultichoicedialogwxmultichoicedialog">external documentation</a>.
-spec new(Parent, Message, Caption, Choices, [Option]) -> wxMultiChoiceDialog() when
- Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]],
+ Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()],
Option :: {style, integer()}
| {pos, {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options)
diff --git a/lib/wx/src/gen/wxRadioBox.erl b/lib/wx/src/gen/wxRadioBox.erl
index 7843fde488..b7f52d7d9c 100644
--- a/lib/wx/src/gen/wxRadioBox.erl
+++ b/lib/wx/src/gen/wxRadioBox.erl
@@ -82,7 +82,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
-type wxRadioBox() :: wx:wx_object().
%% @equiv new(Parent,Id,Title,Pos,Size,Choices, [])
-spec new(Parent, Id, Title, Pos, Size, Choices) -> wxRadioBox() when
- Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]].
+ Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
when is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
@@ -90,7 +90,7 @@ new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxradiobox.html#wxradioboxwxradiobox">external documentation</a>.
-spec new(Parent, Id, Title, Pos, Size, Choices, [Option]) -> wxRadioBox() when
- Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]],
+ Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()],
Option :: {majorDim, integer()}
| {style, integer()}
| {val, wx:wx_object()}.
@@ -110,7 +110,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choic
%% @equiv create(This,Parent,Id,Title,Pos,Size,Choices, [])
-spec create(This, Parent, Id, Title, Pos, Size, Choices) -> boolean() when
- This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]].
+ This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()].
create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) ->
@@ -118,7 +118,7 @@ create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxradiobox.html#wxradioboxcreate">external documentation</a>.
-spec create(This, Parent, Id, Title, Pos, Size, Choices, [Option]) -> boolean() when
- This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]],
+ This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()],
Option :: {majorDim, integer()}
| {style, integer()}
| {val, wx:wx_object()}.
diff --git a/lib/wx/src/gen/wxSingleChoiceDialog.erl b/lib/wx/src/gen/wxSingleChoiceDialog.erl
index 6f96e283c8..db6b41ae75 100644
--- a/lib/wx/src/gen/wxSingleChoiceDialog.erl
+++ b/lib/wx/src/gen/wxSingleChoiceDialog.erl
@@ -94,7 +94,7 @@ new() ->
%% @equiv new(Parent,Message,Caption,Choices, [])
-spec new(Parent, Message, Caption, Choices) -> wxSingleChoiceDialog() when
- Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]].
+ Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()].
new(Parent,Message,Caption,Choices)
when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) ->
@@ -102,7 +102,7 @@ new(Parent,Message,Caption,Choices)
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsinglechoicedialog.html#wxsinglechoicedialogwxsinglechoicedialog">external documentation</a>.
-spec new(Parent, Message, Caption, Choices, [Option]) -> wxSingleChoiceDialog() when
- Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]],
+ Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()],
Option :: {style, integer()}
| {pos, {X::integer(), Y::integer()}}.
new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options)