diff options
author | Dan Gudmundsson <[email protected]> | 2012-03-20 14:25:52 +0100 |
---|---|---|
committer | Dan Gudmundsson <[email protected]> | 2012-03-21 10:02:56 +0100 |
commit | f562e0fc077e546c2b905a7469999fc8419a0aec (patch) | |
tree | 4dd9622e39d1ccd1026b688d301d890037f390f7 | |
parent | d8dbf15de4fa1a08b9a05e7d8e08fdb025fe1dc3 (diff) | |
download | otp-f562e0fc077e546c2b905a7469999fc8419a0aec.tar.gz otp-f562e0fc077e546c2b905a7469999fc8419a0aec.tar.bz2 otp-f562e0fc077e546c2b905a7469999fc8419a0aec.zip |
[eldap] Add infrastructure
-rw-r--r-- | lib/Makefile | 4 | ||||
-rw-r--r-- | lib/eldap/AUTHORS | 7 | ||||
-rw-r--r-- | lib/eldap/Makefile | 39 | ||||
-rw-r--r-- | lib/eldap/info | 2 | ||||
-rw-r--r-- | lib/eldap/src/Makefile | 114 | ||||
-rw-r--r-- | lib/eldap/src/eldap.appup.src | 6 | ||||
-rw-r--r-- | lib/eldap/src/eldap.erl | 9 | ||||
-rw-r--r-- | lib/eldap/test/Makefile | 83 | ||||
-rw-r--r-- | lib/eldap/test/eldap.spec | 1 | ||||
-rw-r--r-- | lib/eldap/test/eldap_basic_SUITE.erl | 224 | ||||
-rw-r--r-- | lib/eldap/test/ldap_server/slapd.conf | 14 | ||||
-rw-r--r-- | lib/eldap/vsn.mk | 1 | ||||
-rw-r--r-- | system/COPYRIGHT | 23 |
13 files changed, 523 insertions, 4 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/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/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/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..a3a818f09e --- /dev/null +++ b/lib/eldap/src/Makefile @@ -0,0 +1,114 @@ +# +# %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_app \ + eldap \ + eldap_fsm \ + eldap_sup + +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) $(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.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 index 7c9c02d681..d144aac872 100644 --- a/lib/eldap/src/eldap.erl +++ b/lib/eldap/src/eldap.erl @@ -352,9 +352,14 @@ parse_args([], _, Data) -> try_connect([Host|Hosts], Data) -> TcpOpts = [{packet, asn1}, {active,false}], - case do_connect(Host, Data, TcpOpts) of + try do_connect(Host, Data, TcpOpts) of {ok,Fd} -> {ok,Data#eldap{host = Host, fd = Fd}}; - _ -> try_connect(Hosts, Data) + 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"}. 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..deb0776f74 --- /dev/null +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -0,0 +1,224 @@ +%% +%% %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, {searchResDone, _}} = All("cn=Bar,"++BasePath), + + {error, strongerAuthRequired} + = 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/system/COPYRIGHT b/system/COPYRIGHT index 94e9795b16..c6b7d8b64f 100644 --- a/system/COPYRIGHT +++ b/system/COPYRIGHT @@ -245,3 +245,26 @@ terms specified in this license. %% POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- +[eldap] + +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. + +--------------------------------------------------------------------------- |