You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/01/17 23:57:37 UTC
[33/50] git commit: Combine modules and refactor
Combine modules and refactor
Project: http://git-wip-us.apache.org/repos/asf/couchdb-oauth/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-oauth/commit/fc5f528c
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-oauth/tree/fc5f528c
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-oauth/diff/fc5f528c
Branch: refs/heads/import
Commit: fc5f528cea05b6b0b5e3ace0300268a330b01ac6
Parents: 9e43769
Author: Tim Fletcher <ma...@tfletcher.com>
Authored: Thu Jun 23 16:00:29 2011 +0100
Committer: Tim Fletcher <ma...@tfletcher.com>
Committed: Thu Jun 23 16:00:29 2011 +0100
----------------------------------------------------------------------
src/oauth.erl | 282 ++++++++++++++++++++++++++++++++++---------
src/oauth_hmac_sha1.erl | 10 --
src/oauth_http.erl | 27 -----
src/oauth_plaintext.erl | 9 --
src/oauth_rsa_sha1.erl | 30 -----
src/oauth_uri.erl | 96 ---------------
6 files changed, 226 insertions(+), 228 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth.erl
----------------------------------------------------------------------
diff --git a/src/oauth.erl b/src/oauth.erl
index f2fa946..fe66d4a 100644
--- a/src/oauth.erl
+++ b/src/oauth.erl
@@ -1,29 +1,41 @@
-module(oauth).
--export([get/5, get/6, header/1, post/5, post/6, signature/5, signature_base_string/3,
- signed_params/6, token/1, token_secret/1, uri/2, verify/6]).
+-export([get/5, get/6, post/5, post/6, uri/2, header/1, sign/6,
+ params_decode/1, token/1, token_secret/1, verify/6]).
+
+-export([plaintext_signature/2, hmac_sha1_signature/5,
+ hmac_sha1_signature/3, rsa_sha1_signature/4, rsa_sha1_signature/2,
+ signature_base_string/3, params_encode/1]).
+
+-export([plaintext_verify/3, hmac_sha1_verify/6, hmac_sha1_verify/4,
+ rsa_sha1_verify/5, rsa_sha1_verify/3]).
+
+-export([header_params_encode/1, header_params_decode/1,
+ uri_params_encode/1, uri_params_decode/1]).
+
+-include_lib("public_key/include/public_key.hrl").
get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
get(URL, ExtraParams, Consumer, Token, TokenSecret, []).
get(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
- SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
- oauth_http:get(uri(URL, SignedParams), HttpcOptions).
+ SignedParams = sign("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
+ http_get(uri(URL, SignedParams), HttpcOptions).
post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
post(URL, ExtraParams, Consumer, Token, TokenSecret, []).
post(URL, ExtraParams, Consumer, Token, TokenSecret, HttpcOptions) ->
- SignedParams = signed_params("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
- oauth_http:post(URL, oauth_uri:params_to_string(SignedParams), HttpcOptions).
+ SignedParams = sign("POST", URL, ExtraParams, Consumer, Token, TokenSecret),
+ http_post(URL, uri_params_encode(SignedParams), HttpcOptions).
uri(Base, []) ->
Base;
uri(Base, Params) ->
- lists:concat([Base, "?", oauth_uri:params_to_string(Params)]).
+ lists:concat([Base, "?", uri_params_encode(Params)]).
header(Params) ->
- {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.
+ {"Authorization", "OAuth " ++ header_params_encode(Params)}.
token(Params) ->
proplists:get_value("oauth_token", Params).
@@ -31,63 +43,123 @@ token(Params) ->
token_secret(Params) ->
proplists:get_value("oauth_token_secret", Params).
+consumer_key(_Consumer={Key, _, _}) ->
+ Key.
+
+consumer_secret(_Consumer={_, Secret, _}) ->
+ Secret.
+
+signature_method(_Consumer={_, _, Method}) ->
+ Method.
+
+sign(HttpMethod, URL, Params, Consumer, Token, TokenSecret) ->
+ SignatureParams = signature_params(Consumer, Params, Token),
+ Signature = signature(HttpMethod, URL, SignatureParams, Consumer, TokenSecret),
+ [{"oauth_signature", Signature} | SignatureParams].
+
+signature_params(Consumer, Params, "") ->
+ signature_params(Consumer, Params);
+signature_params(Consumer, Params, Token) ->
+ signature_params(Consumer, [{"oauth_token", Token} | Params]).
+
+signature_params(Consumer, Params) ->
+ Timestamp = unix_timestamp(),
+ Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
+ [ {"oauth_version", "1.0"}
+ , {"oauth_nonce", Nonce}
+ , {"oauth_timestamp", integer_to_list(Timestamp)}
+ , {"oauth_signature_method", signature_method_string(Consumer)}
+ , {"oauth_consumer_key", consumer_key(Consumer)}
+ | Params
+ ].
+
verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
case signature_method(Consumer) of
plaintext ->
- oauth_plaintext:verify(Signature, consumer_secret(Consumer), TokenSecret);
+ plaintext_verify(Signature, Consumer, TokenSecret);
hmac_sha1 ->
- BaseString = signature_base_string(HttpMethod, URL, Params),
- oauth_hmac_sha1:verify(Signature, BaseString, consumer_secret(Consumer), TokenSecret);
+ hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret);
rsa_sha1 ->
- BaseString = signature_base_string(HttpMethod, URL, Params),
- oauth_rsa_sha1:verify(Signature, BaseString, consumer_secret(Consumer))
+ rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer)
end.
-signed_params(HttpMethod, URL, ExtraParams, Consumer, Token, TokenSecret) ->
- Params = token_param(Token, params(Consumer, ExtraParams)),
- [{"oauth_signature", signature(HttpMethod, URL, Params, Consumer, TokenSecret)}|Params].
-
signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
case signature_method(Consumer) of
plaintext ->
- oauth_plaintext:signature(consumer_secret(Consumer), TokenSecret);
+ plaintext_signature(Consumer, TokenSecret);
hmac_sha1 ->
- BaseString = signature_base_string(HttpMethod, URL, Params),
- oauth_hmac_sha1:signature(BaseString, consumer_secret(Consumer), TokenSecret);
+ hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret);
+ rsa_sha1 ->
+ rsa_sha1_signature(HttpMethod, URL, Params, Consumer)
+ end.
+
+signature_method_string(Consumer) ->
+ case signature_method(Consumer) of
+ plaintext ->
+ "PLAINTEXT";
+ hmac_sha1 ->
+ "HMAC-SHA1";
rsa_sha1 ->
- BaseString = signature_base_string(HttpMethod, URL, Params),
- oauth_rsa_sha1:signature(BaseString, consumer_secret(Consumer))
+ "RSA-SHA1"
end.
+plaintext_signature(Consumer, TokenSecret) ->
+ uri_join([consumer_secret(Consumer), TokenSecret]).
+
+plaintext_verify(Signature, Consumer, TokenSecret) ->
+ Signature =:= plaintext_signature(Consumer, TokenSecret).
+
+hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret) ->
+ BaseString = signature_base_string(HttpMethod, URL, Params),
+ hmac_sha1_signature(BaseString, Consumer, TokenSecret).
+
+hmac_sha1_signature(BaseString, Consumer, TokenSecret) ->
+ Key = uri_join([consumer_secret(Consumer), TokenSecret]),
+ base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
+
+hmac_sha1_verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) ->
+ Signature =:= hmac_sha1_signature(HttpMethod, URL, Params, Consumer, TokenSecret).
+
+hmac_sha1_verify(Signature, BaseString, Consumer, TokenSecret) ->
+ Signature =:= hmac_sha1_signature(BaseString, Consumer, TokenSecret).
+
+rsa_sha1_signature(HttpMethod, URL, Params, Consumer) ->
+ BaseString = signature_base_string(HttpMethod, URL, Params),
+ rsa_sha1_signature(BaseString, Consumer).
+
+rsa_sha1_signature(BaseString, Consumer) ->
+ Key = read_private_key(consumer_secret(Consumer)),
+ base64:encode_to_string(public_key:sign(list_to_binary(BaseString), sha, Key)).
+
+rsa_sha1_verify(Signature, HttpMethod, URL, Params, Consumer) ->
+ BaseString = signature_base_string(HttpMethod, URL, Params),
+ rsa_sha1_verify(Signature, BaseString, Consumer).
+
+rsa_sha1_verify(Signature, BaseString, Consumer) ->
+ Key = read_cert_key(consumer_secret(Consumer)),
+ public_key:verify(to_binary(BaseString), sha, base64:decode(Signature), Key).
+
signature_base_string(HttpMethod, URL, Params) ->
- NormalizedURL = oauth_uri:normalize(URL),
- NormalizedParams = normalized_params_string(Params),
- oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]).
+ uri_join([HttpMethod, uri_normalize(URL), params_encode(Params)]).
-normalized_params_string(Params) ->
+params_encode(Params) ->
% cf. http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
- Encoded = [{oauth_uri:encode(K), oauth_uri:encode(V)} || {K, V} <- Params],
+ Encoded = [{uri_encode(K), uri_encode(V)} || {K, V} <- Params],
Sorted = lists:sort(Encoded),
Concatenated = [lists:concat([K, "=", V]) || {K, V} <- Sorted],
string:join(Concatenated, "&").
-token_param("", Params) ->
- Params;
-token_param(Token, Params) ->
- [{"oauth_token", Token}|Params].
+params_decode(_Response={{_, _, _}, _, Body}) ->
+ uri_params_decode(Body).
-params(Consumer, Params) ->
- Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
- params(Consumer, unix_timestamp(), Nonce, Params).
+http_get(URL, Options) ->
+ http_request(get, {URL, []}, Options).
-params(Consumer, Timestamp, Nonce, Params) ->
- [ {"oauth_version", "1.0"}
- , {"oauth_nonce", Nonce}
- , {"oauth_timestamp", integer_to_list(Timestamp)}
- , {"oauth_signature_method", signature_method_string(Consumer)}
- , {"oauth_consumer_key", consumer_key(Consumer)}
- | Params
- ].
+http_post(URL, Data, Options) ->
+ http_request(post, {URL, [], "application/x-www-form-urlencoded", Data}, Options).
+
+http_request(Method, Request, Options) ->
+ httpc:request(Method, Request, [{autoredirect, false}], Options).
unix_timestamp() ->
unix_timestamp(calendar:universal_time()).
@@ -101,21 +173,119 @@ unix_epoch() ->
unix_seconds(DateTime) ->
calendar:datetime_to_gregorian_seconds(DateTime).
-signature_method_string(Consumer) ->
- case signature_method(Consumer) of
- plaintext ->
- "PLAINTEXT";
- hmac_sha1 ->
- "HMAC-SHA1";
- rsa_sha1 ->
- "RSA-SHA1"
+read_cert_key(Path) when is_list(Path) ->
+ {ok, Contents} = file:read_file(Path),
+ [{'Certificate', DerCert, not_encrypted}] = public_key:pem_decode(Contents),
+ read_cert_key(public_key:pkix_decode_cert(DerCert, otp));
+read_cert_key(#'OTPCertificate'{tbsCertificate=Cert}) ->
+ read_cert_key(Cert);
+read_cert_key(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
+ read_cert_key(Info);
+read_cert_key(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
+ Key.
+
+read_private_key(Path) ->
+ {ok, Contents} = file:read_file(Path),
+ [Info] = public_key:pem_decode(Contents),
+ public_key:pem_entry_decode(Info).
+
+to_binary(Term) when is_list(Term) ->
+ list_to_binary(Term);
+to_binary(Term) when is_binary(Term) ->
+ Term.
+
+header_params_encode(Params) ->
+ intercalate(", ", [lists:concat([uri_encode(K), "=\"", uri_encode(V), "\""]) || {K, V} <- Params]).
+
+header_params_decode(String) ->
+ [header_param_decode(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
+
+header_param_decode(Param) ->
+ [Key, QuotedValue] = string:tokens(Param, "="),
+ Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
+ {uri_decode(Key), uri_decode(Value)}.
+
+uri_normalize(URI) ->
+ case http_uri:parse(URI) of
+ {Scheme, UserInfo, Host, Port, Path, _Query} ->
+ uri_normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
+ Else ->
+ Else
end.
-signature_method(_Consumer={_, _, Method}) ->
- Method.
+uri_normalize(http, UserInfo, Host, 80, Acc) ->
+ uri_normalize(http, UserInfo, [Host|Acc]);
+uri_normalize(https, UserInfo, Host, 443, Acc) ->
+ uri_normalize(https, UserInfo, [Host|Acc]);
+uri_normalize(Scheme, UserInfo, Host, Port, Acc) ->
+ uri_normalize(Scheme, UserInfo, [Host, ":", Port|Acc]).
-consumer_secret(_Consumer={_, Secret, _}) ->
- Secret.
+uri_normalize(Scheme, [], Acc) ->
+ lists:concat([Scheme, "://" | Acc]);
+uri_normalize(Scheme, UserInfo, Acc) ->
+ lists:concat([Scheme, "://", UserInfo, "@" | Acc]).
-consumer_key(_Consumer={Key, _, _}) ->
- Key.
+uri_params_encode(Params) ->
+ intercalate("&", [uri_join([K, V], "=") || {K, V} <- Params]).
+
+uri_params_decode(String) ->
+ [uri_param_decode(Substring) || Substring <- string:tokens(String, "&")].
+
+uri_param_decode(String) ->
+ [Key, Value] = string:tokens(String, "="),
+ {uri_decode(Key), uri_decode(Value)}.
+
+uri_join(Values) ->
+ uri_join(Values, "&").
+
+uri_join(Values, Separator) ->
+ string:join([uri_encode(Value) || Value <- Values], Separator).
+
+intercalate(Sep, Xs) ->
+ lists:concat(intersperse(Sep, Xs)).
+
+intersperse(_, []) ->
+ [];
+intersperse(_, [X]) ->
+ [X];
+intersperse(Sep, [X | Xs]) ->
+ [X, Sep | intersperse(Sep, Xs)].
+
+uri_encode(Term) when is_integer(Term) ->
+ integer_to_list(Term);
+uri_encode(Term) when is_atom(Term) ->
+ uri_encode(atom_to_list(Term));
+uri_encode(Term) when is_list(Term) ->
+ uri_encode(lists:reverse(Term, []), []).
+
+-define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
+
+uri_encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
+ uri_encode(T, [X | Acc]);
+uri_encode([X | T], Acc) ->
+ NewAcc = [$%, dec2hex(X bsr 4), dec2hex(X band 16#0f) | Acc],
+ uri_encode(T, NewAcc);
+uri_encode([], Acc) ->
+ Acc.
+
+uri_decode(Str) when is_list(Str) ->
+ uri_decode(Str, []).
+
+uri_decode([$%, A, B | T], Acc) ->
+ uri_decode(T, [(hex2dec(A) bsl 4) + hex2dec(B) | Acc]);
+uri_decode([X | T], Acc) ->
+ uri_decode(T, [X | Acc]);
+uri_decode([], Acc) ->
+ lists:reverse(Acc, []).
+
+-compile({inline, [{dec2hex, 1}, {hex2dec, 1}]}).
+
+dec2hex(N) when N >= 10 andalso N =< 15 ->
+ N + $A - 10;
+dec2hex(N) when N >= 0 andalso N =< 9 ->
+ N + $0.
+
+hex2dec(C) when C >= $A andalso C =< $F ->
+ C - $A + 10;
+hex2dec(C) when C >= $0 andalso C =< $9 ->
+ C - $0.
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth_hmac_sha1.erl
----------------------------------------------------------------------
diff --git a/src/oauth_hmac_sha1.erl b/src/oauth_hmac_sha1.erl
deleted file mode 100644
index 975278f..0000000
--- a/src/oauth_hmac_sha1.erl
+++ /dev/null
@@ -1,10 +0,0 @@
--module(oauth_hmac_sha1).
-
--export([signature/3, verify/4]).
-
-signature(BaseString, CS, TS) ->
- Key = oauth_uri:calate("&", [CS, TS]),
- base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
-
-verify(Signature, BaseString, CS, TS) ->
- Signature =:= signature(BaseString, CS, TS).
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth_http.erl
----------------------------------------------------------------------
diff --git a/src/oauth_http.erl b/src/oauth_http.erl
deleted file mode 100644
index 02128f7..0000000
--- a/src/oauth_http.erl
+++ /dev/null
@@ -1,27 +0,0 @@
--module(oauth_http).
-
--export([get/1, get/2, post/2, post/3, response_params/1, response_body/1, response_code/1]).
-
-get(URL) ->
- get(URL, []).
-
-get(URL, Options) ->
- request(get, {URL, []}, Options).
-
-post(URL, Data) ->
- post(URL, Data, []).
-
-post(URL, Data, Options) ->
- request(post, {URL, [], "application/x-www-form-urlencoded", Data}, Options).
-
-request(Method, Request, Options) ->
- httpc:request(Method, Request, [{autoredirect, false}], Options).
-
-response_params(Response) ->
- oauth_uri:params_from_string(response_body(Response)).
-
-response_body({{_, _, _}, _, Body}) ->
- Body.
-
-response_code({{_, Code, _}, _, _}) ->
- Code.
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth_plaintext.erl
----------------------------------------------------------------------
diff --git a/src/oauth_plaintext.erl b/src/oauth_plaintext.erl
deleted file mode 100644
index fbdc0a3..0000000
--- a/src/oauth_plaintext.erl
+++ /dev/null
@@ -1,9 +0,0 @@
--module(oauth_plaintext).
-
--export([signature/2, verify/3]).
-
-signature(CS, TS) ->
- oauth_uri:calate("&", [CS, TS]).
-
-verify(Signature, CS, TS) ->
- Signature =:= signature(CS, TS).
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth_rsa_sha1.erl
----------------------------------------------------------------------
diff --git a/src/oauth_rsa_sha1.erl b/src/oauth_rsa_sha1.erl
deleted file mode 100644
index 99bd926..0000000
--- a/src/oauth_rsa_sha1.erl
+++ /dev/null
@@ -1,30 +0,0 @@
--module(oauth_rsa_sha1).
-
--export([signature/2, verify/3]).
-
--include_lib("public_key/include/public_key.hrl").
-
-signature(BaseString, PrivateKeyPath) ->
- {ok, Contents} = file:read_file(PrivateKeyPath),
- [Info] = public_key:pem_decode(Contents),
- PrivateKey = public_key:pem_entry_decode(Info),
- base64:encode_to_string(public_key:sign(list_to_binary(BaseString), sha, PrivateKey)).
-
-verify(Signature, BaseString, Cert) ->
- public_key:verify(to_binary(BaseString), sha, base64:decode(Signature), pkey(Cert)).
-
-to_binary(Term) when is_list(Term) ->
- list_to_binary(Term);
-to_binary(Term) when is_binary(Term) ->
- Term.
-
-pkey(Path) when is_list(Path) ->
- {ok, Contents} = file:read_file(Path),
- [{'Certificate', DerCert, not_encrypted}] = public_key:pem_decode(Contents),
- pkey(public_key:pkix_decode_cert(DerCert, otp));
-pkey(#'OTPCertificate'{tbsCertificate=Cert}) ->
- pkey(Cert);
-pkey(#'OTPTBSCertificate'{subjectPublicKeyInfo=Info}) ->
- pkey(Info);
-pkey(#'OTPSubjectPublicKeyInfo'{subjectPublicKey=Key}) ->
- Key.
http://git-wip-us.apache.org/repos/asf/couchdb-oauth/blob/fc5f528c/src/oauth_uri.erl
----------------------------------------------------------------------
diff --git a/src/oauth_uri.erl b/src/oauth_uri.erl
deleted file mode 100644
index 5c33a1e..0000000
--- a/src/oauth_uri.erl
+++ /dev/null
@@ -1,96 +0,0 @@
--module(oauth_uri).
-
--export([calate/2, encode/1, normalize/1, params_from_string/1,
- params_from_header_string/1, params_to_string/1, params_to_header_string/1]).
-
--import(lists, [concat/1]).
-
-normalize(URI) ->
- case http_uri:parse(URI) of
- {Scheme, UserInfo, Host, Port, Path, _Query} ->
- normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]);
- Else ->
- Else
- end.
-
-normalize(http, UserInfo, Host, 80, Acc) ->
- normalize(http, UserInfo, [Host|Acc]);
-normalize(https, UserInfo, Host, 443, Acc) ->
- normalize(https, UserInfo, [Host|Acc]);
-normalize(Scheme, UserInfo, Host, Port, Acc) ->
- normalize(Scheme, UserInfo, [Host, ":", Port|Acc]).
-
-normalize(Scheme, [], Acc) ->
- concat([Scheme, "://"|Acc]);
-normalize(Scheme, UserInfo, Acc) ->
- concat([Scheme, "://", UserInfo, "@"|Acc]).
-
-params_to_header_string(Params) ->
- intercalate(", ", [concat([encode(K), "=\"", encode(V), "\""]) || {K, V} <- Params]).
-
-params_from_header_string(String) ->
- [param_from_header_string(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""].
-
-param_from_header_string(Param) ->
- [Key, QuotedValue] = string:tokens(Param, "="),
- Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2),
- {decode(Key), decode(Value)}.
-
-params_from_string(Params) ->
- [param_from_string(Param) || Param <- string:tokens(Params, "&")].
-
-param_from_string(Param) ->
- list_to_tuple([decode(Value) || Value <- string:tokens(Param, "=")]).
-
-params_to_string(Params) ->
- intercalate("&", [calate("=", [K, V]) || {K, V} <- Params]).
-
-calate(Sep, Xs) ->
- intercalate(Sep, [encode(X) || X <- Xs]).
-
-intercalate(Sep, Xs) ->
- concat(intersperse(Sep, Xs)).
-
-intersperse(_, []) -> [];
-intersperse(_, [X]) -> [X];
-intersperse(Sep, [X|Xs]) ->
- [X, Sep|intersperse(Sep, Xs)].
-
--define(is_alphanum(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9).
-
-encode(Term) when is_integer(Term) ->
- integer_to_list(Term);
-encode(Term) when is_atom(Term) ->
- encode(atom_to_list(Term));
-encode(Term) when is_list(Term) ->
- encode(lists:reverse(Term, []), []).
-
-encode([X | T], Acc) when ?is_alphanum(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ ->
- encode(T, [X | Acc]);
-encode([X | T], Acc) ->
- NewAcc = [$%, dec2hex(X bsr 4), dec2hex(X band 16#0f) | Acc],
- encode(T, NewAcc);
-encode([], Acc) ->
- Acc.
-
-decode(Str) when is_list(Str) ->
- decode(Str, []).
-
-decode([$%, A, B | T], Acc) ->
- decode(T, [(hex2dec(A) bsl 4) + hex2dec(B) | Acc]);
-decode([X | T], Acc) ->
- decode(T, [X | Acc]);
-decode([], Acc) ->
- lists:reverse(Acc, []).
-
--compile({inline, [{dec2hex, 1}, {hex2dec, 1}]}).
-
-dec2hex(N) when N >= 10 andalso N =< 15 ->
- N + $A - 10;
-dec2hex(N) when N >= 0 andalso N =< 9 ->
- N + $0.
-
-hex2dec(C) when C >= $A andalso C =< $F ->
- C - $A + 10;
-hex2dec(C) when C >= $0 andalso C =< $9 ->
- C - $0.