|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constant VERSION = "1.0"; |
|
|
constant CONSUMER_KEY_KEY = "oauth_consumer_key"; |
|
|
constant CALLBACK_KEY = "oauth_callback"; |
|
|
constant VERSION_KEY = "oauth_version"; |
|
|
constant SIGNATURE_METHOD_KEY = "oauth_signature_method"; |
|
|
constant SIGNATURE_KEY = "oauth_signature"; |
|
|
constant TIMESTAMP_KEY = "oauth_timestamp"; |
|
|
constant NONCE_KEY = "oauth_nonce"; |
|
|
constant TOKEN_KEY = "oauth_token"; |
|
|
constant TOKEN_SECRET_KEY = "oauth_token_secret"; |
|
|
constant UNRESERVED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV" |
"WXYZ0123456789-_.~"/1; |
#include "oauth.h" |
|
|
|
|
|
|
|
|
|
|
|
|
Request request(string|Standards.URI uri, Consumer consumer, Token token, |
void|Params params, void|string http_method) |
{ |
if (!consumer) |
ARG_ERROR("consumer", "Can not be NULL."); |
|
TRACE("### Token: %O\n", token); |
|
Params dparams = get_default_params(consumer, token); |
|
if (params) dparams += params; |
|
return Request(uri, http_method||"GET", dparams); |
} |
|
|
|
|
|
Params get_default_params(Consumer consumer, Token token) |
{ |
Params p = Params( |
Param(VERSION_KEY, VERSION), |
Param(NONCE_KEY, nonce()), |
Param(TIMESTAMP_KEY, time(1)), |
Param(CONSUMER_KEY_KEY, consumer->key) |
); |
|
if (token && token->key) |
p += Param(TOKEN_KEY, token->key); |
|
return p; |
} |
|
|
|
|
|
Params query_to_params(string|Standards.URI|mapping q) |
{ |
if (objectp(q)) |
q = (string)q; |
|
Params ret = Params(); |
|
if (!q || !sizeof(q)) |
return ret; |
|
if (mappingp(q)) { |
foreach(q; string n; string v) |
ret += Param(n, v); |
|
return ret; |
} |
|
int pos = 0, len = sizeof(q); |
if ((pos = search(q, "?")) > -1) |
q = ([string]q)[pos+1..]; |
|
foreach (q/"&", string p) { |
sscanf(p, "%s=%s", string n, string v); |
if (n && v) |
ret += Param(n, v); |
} |
|
return ret; |
} |
|
|
class Request |
{ |
|
protected Standards.URI uri; |
|
|
protected string base_string; |
|
|
protected string method; |
|
|
protected Params params; |
|
|
|
|
|
|
|
|
|
|
|
void create(string|Standards.URI _uri, string http_method, |
void|Params _params) |
{ |
uri = ASSURE_URI(_uri); |
method = upper_case(http_method); |
params = query_to_params(uri); |
|
if (_params) params += _params; |
|
if (!(< "GET", "POST" >)[method]) |
ARG_ERROR("http_method", "Must be one of \"GET\" or \"POST\"."); |
} |
|
|
|
|
|
|
|
|
object add_param(Param|string name, void|string value) |
{ |
if (objectp(name)) |
params += name; |
else |
params += Param(name, value); |
|
return this_object(); |
} |
|
|
|
|
void add_params(Params _params) |
{ |
params += _params; |
} |
|
|
|
|
Param get_param(string name) |
{ |
foreach (values(params), Param p) |
if (p[name]) |
return p; |
|
return 0; |
} |
|
|
Params get_params() |
{ |
return params; |
} |
|
|
|
|
|
|
|
void sign_request(int signature_type, Consumer consumer, Token token) |
{ |
object sig = .Signature.get_object(signature_type); |
params += Param(SIGNATURE_METHOD_KEY, sig->get_method()); |
params += Param(SIGNATURE_KEY, sig->build_signature(this, consumer, token)); |
} |
|
|
string get_signature_base() |
{ |
TRACE("+++ get_signature_base(%s, %s, %s)\n\n", |
method, (normalize_uri(uri)), (params->get_signature())); |
|
return ({ |
method, |
uri_encode(normalize_uri(uri)), |
uri_encode(params->get_signature()) |
}) * "&"; |
} |
|
|
|
|
Protocols.HTTP.Query submit(void|mapping extra_headers) |
{ |
mapping args = params->get_variables(); |
foreach (args; string k; string v) { |
if (!v) continue; |
if (String.width(v) == 8) |
catch(args[k] = utf8_to_string(v)); |
} |
|
if (!extra_headers) |
extra_headers = ([]); |
|
string realm = uri->scheme + "://" + uri->host; |
extra_headers["Authorization"] = "OAuth realm=\"" + realm + "\"," + |
params->get_auth_header(); |
extra_headers["Content-Type"] = "text/plain; charset=utf-8"; |
|
TRACE("submit(%O, %O, %O, %O)\n", method, uri, args, extra_headers); |
|
return Protocols.HTTP.do_method(method, uri, args, extra_headers); |
} |
|
|
|
|
|
mixed cast(string how) |
{ |
if (how != "string") { |
ARG_ERROR("how", "%O can not be casted to \"%s\", only to \"string\"\n", |
this, how); |
} |
|
return (method == "GET" ? normalize_uri(uri) + "?" : "")+(string)params; |
} |
|
|
string _sprintf(int t) |
{ |
return t == 'O' && sprintf("%O(%O, %O, %O)", object_program(this), |
(string)uri, base_string, params); |
} |
} |
|
|
class Consumer |
{ |
|
string key; |
|
|
string secret; |
|
|
string|Standards.URI callback; |
|
|
|
|
|
|
|
void create(string _key, string _secret, void|string|Standards.URI _callback) |
{ |
key = _key; |
secret = _secret; |
callback = ASSURE_URI(_callback); |
} |
|
string _sprintf(int t) |
{ |
return t == 'O' && sprintf("%O(%O, %O, %O)", object_program(this), |
key, secret, callback); |
} |
} |
|
|
class Token |
{ |
|
string key; |
|
|
string secret; |
|
|
|
|
|
void create(string _key, string _secret) |
{ |
key = _key; |
secret = _secret; |
} |
|
|
|
|
|
|
mixed cast(string how) |
{ |
switch (how) { |
case "string": |
return "oauth_token=" + key + "&" |
"oauth_token_secret=" + secret; |
} |
|
error("Can't cast %O() to %O\n", object_program(this), how); |
} |
|
|
string _sprintf(int t) |
{ |
return t == 'O' && sprintf("%O(%O, %O)", object_program(this), |
key, secret); |
} |
} |
|
|
class Param |
{ |
|
protected string name; |
|
|
protected string value; |
|
protected int(0..1) is_null = 1; |
|
|
|
|
|
void create(string _name, mixed _value) |
{ |
name = _name; |
value = (string)_value; |
|
if (_value) is_null = 0; |
} |
|
|
string get_name() { return name; } |
|
|
void set_name(string value) { name = value; } |
|
|
string get_value() { return value; } |
|
|
void set_value(mixed _value) |
{ |
value = (string)_value; |
is_null = !(!!_value); |
} |
|
|
string get_encoded_value() { return value && uri_encode(value); } |
|
|
string get_signature() |
{ |
return name && value && uri_encode(name) + "=" + uri_encode(value); |
} |
|
|
|
|
int(0..1) `==(mixed other) |
{ |
if (object_program(other) != Param) return 0; |
if (name == other->get_name()) |
return value == other->get_value(); |
|
return 0; |
} |
|
|
|
|
int(0..1) `>(mixed other) |
{ |
if (object_program(other) != Param) return 0; |
if (name == other->get_name()) |
return value > other->get_value(); |
|
return name > other->get_name(); |
} |
|
|
|
|
object `[](string key) |
{ |
if (key == name) |
return this; |
|
return 0; |
} |
|
string _sprintf(int t) |
{ |
return t == 'O' && sprintf("%O(%O, %O)", object_program(this), name, value); |
} |
} |
|
|
|
class Params |
{ |
|
private array(Param) params; |
|
|
|
|
|
void create(Param ... _params) |
{ |
params = _params||({}); |
} |
|
|
string get_auth_header() |
{ |
array a = ({}); |
foreach (params, Param p) { |
if (has_prefix(p->get_name(), "oauth_")) { |
if (p->get_value()) |
a += ({ p->get_name() + "=\"" + p->get_encoded_value() + "\"" }); |
} |
else { |
TRACE("**** Some shitty param: %O\n", p); |
} |
} |
|
return a*","; |
} |
|
|
mapping get_variables() |
{ |
mapping m = ([]); |
|
foreach (params, Param p) { |
if (!has_prefix(p->get_name(), "oauth_")) |
m[p->get_name()] = p->get_value(); |
} |
|
return m; |
} |
|
|
string get_query_string() |
{ |
array s = ({}); |
foreach (params, Param p) |
if (!has_prefix(p->get_name(), "oauth_")) |
s += ({ p->get_name() + "=" + uri_encode(p->get_value()) }); |
|
return s*"&"; |
} |
|
|
|
|
|
mapping get_encoded_variables() |
{ |
mapping m = ([]); |
|
foreach (params, Param p) |
if (!has_prefix(p->get_name(), "oauth_")) |
m[p->get_name()] = uri_encode(p->get_value()); |
|
return m; |
} |
|
|
string get_signature() |
{ |
return ((sort(params)->get_signature()) - ({ 0 })) * "&"; |
} |
|
|
|
|
mixed cast(string how) |
{ |
switch (how) |
{ |
case "mapping": |
mapping m = ([]); |
foreach (params, Param p) |
m[p->get_name()] = p->get_value(); |
|
return m; |
break; |
} |
} |
|
|
|
|
|
|
|
object add_mapping(mapping args) |
{ |
foreach (args; string k; string v) |
params += ({ Param(k, v) }); |
|
return this_object; |
} |
|
|
|
|
|
|
|
object `+(Param|Params p) |
{ |
params += object_program(p) == Params ? values(p) : ({ p }); |
return this_object(); |
} |
|
|
|
|
|
|
|
object `-(Param p) |
{ |
foreach (params, Param pm) { |
if (pm == p) { |
params -= ({ pm }); |
break; |
} |
} |
|
return this_object(); |
} |
|
|
|
|
|
|
|
|
|
|
mixed `[](string key) |
{ |
array(Param) p = params[key]-({0}); |
if (!p) return 0; |
return sizeof(p) == 1 ? p[0] : Params(@p); |
} |
|
|
mixed _values() |
{ |
sort(params); |
return params; |
} |
|
|
int _sizeof() |
{ |
return sizeof(params); |
} |
|
|
string _sprintf(int t) |
{ |
return t == 'O' && sprintf("%O(%O)", object_program(this), params); |
} |
} |
|
|
|
|
string uri_encode(string s) |
{ |
#if constant(Protocols.HTTP.uri_encode) |
return Protocols.HTTP.uri_encode(s); |
#else |
if (String.width(s) >= 8) |
catch (s = utf8_to_string(s)); |
|
String.Buffer b = String.Buffer(); |
function add = b->add; |
foreach (s/1, string c) { |
if (has_value(UNRESERVED_CHARS, c)) |
add(c); |
else { |
#if constant(String.string2hex) |
add("%" + upper_case(String.string2hex(c))); |
#else /* Pike 7.4 compat cludge */ |
add("%" + upper_case(Crypto.string_to_hex(c))); |
#endif |
} |
} |
|
return b->get(); |
#endif /* constant(Protocols.HTTP.uri_encode) */ |
} |
|
|
|
|
|
string normalize_uri(string|Standards.URI uri) |
{ |
uri = ASSURE_URI(uri); |
string nuri = sprintf("%s://%s", uri->scheme, uri->host); |
|
if (!(<"http","https">)[uri->scheme] || !(<80,443>)[uri->port]) |
nuri += ":" + uri->port; |
|
return nuri + uri->path; |
} |
|
|
string nonce() |
{ |
#if constant(Standards.UUID) |
return ((string)Standards.UUID.make_version4())-"-"; |
#else |
return (string)time(); |
#endif |
} |
|
|
|