Browse Source

Add rewritten code.

Tim Fletcher 16 years ago
parent
commit
4ab77bc915

+ 37 - 22
src/oauth.erl

@@ -1,31 +1,46 @@
 -module(oauth).
 
--export([get/2, get/3, get/4, post/2, post/3, post/4]).
+-export([get/5, post/5, token/1, token_secret/1, uri/2, header/1, signed_params/6]).
 
 
-get(URL, Consumer) ->
-  get(URL, Consumer, {[], []}, []).
+get(URL, ExtraParams, Consumer, Token, TokenSecret) ->
+  SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
+  oauth_http:get(uri(URL, SignedParams)).
 
-get(URL, Consumer, Params) when is_list(Params) ->
-  get(URL, Consumer, {[], []}, Params);
-get(URL, Consumer, TokenPair) ->
-  get(URL, Consumer, TokenPair, []).
+post(URL, ExtraParams, Consumer, Token, TokenSecret) ->
+  SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret),
+  oauth_http:post(URL, oauth_uri:params_to_string(SignedParams)).
 
-get(URL, Consumer, TokenPair, Params) ->
-  Request = oauth_request:new("GET", URL, Params),
-  RequestURL = oauth_request:to_url(Request, Consumer, TokenPair),
-  http:request(get, {RequestURL, []}, [{autoredirect, false}], []).
+token(Params) ->
+  proplists:get_value("oauth_token", Params).
 
-post(URL, Consumer) ->
-  post(URL, Consumer, {[], []}, []).
+token_secret(Params) ->
+  proplists:get_value("oauth_token_secret", Params).
 
-post(URL, Consumer, Params) when is_list(Params) ->
-  post(URL, Consumer, {[], []}, Params);
-post(URL, Consumer, TokenPair) ->
-  post(URL, Consumer, TokenPair, []).
+uri(Base, []) ->
+  Base;
+uri(Base, Params) ->
+  lists:concat([Base, "?", oauth_uri:params_to_string(Params)]).
 
-post(URL, Consumer, TokenPair, Params) ->
-  Request = oauth_request:new("POST", URL, Params),
-  Data = oauth_request:to_string(Request, Consumer, TokenPair),
-  MimeType = "application/x-www-form-urlencoded",
-  http:request(post, {URL, [], MimeType, Data}, [{autoredirect, false}], []).
+header(Params) ->
+  {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.
+
+signed_params(Method, URL, ExtraParams, Consumer, Token, TokenSecret) ->
+  Params = token_param(Token, params(Consumer, ExtraParams)),
+  [{"oauth_signature", oauth_signature:value(Method, URL, Params, Consumer, TokenSecret)}|Params].
+
+token_param("", Params) ->
+  Params;
+token_param(Token, Params) ->
+  [{"oauth_token", Token}|Params].
+
+params(_Consumer={Key, _, SigMethod}, Params) ->
+  Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
+  params(Key, SigMethod, oauth_unix:timestamp(), Nonce, Params).
+
+params(ConsumerKey, SigMethod, Timestamp, Nonce, Params) -> [
+  {"oauth_version", "1.0"},
+  {"oauth_nonce", Nonce},
+  {"oauth_timestamp", integer_to_list(Timestamp)},
+  {"oauth_signature_method", oauth_signature:method_to_string(SigMethod)},
+  {"oauth_consumer_key", ConsumerKey} | Params].

+ 0 - 19
src/oauth_base.erl

@@ -1,19 +0,0 @@
--module(oauth_base).
-
--export([string/3, normalize/1]).
-
-
-string(Method, URL, Params) when is_list(Method) ->
-  Unencoded = [Method, oauth_uri:normalize(URL), normalize(Params)],
-  string:join([fmt:percent_encode(Str) || Str <- Unencoded], "&").
-
-normalize(Params) ->
-  oauth_params:to_string(sort([to_string(KV) || KV <- Params])).
-
-sort(Params) ->
-  lists:sort(fun({K,X},{K,Y}) -> X < Y; ({A,_},{B,_}) -> A < B end, Params).
-
-to_string({K, V}) when is_atom(K) ->
-  {atom_to_list(K), V};
-to_string({K, V}) when is_list(K) ->
-  {K, V}.

+ 0 - 24
src/oauth_consumer.erl

@@ -1,24 +0,0 @@
--module(oauth_consumer).
-
--export([key/1, new/3, secret/1, signature_method/1, signature_method_string/1]).
-
-
-new(Key, Secret, SignatureMethod) ->
-  {oauth_consumer, Key, Secret, SignatureMethod}.
-
-key(Consumer) ->
-  element(2, Consumer).
-
-secret(Consumer) ->
-  element(3, Consumer).
-
-signature_method(Consumer) ->
-  element(4, Consumer).
-
-signature_method_string(Consumer) ->
-  method_string(signature_method(Consumer)).
-
-method_string({Method, _}) ->
-  Method;
-method_string(Method) ->
-  Method.

+ 0 - 21
src/oauth_crypto.erl

@@ -1,21 +0,0 @@
--module(oauth_crypto).
-
--export([plaintext_signature/2, hmac_signature/3, rsa_signature/2]).
-
-
-plaintext_signature(ConsumerSecret, TokenSecret) ->
-  Encoded = oauth_util:esprintf("%s&%s", [ConsumerSecret, TokenSecret]),
-  fmt:percent_encode(Encoded).
-
-hmac_signature(BaseString, ConsumerSecret, TokenSecret) ->
-  CS = fmt:percent_encode(ConsumerSecret),
-  TS = fmt:percent_encode(TokenSecret),
-  Key = fmt:sprintf("%s&%s", [CS, TS]),
-  base64:encode_to_string(crypto:sha_mac(Key, BaseString)).
-
-rsa_signature(BaseString, Path) when is_list(Path) ->
-  {ok, [Info]} = public_key:pem_to_der(Path),
-  {ok, PrivateKey} = public_key:decode_private_key(Info),
-  rsa_signature(list_to_binary(BaseString), PrivateKey);
-rsa_signature(BaseString, PrivateKey) ->
-  base64:encode_to_string(public_key:sign(BaseString, PrivateKey)).

+ 8 - 0
src/oauth_hmac_sha1.erl

@@ -0,0 +1,8 @@
+-module(oauth_hmac_sha1).
+
+-export([signature/3]).
+
+
+signature(BaseString, CS, TS) ->
+  Key = oauth_uri:calate("&", [CS, TS]),
+  base64:encode_to_string(crypto:sha_mac(Key, BaseString)).

+ 22 - 0
src/oauth_http.erl

@@ -0,0 +1,22 @@
+-module(oauth_http).
+
+-export([get/1, post/2, response_params/1, response_body/1, response_code/1]).
+
+
+get(URL) ->
+  request(get, {URL, []}).
+
+post(URL, Data) ->
+  request(post, {URL, [], "application/x-www-form-urlencoded", Data}).
+
+request(Method, Request) ->
+  http:request(Method, Request, [{autoredirect, false}], []).
+
+response_params(Response) ->
+  oauth_uri:params_from_string(response_body(Response)).
+
+response_body({{_, _, _}, _, Body}) ->
+  Body.
+
+response_code({{_, Code, _}, _, _}) ->
+  Code.

+ 0 - 27
src/oauth_params.erl

@@ -1,27 +0,0 @@
--module(oauth_params).
-
--export([from_string/1, to_string/1, to_header_string/1]).
-
-
-from_string(Data) ->
-  [percent_decode(break_at($=, P)) || P <- string:tokens(Data, "&")].
-
-to_string(Params) ->
-  to_string(Params, "%s=%s", "&").
-
-to_string(Params, Fmt, Sep) ->
-  string:join([oauth_util:esprintf(Fmt, Param) || Param <- Params], Sep).
-
-to_header_string(Params) ->
-  to_string(Params, "%s=\"%s\"", ",").
-
-percent_decode({K, V}) ->
-  {oauth_util:percent_decode(K), oauth_util:percent_decode(V)}.
-
-break_at(Sep, Chars) ->
-  case lists:splitwith(fun(C) -> C =/= Sep end, Chars) of
-    Result={_, []} ->
-      Result;
-    {Before, [Sep|After]} ->
-      {Before, After}
-  end.

+ 7 - 0
src/oauth_plaintext.erl

@@ -0,0 +1,7 @@
+-module(oauth_plaintext).
+
+-export([signature/2]).
+
+
+signature(CS, TS) ->
+  oauth_uri:encode(oauth_uri:calate("&", [CS, TS])).

+ 0 - 84
src/oauth_request.erl

@@ -1,84 +0,0 @@
--module(oauth_request).
-
--export([new/3, is_signed/1, sign/3]).
-
--export([to_header/2, to_header/4, to_string/3, to_string/1, to_url/3, to_url/1]).
-
-
-new(Method, URL, Params) ->
-  {Method, URL, Params}.
-
-is_signed(Request) ->
-  proplists:is_defined(oauth_signature, params(Request)).
-
-sign(Request, Consumer, {Token, TokenSecret}) ->
-  Params = oauth_params(Request, Consumer, Token),
-  Signature = signature(Params, Request, Consumer, TokenSecret),
-  setelement(3, Request, [{oauth_signature, Signature}|Params]).
-
-to_header(Realm, Request, Consumer, TokenPair) ->
-  to_header(Realm, sign(Request, Consumer, TokenPair)).
-
-to_header(Realm, SignedRequest) ->
-  HeaderString = oauth_params:to_header_string(params(SignedRequest)),
-  HeaderValue = fmt:sprintf("OAuth realm=\"%s\", %s", [Realm, HeaderString]),
-  {"Authorization", HeaderValue}.
-
-to_string(Request, Consumer, TokenPair) ->
-  to_string(sign(Request, Consumer, TokenPair)).
-
-to_string(SignedRequest) ->
-  oauth_params:to_string(params(SignedRequest)).
-
-to_url(Request, Consumer, TokenPair) ->
-  to_url(sign(Request, Consumer, TokenPair)).
-
-to_url(SignedRequest) ->
-  fmt:sprintf("%s?%s", [url(SignedRequest), to_string(SignedRequest)]).
-
-signature(Params, Request, Consumer, TokenSecret) ->
-  ConsumerSecret = oauth_consumer:secret(Consumer),
-  case oauth_consumer:signature_method(Consumer) of
-    "PLAINTEXT" ->
-      oauth_crypto:plaintext_signature(ConsumerSecret, TokenSecret);
-    "HMAC-SHA1" ->
-      BaseString = oauth_base:string(method(Request), url(Request), Params),
-      oauth_crypto:hmac_signature(BaseString, ConsumerSecret, TokenSecret);
-    {"RSA-SHA1", PrivateKey} ->
-      BaseString = oauth_base:string(method(Request), url(Request), Params),
-      oauth_crypto:rsa_signature(BaseString, PrivateKey)
-  end.
-
-oauth_params(Request, Consumer, Token) ->
-  set_consumer_key(params(Request), Consumer, Token).
-
-set_consumer_key(Params, Consumer, Token) ->
-  Param = {oauth_consumer_key, oauth_consumer:key(Consumer)},
-  set_signature_method([Param|Params], Consumer, Token).
-
-set_signature_method(Params, Consumer, Token) ->
-  Method = oauth_consumer:signature_method_string(Consumer),
-  set_token([{oauth_signature_method, Method}|Params], Token).
-
-set_token(Params, []) ->
-  set_timestamp(Params);
-set_token(Params, Token) ->
-  set_timestamp([{oauth_token, Token}|Params]).
-
-set_timestamp(Params) ->
-  set_nonce([{oauth_timestamp, oauth_util:unix_timestamp()}|Params]).
-
-set_nonce(Params) ->
-  set_version([{oauth_nonce, oauth_util:nonce()}|Params]).
-
-set_version(Params) ->
-  [{oauth_version, "1.0"}|Params].
-
-method(_Request={Method, _, _}) ->
-  Method.
-
-url(_Request={_, URL, _}) ->
-  URL.
-
-params(_Request={_, _, Params}) ->
-  Params.

+ 9 - 0
src/oauth_rsa_sha1.erl

@@ -0,0 +1,9 @@
+-module(oauth_rsa_sha1).
+
+-export([signature/2]).
+
+
+signature(BaseString, PrivateKeyPath) ->
+  {ok, [Info]} = public_key:pem_to_der(PrivateKeyPath),
+  {ok, PrivateKey} = public_key:decode_private_key(Info),
+  base64:encode_to_string(public_key:sign(list_to_binary(BaseString), PrivateKey)).

+ 26 - 0
src/oauth_signature.erl

@@ -0,0 +1,26 @@
+-module(oauth_signature).
+
+-export([value/5, base_string/3, method_to_string/1]).
+
+
+value(HttpMethod, URL, Params, Consumer, TokenSecret) ->
+  value(base_string(HttpMethod, URL, Params), Consumer, TokenSecret).
+
+value(_, {_, CS, plaintext}, TS) ->
+  oauth_plaintext:signature(CS, TS);
+value(BaseString, {_, CS, hmac_sha1}, TS) ->
+  oauth_hmac_sha1:signature(BaseString, CS, TS);
+value(BaseString, {_, _, {rsa_sha1, PrivateKey}}, _) ->
+  oauth_rsa_sha1:signature(BaseString, PrivateKey).
+
+base_string(HttpMethod, URL, Params) ->
+  NormalizedURL = oauth_uri:normalize(URL),
+  NormalizedParams = oauth_uri:params_to_string(lists:sort(Params)),
+  oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]).
+
+method_to_string(plaintext) ->
+  "PLAINTEXT";
+method_to_string(hmac_sha1) ->
+  "HMAC-SHA1";
+method_to_string({rsa_sha1, _}) ->
+  "RSA-SHA1".

+ 0 - 15
src/oauth_token_pair.erl

@@ -1,15 +0,0 @@
--module(oauth_token_pair).
-
--export([new/1, new/2]).
-
--import(proplists, [get_value/2]).
-
-
-new(Token, TokenSecret) ->
-  {Token, TokenSecret}.
-
-new(_HttpResponse={ok, {_,_,Data}}) ->
-  Params = oauth_params:from_string(Data),
-  {get_value("oauth_token", Params), get_value("oauth_token_secret", Params)};
-new(HttpResponse) ->
-  HttpResponse.

+ 16 - 0
src/oauth_unix.erl

@@ -0,0 +1,16 @@
+-module(oauth_unix).
+
+-export([timestamp/0]).
+
+
+timestamp() ->
+  timestamp(calendar:universal_time()).
+
+timestamp(DateTime) ->
+  seconds(DateTime) - epoch().
+
+epoch() ->
+  seconds({{1970,1,1},{00,00,00}}).
+
+seconds(DateTime) ->
+  calendar:datetime_to_gregorian_seconds(DateTime).

+ 70 - 18
src/oauth_uri.erl

@@ -1,27 +1,79 @@
 -module(oauth_uri).
 
--export([join/1, normalize/1]).
+-export([normalize/1, calate/2, encode/1]).
+-export([params_from_string/1, params_to_string/1, params_to_header_string/1]).
 
+-import(lists, [concat/1]).
 
-join({Scheme, UserInfo, Host, Port, Path, Query}) ->
-  join(Scheme, UserInfo, Host, Port, [Path, Query]).
+-define(is_uppercase_alpha(C), C >= $A, C =< $Z).
+-define(is_lowercase_alpha(C), C >= $a, C =< $z).
+-define(is_alpha(C), ?is_uppercase_alpha(C); ?is_lowercase_alpha(C)).
+-define(is_digit(C), C >= $0, C =< $9).
+-define(is_alphanumeric(C), ?is_alpha(C); ?is_digit(C)).
+-define(is_unreserved(C), ?is_alphanumeric(C); C =:= $-; C =:= $_; C =:= $.; C =:= $~).
+-define(is_hex(C), ?is_digit(C); C >= $A, C =< $F).
 
-join(http, UserInfo, Host, 80, URI) ->
-  join(http, UserInfo, [Host|URI]);
-join(https, UserInfo, Host, 443, URI) ->
-  join(https, UserInfo, [Host|URI]);
-join(Scheme, UserInfo, Host, Port, URI) ->
-  join(Scheme, UserInfo, [Host, ":", Port|URI]).
-
-join(Scheme, [], URI) ->
-  lists:concat([Scheme, "://"|URI]);
-join(Scheme, UserInfo, URI) ->
-  lists:concat([Scheme, "://", UserInfo, "@"|URI]).
 
 normalize(URI) ->
   case http_uri:parse(URI) of
-    {error, _Reason} ->
-      URI;
-    Parts ->
-      join(Parts)
+    {Scheme, UserInfo, Host, Port, Path, Query} ->
+      normalize(Scheme, UserInfo, Host, Port, [Path, Query]);
+    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_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)].
+
+decode(Chars) ->
+  decode(Chars, []).
+
+decode([], Decoded) ->
+  lists:reverse(Decoded);
+decode([$%,A,B|Etc], Decoded) when ?is_hex(A), ?is_hex(B) ->
+  decode(Etc, [erlang:list_to_integer([A,B], 16)|Decoded]);
+decode([C|Etc], Decoded) when ?is_unreserved(C) ->
+  decode(Etc, [C|Decoded]).
+
+encode(Chars) ->
+  encode(Chars, []).
+
+encode([], Encoded) ->
+  lists:flatten(lists:reverse(Encoded));
+encode([C|Etc], Encoded) when ?is_unreserved(C) ->
+  encode(Etc, [C|Encoded]);
+encode([C|Etc], Encoded) ->
+  Value = io_lib:format("%~2.1.0s", [erlang:integer_to_list(C, 16)]),
+  encode(Etc, [Value|Encoded]).

+ 0 - 39
src/oauth_util.erl

@@ -1,39 +0,0 @@
--module(oauth_util).
-
--compile(export_all).
-
--define(is_uppercase_alpha(C), C >= $A, C =< $Z).
--define(is_lowercase_alpha(C), C >= $a, C =< $z).
--define(is_alpha(C), ?is_uppercase_alpha(C); ?is_lowercase_alpha(C)).
--define(is_digit(C), C >= $0, C =< $9).
--define(is_alphanumeric(C), ?is_alpha(C); ?is_digit(C)).
--define(is_unreserved(C), ?is_alphanumeric(C); C =:= $-; C =:= $_; C =:= $.; C =:= $~).
--define(is_hex(C), ?is_digit(C); C >= $A, C =< $F).
-
-
-unix_timestamp() ->
-  unix_timestamp(calendar:universal_time()).
-
-unix_timestamp(DateTime) ->
-  calendar:datetime_to_gregorian_seconds(DateTime) - unix_epoch().
-
-unix_epoch() ->
-  calendar:datetime_to_gregorian_seconds({{1970,1,1},{00,00,00}}).
-
-nonce() ->
-  base64:encode_to_string(crypto:rand_bytes(32)). % cf. ruby-oauth
-
-percent_decode(Chars) when is_list(Chars) ->
-  percent_decode(Chars, []).
-
-percent_decode([], Decoded) ->
-  lists:reverse(Decoded);
-percent_decode([$%,A,B|Etc], Decoded) when ?is_hex(A), ?is_hex(B) ->
-  percent_decode(Etc, [erlang:list_to_integer([A,B], 16)|Decoded]);
-percent_decode([C|Etc], Decoded) when ?is_unreserved(C) ->
-  percent_decode(Etc, [C|Decoded]).
-
-esprintf(Fmt, Values) when is_tuple(Values) ->
-  esprintf(Fmt, tuple_to_list(Values));
-esprintf(Fmt, Values) when is_list(Values) ->
-  fmt:sprintf(Fmt, [fmt:percent_encode(Value) || Value <- Values]).