From b5c8b99272687e0237e78de27dddc5d29f9f2174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-S=C3=A9bastien=20P=C3=A9dron?= Date: Fri, 23 Jan 2009 10:10:33 +0000 Subject: [PATCH] Document every functions to clarify the types to give and returned. PR: EJABP-1 SVN Revision: 1852 --- ChangeLog | 6 ++ src/cyrsasl.erl | 132 ++++++++++++++++++++++++++++++-------- src/cyrsasl_anonymous.erl | 24 +++++++ src/cyrsasl_digest.erl | 69 ++++++++++++++++++++ src/cyrsasl_plain.erl | 30 +++++++++ 5 files changed, 236 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84da75bbf..2dd027adf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2009-01-23 Jean-Sébastien Pédron + + * src/cyrsasl.erl, src/cyrsasl_plain.erl, src/cyrsasl_anonymous.erl, + src/cyrsasl_digest.erl: Document every functions to clarify the types + to give and returned. + 2009-01-23 Badlop * src/odbc/mysql.sql: Fix complain about comment syntax diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl index e742cee17..1f99f63ee 100644 --- a/src/cyrsasl.erl +++ b/src/cyrsasl.erl @@ -34,18 +34,39 @@ server_start/3, server_step/2]). +%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain} +%% Mechanism = string() +%% Module = atom() +%% Require_Plain = bool(). +%% Registry entry of a supported SASL mechanism. + -record(sasl_mechanism, {mechanism, module, require_plain_password}). + +%% @type saslstate() = {sasl_state, Service, Myname, Realm, GetPassword, CheckPassword, Mech_Mod, Mech_State} +%% Service = string() +%% Myname = string() +%% Realm = string() +%% GetPassword = function() +%% CheckPassword = function() +%% Mech_Mod = atom() +%% Mech_State = term(). +%% State of this process. + -record(sasl_state, {service, myname, realm, get_password, check_password, mech_mod, mech_state}). -export([behaviour_info/1]). +%% @hidden + behaviour_info(callbacks) -> [{mech_new, 3}, {mech_step, 2}]; behaviour_info(_Other) -> undefined. +%% @spec () -> ok + start() -> ets:new(sasl_mechanism, [named_table, public, @@ -55,35 +76,46 @@ start() -> cyrsasl_anonymous:start([]), ok. +%% @spec (Mechanism, Module, Require_Plain) -> true +%% Mechanism = string() +%% Module = atom() +%% Require_Plain = bool() + register_mechanism(Mechanism, Module, RequirePlainPassword) -> ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism, module = Module, require_plain_password = RequirePlainPassword}). -%%% TODO: use callbacks -%%-include("ejabberd.hrl"). -%%-include("jlib.hrl"). -%%check_authzid(_State, Props) -> -%% AuthzId = xml:get_attr_s(authzid, Props), -%% case jlib:string_to_jid(AuthzId) of -%% error -> -%% {error, "invalid-authzid"}; -%% JID -> -%% LUser = jlib:nodeprep(xml:get_attr_s(username, Props)), -%% {U, S, R} = jlib:jid_tolower(JID), -%% case R of -%% "" -> -%% {error, "invalid-authzid"}; -%% _ -> -%% case {LUser, ?MYNAME} of -%% {U, S} -> -%% ok; -%% _ -> -%% {error, "invalid-authzid"} -%% end -%% end -%% end. +% TODO use callbacks +%-include("ejabberd.hrl"). +%-include("jlib.hrl"). +%check_authzid(_State, Props) -> +% AuthzId = xml:get_attr_s(authzid, Props), +% case jlib:string_to_jid(AuthzId) of +% error -> +% {error, "invalid-authzid"}; +% JID -> +% LUser = jlib:nodeprep(xml:get_attr_s(username, Props)), +% {U, S, R} = jlib:jid_tolower(JID), +% case R of +% "" -> +% {error, "invalid-authzid"}; +% _ -> +% case {LUser, ?MYNAME} of +% {U, S} -> +% ok; +% _ -> +% {error, "invalid-authzid"} +% end +% end +% end. + +%% @spec (State, Props) -> ok | {error, 'not-authorized'} +%% State = saslstate() +%% Props = [{Key, Value}] +%% Key = atom() +%% Value = string() check_credentials(_State, Props) -> case proplists:get_value(username, Props) of @@ -96,6 +128,10 @@ check_credentials(_State, Props) -> end end. +%% @spec (Host) -> [Mechanism] +%% Host = string() +%% Mechanism = string() + listmech(Host) -> RequirePlainPassword = ejabberd_auth:plain_password_required(Host), @@ -112,6 +148,14 @@ listmech(Host) -> ['$1']}]), filter_anonymous(Host, Mechs). +%% @spec (Service, ServerFQDN, UserRealm, SecFlags, GetPassword, CheckPassword) -> saslstate() +%% Service = string() +%% ServerFQDN = string() +%% UserRealm = string() +%% SecFlags = [term()] +%% GetPassword = function() +%% CheckPassword = function() + server_new(Service, ServerFQDN, UserRealm, _SecFlags, GetPassword, CheckPassword) -> #sasl_state{service = Service, @@ -120,6 +164,22 @@ server_new(Service, ServerFQDN, UserRealm, _SecFlags, get_password = GetPassword, check_password = CheckPassword}. +%% @spec (State, Mech, ClientIn) -> Ok | Continue | Error +%% State = saslstate() +%% Mech = string() +%% ClientIn = string() +%% Ok = {ok, Props} +%% Props = [Prop] +%% Prop = [{Key, Value}] +%% Key = atom() +%% Value = string() +%% Continue = {continue, ServerOut, New_State} +%% ServerOut = string() +%% New_State = saslstate() +%% Error = {error, Reason} | {error, Username, Reason} +%% Reason = term() +%% Username = string() + server_start(State, Mech, ClientIn) -> case lists:member(Mech, listmech(State#sasl_state.myname)) of true -> @@ -139,6 +199,21 @@ server_start(State, Mech, ClientIn) -> {error, 'invalid-mechanism'} end. +%% @spec (State, ClientIn) -> Ok | Continue | Error +%% State = saslstate() +%% ClientIn = string() +%% Ok = {ok, Props} +%% Props = [Prop] +%% Prop = [{Key, Value}] +%% Key = atom() +%% Value = string() +%% Continue = {continue, ServerOut, New_State} +%% ServerOut = string() +%% New_State = saslstate() +%% Error = {error, Reason} | {error, Username, Reason} +%% Reason = term() +%% Username = string() + server_step(State, ClientIn) -> Module = State#sasl_state.mech_mod, MechState = State#sasl_state.mech_state, @@ -159,8 +234,15 @@ server_step(State, ClientIn) -> {error, Error} end. -%% Remove the anonymous mechanism from the list if not enabled for the given -%% host +%% @spec (Host, Mechs) -> [Filtered_Mechs] +%% Host = string() +%% Mechs = [Mech] +%% Mech = string() +%% Filtered_Mechs = [Mech] +%% +%% @doc Remove the anonymous mechanism from the list if not enabled for +%% the given host. + filter_anonymous(Host, Mechs) -> case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of true -> Mechs; diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl index c8f913ce6..84dc24831 100644 --- a/src/cyrsasl_anonymous.erl +++ b/src/cyrsasl_anonymous.erl @@ -31,18 +31,42 @@ -behaviour(cyrsasl). +%% @type mechstate() = {state, Server} +%% Server = string(). + -record(state, {server}). +%% @spec (Opts) -> true +%% Opts = term() + start(_Opts) -> cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false), ok. +%% @spec () -> ok + stop() -> ok. +%% @spec (Host, GetPassword, CheckPassword) -> {ok, State} +%% Host = string() +%% GetPassword = function() +%% CheckPassword = function() +%% State = mechstate() + mech_new(Host, _GetPassword, _CheckPassword) -> {ok, #state{server = Host}}. +%% @spec (State, ClientIn) -> Ok | Error +%% State = mechstate() +%% ClientIn = string() +%% Ok = {ok, Props} +%% Props = [Prop] +%% Prop = {username, Username} | {auth_module, AuthModule} +%% Username = string() +%% AuthModule = ejabberd_auth_anonymous +%% Error = {error, 'not-authorized'} + mech_step(State, _ClientIn) -> %% We generate a random username: User = lists:concat([randoms:get_string() | tuple_to_list(now())]), diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index b585c6e6c..1f07e407f 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -18,21 +18,56 @@ -behaviour(cyrsasl). +%% @type mechstate() = {state, Step, Nonce, Username, AuthzId, GetPassword, AuthModule, Host} +%% Step = 1 | 3 | 5 +%% Nonce = string() +%% Username = string() +%% AuthzId = string() +%% GetPassword = function() +%% AuthModule = atom() +%% Host = string(). + -record(state, {step, nonce, username, authzid, get_password, auth_module, host}). +%% @spec (Opts) -> true +%% Opts = term() + start(_Opts) -> cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true). +%% @spec () -> ok + stop() -> ok. +%% @spec (Host, GetPassword, CheckPassword) -> {ok, State} +%% Host = string() +%% GetPassword = function() +%% CheckPassword = function() +%% State = mechstate() + mech_new(Host, GetPassword, _CheckPassword) -> {ok, #state{step = 1, nonce = randoms:get_string(), host = Host, get_password = GetPassword}}. +%% @spec (State, ClientIn) -> Ok | Continue | Error +%% State = mechstate() +%% ClientIn = string() +%% Ok = {ok, Props} +%% Props = [Prop] +%% Prop = {username, Username} | {authzid, AuthzId} | {auth_module, AuthModule} +%% Username = string() +%% AuthzId = string() +%% AuthModule = atom() +%% Continue = {continue, ServerOut, New_State} +%% ServerOut = string() +%% New_State = mechstate() +%% Error = {error, Reason} | {error, Reason, Username} +%% Reason = term() + mech_step(#state{step = 1, nonce = Nonce} = State, _) -> {continue, "nonce=\"" ++ Nonce ++ @@ -85,9 +120,16 @@ mech_step(A, B) -> ?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]), {error, 'bad-protocol'}. +%% @spec (S) -> [{Key, Value}] | bad +%% S = string() +%% Key = string() +%% Value = string() + parse(S) -> parse1(S, "", []). +%% @hidden + parse1([$= | Cs], S, Ts) -> parse2(Cs, lists:reverse(S), "", Ts); parse1([$, | Cs], [], Ts) -> @@ -101,6 +143,8 @@ parse1([], [], T) -> parse1([], _S, _T) -> bad. +%% @hidden + parse2([$\" | Cs], Key, Val, Ts) -> parse3(Cs, Key, Val, Ts); parse2([C | Cs], Key, Val, Ts) -> @@ -108,6 +152,8 @@ parse2([C | Cs], Key, Val, Ts) -> parse2([], _, _, _) -> bad. +%% @hidden + parse3([$\" | Cs], Key, Val, Ts) -> parse4(Cs, Key, Val, Ts); parse3([$\\, C | Cs], Key, Val, Ts) -> @@ -117,6 +163,8 @@ parse3([C | Cs], Key, Val, Ts) -> parse3([], _, _, _) -> bad. +%% @hidden + parse4([$, | Cs], Key, Val, Ts) -> parse1(Cs, "", [{Key, lists:reverse(Val)} | Ts]); parse4([$\s | Cs], Key, Val, Ts) -> @@ -127,6 +175,10 @@ parse4([], Key, Val, Ts) -> parse1([], "", [{Key, lists:reverse(Val)} | Ts]). +%% @spec (DigestURICase, JabberHost) -> bool() +%% DigestURICase = string() +%% JabberHost = string() +%% %% @doc Check if the digest-uri is valid. %% RFC-2831 allows to provide the IP address in Host, %% however ejabberd doesn't allow that. @@ -134,6 +186,7 @@ parse4([], Key, Val, Ts) -> %% is provided by several hosts (being one of them server3.example.org), %% then digest-uri can be like xmpp/server3.example.org/jabber.example.org %% In that case, ejabberd only checks the service name, not the host. + is_digesturi_valid(DigestURICase, JabberHost) -> DigestURI = exmpp_stringprep:to_lower(DigestURICase), case catch string:tokens(DigestURI, "/") of @@ -148,14 +201,20 @@ is_digesturi_valid(DigestURICase, JabberHost) -> +%% @hidden + digit_to_xchar(D) when (D >= 0) and (D < 10) -> D + 48; digit_to_xchar(D) -> D + 87. +%% @hidden + hex(S) -> hex(S, []). +%% @hidden + hex([], Res) -> lists:reverse(Res); hex([N | Ns], Res) -> @@ -163,6 +222,16 @@ hex([N | Ns], Res) -> digit_to_xchar(N div 16) | Res]). +%% @spec (KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) -> string() +%% KeyVals = [{Key, Value}] +%% Key = string() +%% Value = string() +%% User = string() +%% Passwd = string() +%% Nonce = string() +%% AuthzId = nil() | string() +%% A2Prefix = string() + response(KeyVals, User, Passwd, Nonce, AuthzId, A2Prefix) -> Realm = proplists:get_value("realm", KeyVals, ""), CNonce = proplists:get_value("cnonce", KeyVals, ""), diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl index b3ecff83c..ffcf4b104 100644 --- a/src/cyrsasl_plain.erl +++ b/src/cyrsasl_plain.erl @@ -31,18 +31,44 @@ -behaviour(cyrsasl). +%% @type mechstate() = {state, CheckPassword} +%% CheckPassword = function(). + -record(state, {check_password}). +%% @spec (Opts) -> true +%% Opts = term() + start(_Opts) -> cyrsasl:register_mechanism("PLAIN", ?MODULE, false), ok. +%% @spec () -> ok + stop() -> ok. +%% @spec (Host, GetPassword, CheckPassword) -> {ok, State} +%% Host = string() +%% GetPassword = function() +%% CheckPassword = function() +%% State = mechstate() + mech_new(_Host, _GetPassword, CheckPassword) -> {ok, #state{check_password = CheckPassword}}. +%% @spec (State, ClientIn) -> Ok | Error +%% State = mechstate() +%% ClientIn = string() +%% Ok = {ok, Props} +%% Props = [Prop] +%% Prop = {username, Username} | {authzid, AuthzId} | {auth_module, AuthModule} +%% Username = string() +%% AuthzId = string() +%% AuthModule = atom() +%% Error = {error, Reason} | {error, Reason, Username} +%% Reason = term() + mech_step(State, ClientIn) -> case parse(ClientIn) of [AuthzId, User, Password] -> @@ -58,9 +84,13 @@ mech_step(State, ClientIn) -> end. +%% @hidden + parse(S) -> parse1(S, "", []). +%% @hidden + parse1([0 | Cs], S, T) -> parse1(Cs, "", [lists:reverse(S) | T]); parse1([C | Cs], S, T) ->