Ok, very basic, needs refactoring Getting of Request and Access tokens (POST stylee)
works.
This commit is contained in:
parent
d15500781e
commit
22402db273
|
@ -0,0 +1,9 @@
|
|||
|
||||
NODE = node
|
||||
|
||||
all: test
|
||||
|
||||
test:
|
||||
@$(NODE) spec/node.js all
|
||||
|
||||
.PHONY: test
|
756
lib/oauth.js
756
lib/oauth.js
|
@ -1,548 +1,232 @@
|
|||
/*
|
||||
* Copyright 2008 Netflix, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
var sha1= require('./sha1'),
|
||||
http= require('http');
|
||||
|
||||
/* Here's some JavaScript software for implementing OAuth.
|
||||
exports.OAuth= function(requestUrl, accessUrl, authorizeUrl, consumerKey, consumerSecret, version, signatureMethod) {
|
||||
this._requestUrl= requestUrl;
|
||||
this._accessUrl= accessUrl;
|
||||
this._authorizeUrl= authorizeUrl;
|
||||
this._consumerKey= consumerKey;
|
||||
this._consumerSecret= this._encodeData( consumerSecret );
|
||||
this._version= version;
|
||||
this._signatureMethod= signatureMethod;
|
||||
};
|
||||
|
||||
This isn't as useful as you might hope. OAuth is based around
|
||||
allowing tools and websites to talk to each other. However,
|
||||
JavaScript running in web browsers is hampered by security
|
||||
restrictions that prevent code running on one website from
|
||||
accessing data stored or served on another.
|
||||
|
||||
Before you start hacking, make sure you understand the limitations
|
||||
posed by cross-domain XMLHttpRequest.
|
||||
|
||||
On the bright side, some platforms use JavaScript as their
|
||||
language, but enable the programmer to access other web sites.
|
||||
Examples include Google Gadgets, and Microsoft Vista Sidebar.
|
||||
For those platforms, this library should come in handy.
|
||||
*/
|
||||
|
||||
// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by
|
||||
// http://pajhome.org.uk/crypt/md5/sha1.js
|
||||
|
||||
/* An OAuth message is represented as an object like this:
|
||||
{method: "GET", action: "http://server.com/path", parameters: ...}
|
||||
|
||||
The parameters may be either a map {name: value, name2: value2}
|
||||
or an Array of name-value pairs [[name, value], [name2, value2]].
|
||||
The latter representation is more powerful: it supports parameters
|
||||
in a specific sequence, or several parameters with the same name;
|
||||
for example [["a", 1], ["b", 2], ["a", 3]].
|
||||
|
||||
Parameter names and values are NOT percent-encoded in an object.
|
||||
They must be encoded before transmission and decoded after reception.
|
||||
For example, this message object:
|
||||
{method: "GET", action: "http://server/path", parameters: {p: "x y"}}
|
||||
... can be transmitted as an HTTP request that begins:
|
||||
GET /path?p=x%20y HTTP/1.0
|
||||
(This isn't a valid OAuth request, since it lacks a signature etc.)
|
||||
Note that the object "x y" is transmitted as x%20y. To encode
|
||||
parameters, you can call OAuth.addToURL, OAuth.formEncode or
|
||||
OAuth.getAuthorization.
|
||||
|
||||
This message object model harmonizes with the browser object model for
|
||||
input elements of an form, whose value property isn't percent encoded.
|
||||
The browser encodes each value before transmitting it. For example,
|
||||
see consumer.setInputs in example/consumer.js.
|
||||
*/
|
||||
|
||||
/* This script needs to know what time it is. By default, it uses the local
|
||||
clock (new Date), which is apt to be inaccurate in browsers. To do
|
||||
better, you can load this script from a URL whose query string contains
|
||||
an oauth_timestamp parameter, whose value is a current Unix timestamp.
|
||||
For example, when generating the enclosing document using PHP:
|
||||
|
||||
<script src="oauth.js?oauth_timestamp=<?=time()?>" ...
|
||||
|
||||
Another option is to call OAuth.correctTimestamp with a Unix timestamp.
|
||||
*/
|
||||
|
||||
var OAuth; if (OAuth == null) OAuth = {};
|
||||
|
||||
OAuth.setProperties = function setProperties(into, from) {
|
||||
if (into != null && from != null) {
|
||||
for (var key in from) {
|
||||
into[key] = from[key];
|
||||
}
|
||||
}
|
||||
return into;
|
||||
exports.OAuth.prototype._getTimestamp= function() {
|
||||
return Math.floor( (new Date()).getTime() / 1000 );
|
||||
}
|
||||
|
||||
OAuth.setProperties(OAuth, // utility functions
|
||||
{
|
||||
percentEncode: function percentEncode(s) {
|
||||
if (s == null) {
|
||||
return "";
|
||||
}
|
||||
if (s instanceof Array) {
|
||||
var e = "";
|
||||
for (var i = 0; i < s.length; ++s) {
|
||||
if (e != "") e += '&';
|
||||
e += OAuth.percentEncode(s[i]);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
s = encodeURIComponent(s);
|
||||
// Now replace the values which encodeURIComponent doesn't do
|
||||
// encodeURIComponent ignores: - _ . ! ~ * ' ( )
|
||||
// OAuth dictates the only ones you can ignore are: - _ . ~
|
||||
// Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent
|
||||
s = s.replace(/\!/g, "%21");
|
||||
s = s.replace(/\*/g, "%2A");
|
||||
s = s.replace(/\'/g, "%27");
|
||||
s = s.replace(/\(/g, "%28");
|
||||
s = s.replace(/\)/g, "%29");
|
||||
return s;
|
||||
}
|
||||
,
|
||||
decodePercent: function decodePercent(s) {
|
||||
if (s != null) {
|
||||
// Handle application/x-www-form-urlencoded, which is defined by
|
||||
// http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
|
||||
s = s.replace(/\+/g, " ");
|
||||
}
|
||||
return decodeURIComponent(s);
|
||||
}
|
||||
,
|
||||
/** Convert the given parameters to an Array of name-value pairs. */
|
||||
getParameterList: function getParameterList(parameters) {
|
||||
if (parameters == null) {
|
||||
return [];
|
||||
}
|
||||
if (typeof parameters != "object") {
|
||||
return OAuth.decodeForm(parameters + "");
|
||||
}
|
||||
if (parameters instanceof Array) {
|
||||
return parameters;
|
||||
}
|
||||
var list = [];
|
||||
for (var p in parameters) {
|
||||
list.push([p, parameters[p]]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
,
|
||||
/** Convert the given parameters to a map from name to value. */
|
||||
getParameterMap: function getParameterMap(parameters) {
|
||||
if (parameters == null) {
|
||||
return {};
|
||||
}
|
||||
if (typeof parameters != "object") {
|
||||
return OAuth.getParameterMap(OAuth.decodeForm(parameters + ""));
|
||||
}
|
||||
if (parameters instanceof Array) {
|
||||
var map = {};
|
||||
for (var p = 0; p < parameters.length; ++p) {
|
||||
var key = parameters[p][0];
|
||||
if (map[key] === undefined) { // first value wins
|
||||
map[key] = parameters[p][1];
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
,
|
||||
getParameter: function getParameter(parameters, name) {
|
||||
if (parameters instanceof Array) {
|
||||
for (var p = 0; p < parameters.length; ++p) {
|
||||
if (parameters[p][0] == name) {
|
||||
return parameters[p][1]; // first value wins
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return OAuth.getParameterMap(parameters)[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
,
|
||||
formEncode: function formEncode(parameters) {
|
||||
var form = "";
|
||||
var list = OAuth.getParameterList(parameters);
|
||||
for (var p = 0; p < list.length; ++p) {
|
||||
var value = list[p][1];
|
||||
if (value == null) value = "";
|
||||
if (form != "") form += '&';
|
||||
form += OAuth.percentEncode(list[p][0])
|
||||
+'='+ OAuth.percentEncode(value);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
,
|
||||
decodeForm: function decodeForm(form) {
|
||||
var list = [];
|
||||
var nvps = form.split('&');
|
||||
for (var n = 0; n < nvps.length; ++n) {
|
||||
var nvp = nvps[n];
|
||||
if (nvp == "") {
|
||||
continue;
|
||||
}
|
||||
var equals = nvp.indexOf('=');
|
||||
var name;
|
||||
var value;
|
||||
if (equals < 0) {
|
||||
name = OAuth.decodePercent(nvp);
|
||||
value = null;
|
||||
} else {
|
||||
name = OAuth.decodePercent(nvp.substring(0, equals));
|
||||
value = OAuth.decodePercent(nvp.substring(equals + 1));
|
||||
}
|
||||
list.push([name, value]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
,
|
||||
setParameter: function setParameter(message, name, value) {
|
||||
var parameters = message.parameters;
|
||||
if (parameters instanceof Array) {
|
||||
for (var p = 0; p < parameters.length; ++p) {
|
||||
if (parameters[p][0] == name) {
|
||||
if (value === undefined) {
|
||||
parameters.splice(p, 1);
|
||||
} else {
|
||||
parameters[p][1] = value;
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value !== undefined) {
|
||||
parameters.push([name, value]);
|
||||
}
|
||||
} else {
|
||||
parameters = OAuth.getParameterMap(parameters);
|
||||
parameters[name] = value;
|
||||
message.parameters = parameters;
|
||||
}
|
||||
}
|
||||
,
|
||||
setParameters: function setParameters(message, parameters) {
|
||||
var list = OAuth.getParameterList(parameters);
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
OAuth.setParameter(message, list[i][0], list[i][1]);
|
||||
}
|
||||
}
|
||||
,
|
||||
/** Fill in parameters to help construct a request message.
|
||||
This function doesn't fill in every parameter.
|
||||
The accessor object should be like:
|
||||
{consumerKey:'foo', consumerSecret:'bar', accessorSecret:'nurn', token:'krelm', tokenSecret:'blah'}
|
||||
The accessorSecret property is optional.
|
||||
*/
|
||||
completeRequest: function completeRequest(message, accessor) {
|
||||
if (message.method == null) {
|
||||
message.method = "GET";
|
||||
}
|
||||
var map = OAuth.getParameterMap(message.parameters);
|
||||
if (map.oauth_consumer_key == null) {
|
||||
OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || "");
|
||||
}
|
||||
if (map.oauth_token == null && accessor.token != null) {
|
||||
OAuth.setParameter(message, "oauth_token", accessor.token);
|
||||
}
|
||||
if (map.oauth_version == null) {
|
||||
OAuth.setParameter(message, "oauth_version", "1.0");
|
||||
}
|
||||
if (map.oauth_timestamp == null) {
|
||||
OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
|
||||
}
|
||||
if (map.oauth_nonce == null) {
|
||||
OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
|
||||
}
|
||||
OAuth.SignatureMethod.sign(message, accessor);
|
||||
}
|
||||
,
|
||||
setTimestampAndNonce: function setTimestampAndNonce(message) {
|
||||
OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
|
||||
OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
|
||||
}
|
||||
,
|
||||
addToURL: function addToURL(url, parameters) {
|
||||
newURL = url;
|
||||
if (parameters != null) {
|
||||
var toAdd = OAuth.formEncode(parameters);
|
||||
if (toAdd.length > 0) {
|
||||
var q = url.indexOf('?');
|
||||
if (q < 0) newURL += '?';
|
||||
else newURL += '&';
|
||||
newURL += toAdd;
|
||||
}
|
||||
}
|
||||
return newURL;
|
||||
}
|
||||
,
|
||||
/** Construct the value of the Authorization header for an HTTP request. */
|
||||
getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) {
|
||||
var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"';
|
||||
var list = OAuth.getParameterList(parameters);
|
||||
for (var p = 0; p < list.length; ++p) {
|
||||
var parameter = list[p];
|
||||
var name = parameter[0];
|
||||
if (name.indexOf("oauth_") == 0) {
|
||||
header += ',' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"';
|
||||
}
|
||||
}
|
||||
return header;
|
||||
}
|
||||
,
|
||||
/** Correct the time using a parameter from the URL from which the last script was loaded. */
|
||||
correctTimestampFromSrc: function correctTimestampFromSrc(parameterName) {
|
||||
parameterName = parameterName || "oauth_timestamp";
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
if (scripts == null || !scripts.length) return;
|
||||
var src = scripts[scripts.length-1].src;
|
||||
if (!src) return;
|
||||
var q = src.indexOf("?");
|
||||
if (q < 0) return;
|
||||
parameters = OAuth.getParameterMap(OAuth.decodeForm(src.substring(q+1)));
|
||||
var t = parameters[parameterName];
|
||||
if (t == null) return;
|
||||
OAuth.correctTimestamp(t);
|
||||
}
|
||||
,
|
||||
/** Generate timestamps starting with the given value. */
|
||||
correctTimestamp: function correctTimestamp(timestamp) {
|
||||
OAuth.timeCorrectionMsec = (timestamp * 1000) - (new Date()).getTime();
|
||||
}
|
||||
,
|
||||
/** The difference between the correct time and my clock. */
|
||||
timeCorrectionMsec: 0
|
||||
,
|
||||
timestamp: function timestamp() {
|
||||
var t = (new Date()).getTime() + OAuth.timeCorrectionMsec;
|
||||
return Math.floor(t / 1000);
|
||||
}
|
||||
,
|
||||
nonce: function nonce(length) {
|
||||
var chars = OAuth.nonce.CHARS;
|
||||
var result = "";
|
||||
for (var i = 0; i < length; ++i) {
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
result += chars.substring(rnum, rnum+1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
exports.OAuth.prototype._encodeData= function(toEncode){
|
||||
if( toEncode == null || toEncode == "" ) return ""
|
||||
else {
|
||||
var result= encodeURIComponent(toEncode);
|
||||
|
||||
OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||
|
||||
/** Define a constructor function,
|
||||
without causing trouble to anyone who was using it as a namespace.
|
||||
That is, if parent[name] already existed and had properties,
|
||||
copy those properties into the new constructor.
|
||||
*/
|
||||
OAuth.declareClass = function declareClass(parent, name, newConstructor) {
|
||||
var previous = parent[name];
|
||||
parent[name] = newConstructor;
|
||||
if (newConstructor != null && previous != null) {
|
||||
for (var key in previous) {
|
||||
if (key != "prototype") {
|
||||
newConstructor[key] = previous[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return newConstructor;
|
||||
// Fix the mismatch between OAuth's RFC2396's and Javascript's beliefs in what is right and wrong ;)
|
||||
return result.replace(/\!/g, "%21")
|
||||
.replace(/\'/g, "%27")
|
||||
.replace(/\(/g, "%28")
|
||||
.replace(/\)/g, "%29")
|
||||
.replace(/\*/g, "%2A");
|
||||
}
|
||||
}
|
||||
|
||||
/** An abstract algorithm for signing messages. */
|
||||
OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod(){});
|
||||
exports.OAuth.prototype._decodeData= function(toDecode) {
|
||||
if( toDecode != null ) {
|
||||
toDecode = toDecode.replace(/\+/g, " ");
|
||||
}
|
||||
return decodeURIComponent( toDecode);
|
||||
}
|
||||
|
||||
exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSecret) {
|
||||
var signatureBase= this._createSignatureBase(method, url, parameters);
|
||||
return this._createSignature( signatureBase, tokenSecret );
|
||||
}
|
||||
|
||||
exports.OAuth.prototype._splitQueryString= function(stringToSplit) {
|
||||
var result= {};
|
||||
var parameters= stringToSplit.split("&");
|
||||
for(var key in parameters) {
|
||||
var parameterPair= parameters[key].split("=");
|
||||
result[parameterPair[0]]= parameterPair[1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Takes a literal in, then returns a sorted array
|
||||
exports.OAuth.prototype._sortRequestParams= function(argumentsHash) {
|
||||
var argument_pairs= [];
|
||||
for(var key in argumentsHash ) {
|
||||
argument_pairs[argument_pairs.length]= [key, argumentsHash[key]];
|
||||
}
|
||||
// Sort by name, then value.
|
||||
argument_pairs.sort(function(a,b) {
|
||||
if ( a[0]== b[0] ) {
|
||||
return a[1] < b[1] ? -1 : 1;
|
||||
}
|
||||
else return a[0] < b[0] ? -1 : 1;
|
||||
});
|
||||
|
||||
return argument_pairs;
|
||||
}
|
||||
|
||||
exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
|
||||
var argument_pairs= this._sortRequestParams( arguments );
|
||||
var args= "";
|
||||
for(var i=0;i<argument_pairs.length;i++) {
|
||||
args+= argument_pairs[i][0];
|
||||
args+= "="
|
||||
args+= argument_pairs[i][1];
|
||||
if( i < argument_pairs.length-1 ) args+= "&";
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
|
||||
url= this._encodeData(url);
|
||||
parameters= this._encodeData(parameters);
|
||||
return method.toUpperCase() + "&" + url + "&" + parameters;
|
||||
}
|
||||
|
||||
exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
|
||||
if( tokenSecret === undefined ) var tokenSecret= "";
|
||||
else tokenSecret= this._encodeData( tokenSecret );
|
||||
|
||||
var key= this._consumerSecret + "&" + tokenSecret;
|
||||
|
||||
//TODO: whilst we support different signature methods being passed
|
||||
// we currenting only do SHA1-HMAC
|
||||
var hash= sha1.HMACSHA1(key, signatureBase);
|
||||
signature = this._encodeData(hash);
|
||||
|
||||
return signature;
|
||||
}
|
||||
exports.OAuth.prototype.NONCE_CHARS= ['a','b','c','d','e','f','g','h','i','j','k','l','m','n',
|
||||
'o','p','q','r','s','t','u','v','w','x','y','z','A','B',
|
||||
'C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3',
|
||||
'4','5','6','7','8','9'];
|
||||
|
||||
exports.OAuth.prototype._getNonce= function(nonceSize) {
|
||||
var result = [];
|
||||
var chars= this.NONCE_CHARS;
|
||||
var char_pos;
|
||||
var nonce_chars_length= chars.length;
|
||||
|
||||
for (var i = 0; i < nonceSize; i++) {
|
||||
char_pos= Math.floor(Math.random() * nonce_chars_length);
|
||||
result[i]= chars[char_pos];
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_secret, callback) {
|
||||
require('sys').puts('getOauthAccessToken')
|
||||
var oauthParameters= {
|
||||
"oauth_timestamp": this._getTimestamp(),
|
||||
"oauth_nonce": this._getNonce(32),
|
||||
"oauth_version": this._version,
|
||||
"oauth_signature_method": this._signatureMethod,
|
||||
"oauth_consumer_key": this._consumerKey,
|
||||
"oauth_token": oauth_token
|
||||
};
|
||||
|
||||
var method= "GET";
|
||||
var sig= this._getSignature( method, this._accessUrl, this._normaliseRequestParams(oauthParameters), oauth_token_secret);
|
||||
|
||||
var orderedParameters= this._sortRequestParams( oauthParameters );
|
||||
orderedParameters[orderedParameters.length]= ["oauth_signature", sig];
|
||||
|
||||
var query="";
|
||||
for( var i= 0 ; i < orderedParameters.length; i++) {
|
||||
query+= orderedParameters[i][0]+"="+ orderedParameters[i][1] + "&";
|
||||
}
|
||||
query= query.substring(0, query.length-1);
|
||||
var oauthProvider= http.createClient(80, 'twitter.com');
|
||||
var headers= {'Host': 'twitter.com'}
|
||||
var request = oauthProvider.request("GET", "/oauth/access_token"+"?"+query, headers);
|
||||
var data="";
|
||||
var self= this;
|
||||
request.addListener('response', function (response) {
|
||||
response.setEncoding('utf8');
|
||||
response.addListener('data', function (chunk) {
|
||||
data+=chunk;
|
||||
});
|
||||
response.addListener('end', function () {
|
||||
if( response.statusCode != 200 ) {
|
||||
callback( response.statusCode +" : " + data );
|
||||
} else {
|
||||
var results= self._splitQueryString(data);
|
||||
var oauth_token= results["oauth_token"];
|
||||
results["oauth_token"]= undefined;
|
||||
var oauth_token_secret= results["oauth_token_secret"];
|
||||
results["oauth_token_secret"]= undefined;
|
||||
callback(null, oauth_token, oauth_token_secret, results );
|
||||
}
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
}
|
||||
|
||||
exports.OAuth.prototype.getOAuthRequestToken= function(callback) {
|
||||
require('sys').puts('getOauthRequestToken')
|
||||
|
||||
var oauthParameters= {
|
||||
"oauth_timestamp": this._getTimestamp(),
|
||||
"oauth_nonce": this._getNonce(32),
|
||||
"oauth_version": this._version,
|
||||
"oauth_signature_method": this._signatureMethod,
|
||||
"oauth_consumer_key": this._consumerKey
|
||||
};
|
||||
var method= "POST";
|
||||
require('sys').puts(this._requestUrl)
|
||||
var sig= this._getSignature( method, this._requestUrl, this._normaliseRequestParams(oauthParameters));
|
||||
|
||||
|
||||
var orderedParameters= this._sortRequestParams( oauthParameters );
|
||||
orderedParameters[orderedParameters.length]= ["oauth_signature", sig];
|
||||
var headers= {};
|
||||
|
||||
// build request authorization header
|
||||
var authHeader="OAuth ";
|
||||
for( var i= 0 ; i < orderedParameters.length; i++) {
|
||||
authHeader+= orderedParameters[i][0]+"=\""+orderedParameters[i][1] +"\",";
|
||||
}
|
||||
authHeader= authHeader.substring(0, authHeader.length-1);
|
||||
|
||||
headers["Authorization"]= authHeader;
|
||||
headers["Host"] = "twitter.com"
|
||||
headers["Accept"]= "*/*"
|
||||
headers["Connection"]= "close"
|
||||
headers["User-Agent"]= "Express authentication"
|
||||
headers["Content-length"]= 0
|
||||
headers["Content-Type"]= "application/x-www-form-urlencoded"
|
||||
|
||||
var oauthProvider= http.createClient(80, 'twitter.com');
|
||||
var request = oauthProvider.request(method, "/oauth/request_token", headers);
|
||||
var data="";
|
||||
var self= this;
|
||||
request.addListener('response', function (response) {
|
||||
response.setEncoding('utf8');
|
||||
response.addListener('data', function (chunk) {
|
||||
data+=chunk;
|
||||
});
|
||||
response.addListener('end', function () {
|
||||
if( response.statusCode != 200 ) {
|
||||
callback( response.statusCode +" : " + data );
|
||||
} else {
|
||||
var results= self._splitQueryString(data);
|
||||
|
||||
var oauth_token= results["oauth_token"];
|
||||
var oauth_token_secret= results["oauth_token_secret"];
|
||||
delete results["oauth_token"];
|
||||
delete results["oauth_token_secret"];
|
||||
callback(null, oauth_token, oauth_token_secret, (self._authorizeUrl + oauth_token), results );
|
||||
}
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
}
|
||||
|
||||
OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members
|
||||
{
|
||||
/** Add a signature to the message. */
|
||||
sign: function sign(message) {
|
||||
var baseString = OAuth.SignatureMethod.getBaseString(message);
|
||||
var signature = this.getSignature(baseString);
|
||||
OAuth.setParameter(message, "oauth_signature", signature);
|
||||
return signature; // just in case someone's interested
|
||||
}
|
||||
,
|
||||
/** Set the key string for signing. */
|
||||
initialize: function initialize(name, accessor) {
|
||||
var consumerSecret;
|
||||
if (accessor.accessorSecret != null
|
||||
&& name.length > 9
|
||||
&& name.substring(name.length-9) == "-Accessor")
|
||||
{
|
||||
consumerSecret = accessor.accessorSecret;
|
||||
} else {
|
||||
consumerSecret = accessor.consumerSecret;
|
||||
}
|
||||
this.key = OAuth.percentEncode(consumerSecret)
|
||||
+"&"+ OAuth.percentEncode(accessor.tokenSecret);
|
||||
}
|
||||
});
|
||||
|
||||
/* SignatureMethod expects an accessor object to be like this:
|
||||
{tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."}
|
||||
The accessorSecret property is optional.
|
||||
*/
|
||||
// Class members:
|
||||
OAuth.setProperties(OAuth.SignatureMethod, // class members
|
||||
{
|
||||
sign: function sign(message, accessor) {
|
||||
var name = OAuth.getParameterMap(message.parameters).oauth_signature_method;
|
||||
if (name == null || name == "") {
|
||||
name = "HMAC-SHA1";
|
||||
OAuth.setParameter(message, "oauth_signature_method", name);
|
||||
}
|
||||
OAuth.SignatureMethod.newMethod(name, accessor).sign(message);
|
||||
}
|
||||
,
|
||||
/** Instantiate a SignatureMethod for the given method name. */
|
||||
newMethod: function newMethod(name, accessor) {
|
||||
var impl = OAuth.SignatureMethod.REGISTERED[name];
|
||||
if (impl != null) {
|
||||
var method = new impl();
|
||||
method.initialize(name, accessor);
|
||||
return method;
|
||||
}
|
||||
var err = new Error("signature_method_rejected");
|
||||
var acceptable = "";
|
||||
for (var r in OAuth.SignatureMethod.REGISTERED) {
|
||||
if (acceptable != "") acceptable += '&';
|
||||
acceptable += OAuth.percentEncode(r);
|
||||
}
|
||||
err.oauth_acceptable_signature_methods = acceptable;
|
||||
throw err;
|
||||
}
|
||||
,
|
||||
/** A map from signature method name to constructor. */
|
||||
REGISTERED : {}
|
||||
,
|
||||
/** Subsequently, the given constructor will be used for the named methods.
|
||||
The constructor will be called with no parameters.
|
||||
The resulting object should usually implement getSignature(baseString).
|
||||
You can easily define such a constructor by calling makeSubclass, below.
|
||||
*/
|
||||
registerMethodClass: function registerMethodClass(names, classConstructor) {
|
||||
for (var n = 0; n < names.length; ++n) {
|
||||
OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor;
|
||||
}
|
||||
}
|
||||
,
|
||||
/** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */
|
||||
makeSubclass: function makeSubclass(getSignatureFunction) {
|
||||
var superClass = OAuth.SignatureMethod;
|
||||
var subClass = function() {
|
||||
superClass.call(this);
|
||||
};
|
||||
subClass.prototype = new superClass();
|
||||
// Delete instance variables from prototype:
|
||||
// delete subclass.prototype... There aren't any.
|
||||
subClass.prototype.getSignature = getSignatureFunction;
|
||||
subClass.prototype.constructor = subClass;
|
||||
return subClass;
|
||||
}
|
||||
,
|
||||
getBaseString: function getBaseString(message) {
|
||||
var URL = message.action;
|
||||
var q = URL.indexOf('?');
|
||||
var parameters;
|
||||
if (q < 0) {
|
||||
parameters = message.parameters;
|
||||
} else {
|
||||
// Combine the URL query string with the other parameters:
|
||||
parameters = OAuth.decodeForm(URL.substring(q + 1));
|
||||
var toAdd = OAuth.getParameterList(message.parameters);
|
||||
for (var a = 0; a < toAdd.length; ++a) {
|
||||
parameters.push(toAdd[a]);
|
||||
}
|
||||
}
|
||||
return OAuth.percentEncode(message.method.toUpperCase())
|
||||
+'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL))
|
||||
+'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters));
|
||||
}
|
||||
,
|
||||
normalizeUrl: function normalizeUrl(url) {
|
||||
var uri = OAuth.SignatureMethod.parseUri(url);
|
||||
var scheme = uri.protocol.toLowerCase();
|
||||
var authority = uri.authority.toLowerCase();
|
||||
var dropPort = (scheme == "http" && uri.port == 80)
|
||||
|| (scheme == "https" && uri.port == 443);
|
||||
if (dropPort) {
|
||||
// find the last : in the authority
|
||||
var index = authority.lastIndexOf(":");
|
||||
if (index >= 0) {
|
||||
authority = authority.substring(0, index);
|
||||
}
|
||||
}
|
||||
var path = uri.path;
|
||||
if (!path) {
|
||||
path = "/"; // conforms to RFC 2616 section 3.2.2
|
||||
}
|
||||
// we know that there is no query and no fragment here.
|
||||
return scheme + "://" + authority + path;
|
||||
}
|
||||
,
|
||||
parseUri: function parseUri (str) {
|
||||
/* This function was adapted from parseUri 1.2.1
|
||||
http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
|
||||
*/
|
||||
var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
|
||||
parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }};
|
||||
var m = o.parser.strict.exec(str);
|
||||
var uri = {};
|
||||
var i = 14;
|
||||
while (i--) uri[o.key[i]] = m[i] || "";
|
||||
return uri;
|
||||
}
|
||||
,
|
||||
normalizeParameters: function normalizeParameters(parameters) {
|
||||
if (parameters == null) {
|
||||
return "";
|
||||
}
|
||||
var list = OAuth.getParameterList(parameters);
|
||||
var sortable = [];
|
||||
for (var p = 0; p < list.length; ++p) {
|
||||
var nvp = list[p];
|
||||
if (nvp[0] != "oauth_signature") {
|
||||
sortable.push([ OAuth.percentEncode(nvp[0])
|
||||
+ " " // because it comes before any character that can appear in a percentEncoded string.
|
||||
+ OAuth.percentEncode(nvp[1])
|
||||
, nvp]);
|
||||
}
|
||||
}
|
||||
sortable.sort(function(a,b) {
|
||||
if (a[0] < b[0]) return -1;
|
||||
if (a[0] > b[0]) return 1;
|
||||
return 0;
|
||||
});
|
||||
var sorted = [];
|
||||
for (var s = 0; s < sortable.length; ++s) {
|
||||
sorted.push(sortable[s][1]);
|
||||
}
|
||||
return OAuth.formEncode(sorted);
|
||||
}
|
||||
});
|
||||
|
||||
OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"],
|
||||
OAuth.SignatureMethod.makeSubclass(
|
||||
function getSignature(baseString) {
|
||||
return this.key;
|
||||
}
|
||||
));
|
||||
|
||||
OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"],
|
||||
OAuth.SignatureMethod.makeSubclass(
|
||||
function getSignature(baseString) {
|
||||
b64pad = '=';
|
||||
var signature = b64_hmac_sha1(this.key, baseString);
|
||||
return signature;
|
||||
}
|
||||
));
|
||||
|
||||
OAuth.correctTimestampFromSrc();
|
||||
|
|
324
lib/sha1.js
324
lib/sha1.js
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
|
||||
* in FIPS PUB 180-1
|
||||
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
|
||||
* in FIPS 180-1
|
||||
* Version 2.2 Copyright Paul Johnston 2000 - 2009.
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for details.
|
||||
|
@ -11,33 +11,241 @@
|
|||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
|
||||
var hexcase = 1; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
|
||||
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
|
||||
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
|
||||
function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
|
||||
function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
|
||||
function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
|
||||
function hex_hmac_sha1(k, d)
|
||||
{ return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
|
||||
function b64_hmac_sha1(k, d)
|
||||
{ return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
|
||||
function any_hmac_sha1(k, d, e)
|
||||
{ return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
|
||||
|
||||
/*
|
||||
* Perform a simple self-test to see if the VM is working
|
||||
*/
|
||||
function sha1_vm_test()
|
||||
{
|
||||
return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
|
||||
return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the SHA1 of a raw string
|
||||
*/
|
||||
function rstr_sha1(s)
|
||||
{
|
||||
return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-SHA1 of a key and some data (raw strings)
|
||||
*/
|
||||
function rstr_hmac_sha1(key, data)
|
||||
{
|
||||
var bkey = rstr2binb(key);
|
||||
if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for(var i = 0; i < 16; i++)
|
||||
{
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
|
||||
return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to a hex string
|
||||
*/
|
||||
function rstr2hex(input)
|
||||
{
|
||||
try { hexcase } catch(e) { hexcase=0; }
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var output = "";
|
||||
var x;
|
||||
for(var i = 0; i < input.length; i++)
|
||||
{
|
||||
x = input.charCodeAt(i);
|
||||
output += hex_tab.charAt((x >>> 4) & 0x0F)
|
||||
+ hex_tab.charAt( x & 0x0F);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to a base-64 string
|
||||
*/
|
||||
function rstr2b64(input)
|
||||
{
|
||||
try { b64pad } catch(e) { b64pad=''; }
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var output = "";
|
||||
var len = input.length;
|
||||
for(var i = 0; i < len; i += 3)
|
||||
{
|
||||
var triplet = (input.charCodeAt(i) << 16)
|
||||
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
|
||||
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
|
||||
for(var j = 0; j < 4; j++)
|
||||
{
|
||||
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
|
||||
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to an arbitrary string encoding
|
||||
*/
|
||||
function rstr2any(input, encoding)
|
||||
{
|
||||
var divisor = encoding.length;
|
||||
var remainders = Array();
|
||||
var i, q, x, quotient;
|
||||
|
||||
/* Convert to an array of 16-bit big-endian values, forming the dividend */
|
||||
var dividend = Array(Math.ceil(input.length / 2));
|
||||
for(i = 0; i < dividend.length; i++)
|
||||
{
|
||||
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeatedly perform a long division. The binary array forms the dividend,
|
||||
* the length of the encoding is the divisor. Once computed, the quotient
|
||||
* forms the dividend for the next step. We stop when the dividend is zero.
|
||||
* All remainders are stored for later use.
|
||||
*/
|
||||
while(dividend.length > 0)
|
||||
{
|
||||
quotient = Array();
|
||||
x = 0;
|
||||
for(i = 0; i < dividend.length; i++)
|
||||
{
|
||||
x = (x << 16) + dividend[i];
|
||||
q = Math.floor(x / divisor);
|
||||
x -= q * divisor;
|
||||
if(quotient.length > 0 || q > 0)
|
||||
quotient[quotient.length] = q;
|
||||
}
|
||||
remainders[remainders.length] = x;
|
||||
dividend = quotient;
|
||||
}
|
||||
|
||||
/* Convert the remainders to the output string */
|
||||
var output = "";
|
||||
for(i = remainders.length - 1; i >= 0; i--)
|
||||
output += encoding.charAt(remainders[i]);
|
||||
|
||||
/* Append leading zero equivalents */
|
||||
var full_length = Math.ceil(input.length * 8 /
|
||||
(Math.log(encoding.length) / Math.log(2)))
|
||||
for(i = output.length; i < full_length; i++)
|
||||
output = encoding[0] + output;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a string as utf-8.
|
||||
* For efficiency, this assumes the input is valid utf-16.
|
||||
*/
|
||||
function str2rstr_utf8(input)
|
||||
{
|
||||
var output = "";
|
||||
var i = -1;
|
||||
var x, y;
|
||||
|
||||
while(++i < input.length)
|
||||
{
|
||||
/* Decode utf-16 surrogate pairs */
|
||||
x = input.charCodeAt(i);
|
||||
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
|
||||
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
|
||||
{
|
||||
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Encode output as utf-8 */
|
||||
if(x <= 0x7F)
|
||||
output += String.fromCharCode(x);
|
||||
else if(x <= 0x7FF)
|
||||
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
|
||||
0x80 | ( x & 0x3F));
|
||||
else if(x <= 0xFFFF)
|
||||
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F));
|
||||
else if(x <= 0x1FFFFF)
|
||||
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
|
||||
0x80 | ((x >>> 12) & 0x3F),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a string as utf-16
|
||||
*/
|
||||
function str2rstr_utf16le(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length; i++)
|
||||
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
|
||||
(input.charCodeAt(i) >>> 8) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
function str2rstr_utf16be(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length; i++)
|
||||
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
|
||||
input.charCodeAt(i) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to an array of big-endian words
|
||||
* Characters >255 have their high-byte silently ignored.
|
||||
*/
|
||||
function rstr2binb(input)
|
||||
{
|
||||
var output = Array(input.length >> 2);
|
||||
for(var i = 0; i < output.length; i++)
|
||||
output[i] = 0;
|
||||
for(var i = 0; i < input.length * 8; i += 8)
|
||||
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a string
|
||||
*/
|
||||
function binb2rstr(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length * 32; i += 8)
|
||||
output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the SHA-1 of an array of big-endian words, and a bit length
|
||||
*/
|
||||
function core_sha1(x, len)
|
||||
function binb_sha1(x, len)
|
||||
{
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << (24 - len % 32);
|
||||
|
@ -61,12 +269,12 @@ function core_sha1(x, len)
|
|||
for(var j = 0; j < 80; j++)
|
||||
{
|
||||
if(j < 16) w[j] = x[i + j];
|
||||
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
|
||||
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
|
||||
else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
|
||||
var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
|
||||
safe_add(safe_add(e, w[j]), sha1_kt(j)));
|
||||
e = d;
|
||||
d = c;
|
||||
c = rol(b, 30);
|
||||
c = bit_rol(b, 30);
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
@ -102,25 +310,6 @@ function sha1_kt(t)
|
|||
(t < 60) ? -1894007588 : -899497514;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-SHA1 of a key and some data
|
||||
*/
|
||||
function core_hmac_sha1(key, data)
|
||||
{
|
||||
var bkey = str2binb(key);
|
||||
if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for(var i = 0; i < 16; i++)
|
||||
{
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
|
||||
return core_sha1(opad.concat(hash), 512 + 160);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
|
@ -135,68 +324,11 @@ function safe_add(x, y)
|
|||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function rol(num, cnt)
|
||||
function bit_rol(num, cnt)
|
||||
{
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an 8-bit or 16-bit string to an array of big-endian words
|
||||
* In 8-bit function, characters >255 have their hi-byte silently ignored.
|
||||
*/
|
||||
function str2binb(str)
|
||||
{
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a string
|
||||
*/
|
||||
function binb2str(bin)
|
||||
{
|
||||
var str = "";
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < bin.length * 32; i += chrsz)
|
||||
str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a hex string.
|
||||
*/
|
||||
function binb2hex(binarray)
|
||||
{
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i++)
|
||||
{
|
||||
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a base-64 string
|
||||
*/
|
||||
function binb2b64(binarray)
|
||||
{
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i += 3)
|
||||
{
|
||||
var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
|
||||
| (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
|
||||
| ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
|
||||
for(var j = 0; j < 4; j++)
|
||||
{
|
||||
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
|
||||
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
exports.HMACSHA1= function(key, data) {
|
||||
return b64_hmac_sha1(key, data);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
name: oauth
|
||||
description: An implementation of an OAuth client.
|
||||
version: 0.0.1
|
||||
dependencies:
|
Binary file not shown.
After Width: | Height: | Size: 154 B |
Binary file not shown.
After Width: | Height: | Size: 321 B |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 145 B |
|
@ -0,0 +1,149 @@
|
|||
body.jspec {
|
||||
margin: 45px 0;
|
||||
font: 12px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
|
||||
background: #efefef url(images/bg.png) top left repeat-x;
|
||||
text-align: center;
|
||||
}
|
||||
#jspec {
|
||||
margin: 0 auto;
|
||||
padding-top: 30px;
|
||||
width: 1008px;
|
||||
background: url(images/vr.png) top left repeat-y;
|
||||
text-align: left;
|
||||
}
|
||||
#jspec-top {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: 1008px;
|
||||
height: 40px;
|
||||
background: url(images/sprites.bg.png) top left no-repeat;
|
||||
}
|
||||
#jspec-bottom {
|
||||
margin: 0 auto;
|
||||
width: 1008px;
|
||||
height: 15px;
|
||||
background: url(images/sprites.bg.png) bottom left no-repeat;
|
||||
}
|
||||
#jspec .loading {
|
||||
margin-top: -45px;
|
||||
width: 1008px;
|
||||
height: 80px;
|
||||
background: url(images/loading.gif) 50% 50% no-repeat;
|
||||
}
|
||||
#jspec-title {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 20px;
|
||||
width: 160px;
|
||||
font-size: 22px;
|
||||
font-weight: normal;
|
||||
background: url(images/sprites.png) 0 -126px no-repeat;
|
||||
text-align: center;
|
||||
}
|
||||
#jspec-title em {
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
color: #BCC8D1;
|
||||
}
|
||||
#jspec-report * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
#jspec-report {
|
||||
padding: 15px 40px;
|
||||
font: 11px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
|
||||
color: #7B8D9B;
|
||||
}
|
||||
#jspec-report.has-failures {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
#jspec-report .hidden {
|
||||
display: none;
|
||||
}
|
||||
#jspec-report .heading {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
#jspec-report .heading span {
|
||||
padding-right: 10px;
|
||||
}
|
||||
#jspec-report .heading .passes em {
|
||||
color: #0ea0eb;
|
||||
}
|
||||
#jspec-report .heading .failures em {
|
||||
color: #FA1616;
|
||||
}
|
||||
#jspec-report table {
|
||||
font-size: 11px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#jspec-report td {
|
||||
padding: 8px;
|
||||
text-indent: 30px;
|
||||
color: #7B8D9B;
|
||||
}
|
||||
#jspec-report tr.body {
|
||||
display: none;
|
||||
}
|
||||
#jspec-report tr.body pre {
|
||||
margin: 0;
|
||||
padding: 0 0 5px 25px;
|
||||
}
|
||||
#jspec-report tr.even:hover + tr.body,
|
||||
#jspec-report tr.odd:hover + tr.body {
|
||||
display: block;
|
||||
}
|
||||
#jspec-report tr td:first-child em {
|
||||
display: block;
|
||||
clear: both;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
color: #7B8D9B;
|
||||
}
|
||||
#jspec-report tr.even:hover,
|
||||
#jspec-report tr.odd:hover {
|
||||
text-shadow: 1px 1px 1px #fff;
|
||||
background: #F2F5F7;
|
||||
}
|
||||
#jspec-report td + td {
|
||||
padding-right: 0;
|
||||
width: 15px;
|
||||
}
|
||||
#jspec-report td.pass {
|
||||
background: url(images/sprites.png) 3px -7px no-repeat;
|
||||
}
|
||||
#jspec-report td.fail {
|
||||
background: url(images/sprites.png) 3px -158px no-repeat;
|
||||
font-weight: bold;
|
||||
color: #FC0D0D;
|
||||
}
|
||||
#jspec-report td.requires-implementation {
|
||||
background: url(images/sprites.png) 3px -333px no-repeat;
|
||||
}
|
||||
#jspec-report tr.description td {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-indent: 0;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
#jspec-report tr.description:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
#jspec-report .assertion {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 0 0 1px;
|
||||
padding: 0;
|
||||
width: 1px;
|
||||
height: 5px;
|
||||
background: #7B8D9B;
|
||||
}
|
||||
#jspec-report .assertion.failed {
|
||||
background: red;
|
||||
}
|
||||
.jspec-sandbox {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
|
||||
// JSpec - Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
;(function(){
|
||||
|
||||
Growl = {
|
||||
|
||||
// --- Version
|
||||
|
||||
version: '1.0.0',
|
||||
|
||||
/**
|
||||
* Execute the given _cmd_, returning an array of lines from stdout.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Growl.exec('growlnotify', '-m', msg)
|
||||
*
|
||||
* @param {string ...} cmd
|
||||
* @return {array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exec: function(cmd) {
|
||||
var lines = [], line
|
||||
with (JavaImporter(java.lang, java.io)) {
|
||||
var proccess = Runtime.getRuntime().exec(Array.prototype.slice.call(arguments))
|
||||
var stream = new DataInputStream(proccess.getInputStream())
|
||||
while (line = stream.readLine())
|
||||
lines.push(line + '')
|
||||
stream.close()
|
||||
}
|
||||
return lines
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the extension of the given _path_ or null.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
extname: function(path) {
|
||||
return path.lastIndexOf('.') != -1 ?
|
||||
path.slice(path.lastIndexOf('.') + 1, path.length) :
|
||||
null
|
||||
},
|
||||
|
||||
/**
|
||||
* Version of the 'growlnotify' binary.
|
||||
*
|
||||
* @return {string}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
binVersion: function() {
|
||||
try { return this.exec('growlnotify', '-v')[0].split(' ')[1] } catch (e) {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Send growl notification _msg_ with _options_.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - title Notification title
|
||||
* - sticky Make the notification stick (defaults to false)
|
||||
* - name Application name (defaults to growlnotify)
|
||||
* - image
|
||||
* - path to an icon sets --iconpath
|
||||
* - path to an image sets --image
|
||||
* - capitalized word sets --appIcon
|
||||
* - filename uses extname as --icon
|
||||
* - otherwise treated as --icon
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Growl.notify('New email')
|
||||
* Growl.notify('5 new emails', { title: 'Thunderbird' })
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {options} hash
|
||||
* @api public
|
||||
*/
|
||||
|
||||
notify: function(msg, options) {
|
||||
options = options || {}
|
||||
var args = ['growlnotify', '-m', msg]
|
||||
if (!this.binVersion()) throw new Error('growlnotify executable is required')
|
||||
if (image = options.image) {
|
||||
var flag, ext = this.extname(image)
|
||||
flag = flag || ext == 'icns' && 'iconpath'
|
||||
flag = flag || /^[A-Z]/.test(image) && 'appIcon'
|
||||
flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
|
||||
flag = flag || ext && (image = ext) && 'icon'
|
||||
flag = flag || 'icon'
|
||||
args.push('--' + flag, image)
|
||||
}
|
||||
if (options.sticky) args.push('--sticky')
|
||||
if (options.name) args.push('--name', options.name)
|
||||
if (options.title) args.push(options.title)
|
||||
this.exec.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
JSpec.include({
|
||||
name: 'Growl',
|
||||
reporting: function(options){
|
||||
var stats = JSpec.stats
|
||||
if (stats.failures) Growl.notify('failed ' + stats.failures + ' assertions', { title: 'JSpec'})
|
||||
else Growl.notify('passed ' + stats.passes + ' assertions', { title: 'JSpec' })
|
||||
}
|
||||
})
|
||||
|
||||
})()
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
// JSpec - jQuery - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
JSpec
|
||||
.requires('jQuery', 'when using jspec.jquery.js')
|
||||
.include({
|
||||
name: 'jQuery',
|
||||
|
||||
// --- Initialize
|
||||
|
||||
init : function() {
|
||||
jQuery.ajaxSetup({ async: false })
|
||||
},
|
||||
|
||||
// --- Utilities
|
||||
|
||||
utilities : {
|
||||
element: jQuery,
|
||||
elements: jQuery,
|
||||
sandbox : function() {
|
||||
return jQuery('<div class="sandbox"></div>')
|
||||
}
|
||||
},
|
||||
|
||||
// --- Matchers
|
||||
|
||||
matchers : {
|
||||
have_tag : "jQuery(expected, actual).length == 1",
|
||||
have_one : "alias have_tag",
|
||||
have_tags : "jQuery(expected, actual).length > 1",
|
||||
have_many : "alias have_tags",
|
||||
have_child : "jQuery(actual).children(expected).length == 1",
|
||||
have_children : "jQuery(actual).children(expected).length > 1",
|
||||
have_text : "jQuery(actual).text() == expected",
|
||||
have_value : "jQuery(actual).val() == expected",
|
||||
be_enabled : "!jQuery(actual).attr('disabled')",
|
||||
have_class : "jQuery(actual).hasClass(expected)",
|
||||
|
||||
be_visible : function(actual) {
|
||||
return jQuery(actual).css('display') != 'none' &&
|
||||
jQuery(actual).css('visibility') != 'hidden' &&
|
||||
jQuery(actual).attr('type') != 'hidden'
|
||||
},
|
||||
|
||||
be_hidden : function(actual) {
|
||||
return !JSpec.does(actual, 'be_visible')
|
||||
},
|
||||
|
||||
have_classes : function(actual) {
|
||||
return !JSpec.any(JSpec.toArray(arguments, 1), function(arg){
|
||||
return !JSpec.does(actual, 'have_class', arg)
|
||||
})
|
||||
},
|
||||
|
||||
have_attr : function(actual, attr, value) {
|
||||
return value ? jQuery(actual).attr(attr) == value:
|
||||
jQuery(actual).attr(attr)
|
||||
},
|
||||
|
||||
'be disabled selected checked' : function(attr) {
|
||||
return 'jQuery(actual).attr("' + attr + '")'
|
||||
},
|
||||
|
||||
'have type id title alt href src sel rev name target' : function(attr) {
|
||||
return function(actual, value) {
|
||||
return JSpec.does(actual, 'have_attr', attr, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
|
||||
// JSpec - Shell - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
;(function(){
|
||||
|
||||
var _quit = quit
|
||||
|
||||
Shell = {
|
||||
|
||||
// --- Global
|
||||
|
||||
main: this,
|
||||
|
||||
// --- Commands
|
||||
|
||||
commands: {
|
||||
quit: ['Terminate the shell', function(){ _quit() }],
|
||||
exit: ['Terminate the shell', function(){ _quit() }],
|
||||
p: ['Inspect an object', function(o){ return o.toSource() }]
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the interactive shell.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
start : function() {
|
||||
for (var name in this.commands)
|
||||
if (this.commands.hasOwnProperty(name))
|
||||
this.commands[name][1].length ?
|
||||
this.main[name] = this.commands[name][1] :
|
||||
this.main.__defineGetter__(name, this.commands[name][1])
|
||||
}
|
||||
}
|
||||
|
||||
Shell.start()
|
||||
|
||||
})()
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
// JSpec - Mock Timers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
;(function(){
|
||||
|
||||
/**
|
||||
* Version.
|
||||
*/
|
||||
|
||||
mockTimersVersion = '1.0.2'
|
||||
|
||||
/**
|
||||
* Localized timer stack.
|
||||
*/
|
||||
|
||||
var timers = []
|
||||
|
||||
/**
|
||||
* Set mock timeout with _callback_ and timeout of _ms_.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @param {int} ms
|
||||
* @return {int}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
setTimeout = function(callback, ms) {
|
||||
var id
|
||||
return id = setInterval(function(){
|
||||
callback()
|
||||
clearInterval(id)
|
||||
}, ms)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mock interval with _callback_ and interval of _ms_.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @param {int} ms
|
||||
* @return {int}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
setInterval = function(callback, ms) {
|
||||
callback.step = ms, callback.current = callback.last = 0
|
||||
return timers[timers.length] = callback, timers.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy timer with _id_.
|
||||
*
|
||||
* @param {int} id
|
||||
* @return {bool}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clearInterval = clearTimeout = function(id) {
|
||||
return delete timers[--id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset timers.
|
||||
*
|
||||
* @return {array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
resetTimers = function() {
|
||||
return timers = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment each timers internal clock by _ms_.
|
||||
*
|
||||
* @param {int} ms
|
||||
* @api public
|
||||
*/
|
||||
|
||||
tick = function(ms) {
|
||||
for (var i = 0, len = timers.length; i < len; ++i)
|
||||
if (timers[i] && (timers[i].current += ms))
|
||||
if (timers[i].current - timers[i].last >= timers[i].step) {
|
||||
var times = Math.floor((timers[i].current - timers[i].last) / timers[i].step)
|
||||
var remainder = (timers[i].current - timers[i].last) % timers[i].step
|
||||
timers[i].last = timers[i].current - remainder
|
||||
while (times-- && timers[i]) timers[i]()
|
||||
}
|
||||
}
|
||||
|
||||
})()
|
|
@ -0,0 +1,193 @@
|
|||
|
||||
// JSpec - XHR - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
(function(){
|
||||
|
||||
// --- Original XMLHttpRequest
|
||||
|
||||
var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ?
|
||||
XMLHttpRequest :
|
||||
function(){}
|
||||
var OriginalActiveXObject = 'ActiveXObject' in this ?
|
||||
ActiveXObject :
|
||||
undefined
|
||||
|
||||
// --- MockXMLHttpRequest
|
||||
|
||||
var MockXMLHttpRequest = function() {
|
||||
this.requestHeaders = {}
|
||||
}
|
||||
|
||||
MockXMLHttpRequest.prototype = {
|
||||
status: 0,
|
||||
async: true,
|
||||
readyState: 0,
|
||||
responseText: '',
|
||||
abort: function(){},
|
||||
onreadystatechange: function(){},
|
||||
|
||||
/**
|
||||
* Return response headers hash.
|
||||
*/
|
||||
|
||||
getAllResponseHeaders : function(){
|
||||
return this.responseHeaders
|
||||
},
|
||||
|
||||
/**
|
||||
* Return case-insensitive value for header _name_.
|
||||
*/
|
||||
|
||||
getResponseHeader : function(name) {
|
||||
return this.responseHeaders[name.toLowerCase()]
|
||||
},
|
||||
|
||||
/**
|
||||
* Set case-insensitive _value_ for header _name_.
|
||||
*/
|
||||
|
||||
setRequestHeader : function(name, value) {
|
||||
this.requestHeaders[name.toLowerCase()] = value
|
||||
},
|
||||
|
||||
/**
|
||||
* Open mock request.
|
||||
*/
|
||||
|
||||
open : function(method, url, async, user, password) {
|
||||
this.user = user
|
||||
this.password = password
|
||||
this.url = url
|
||||
this.readyState = 1
|
||||
this.method = method.toUpperCase()
|
||||
if (async != undefined) this.async = async
|
||||
if (this.async) this.onreadystatechange()
|
||||
},
|
||||
|
||||
/**
|
||||
* Send request _data_.
|
||||
*/
|
||||
|
||||
send : function(data) {
|
||||
var self = this
|
||||
this.data = data
|
||||
this.readyState = 4
|
||||
if (this.method == 'HEAD') this.responseText = null
|
||||
this.responseHeaders['content-length'] = (this.responseText || '').length
|
||||
if(this.async) this.onreadystatechange()
|
||||
lastRequest = function(){
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Response status codes
|
||||
|
||||
JSpec.statusCodes = {
|
||||
100: 'Continue',
|
||||
101: 'Switching Protocols',
|
||||
200: 'OK',
|
||||
201: 'Created',
|
||||
202: 'Accepted',
|
||||
203: 'Non-Authoritative Information',
|
||||
204: 'No Content',
|
||||
205: 'Reset Content',
|
||||
206: 'Partial Content',
|
||||
300: 'Multiple Choice',
|
||||
301: 'Moved Permanently',
|
||||
302: 'Found',
|
||||
303: 'See Other',
|
||||
304: 'Not Modified',
|
||||
305: 'Use Proxy',
|
||||
307: 'Temporary Redirect',
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Request Entity Too Large',
|
||||
414: 'Request-URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Requested Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
422: 'Unprocessable Entity',
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported'
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock XMLHttpRequest requests.
|
||||
*
|
||||
* mockRequest().and_return('some data', 'text/plain', 200, { 'X-SomeHeader' : 'somevalue' })
|
||||
*
|
||||
* @return {hash}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function mockRequest() {
|
||||
return { and_return : function(body, type, status, headers) {
|
||||
XMLHttpRequest = MockXMLHttpRequest
|
||||
ActiveXObject = false
|
||||
status = status || 200
|
||||
headers = headers || {}
|
||||
headers['content-type'] = type
|
||||
JSpec.extend(XMLHttpRequest.prototype, {
|
||||
responseText: body,
|
||||
responseHeaders: headers,
|
||||
status: status,
|
||||
statusText: JSpec.statusCodes[status]
|
||||
})
|
||||
}}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmock XMLHttpRequest requests.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function unmockRequest() {
|
||||
XMLHttpRequest = OriginalXMLHttpRequest
|
||||
ActiveXObject = OriginalActiveXObject
|
||||
}
|
||||
|
||||
JSpec.include({
|
||||
name: 'Mock XHR',
|
||||
|
||||
// --- Utilities
|
||||
|
||||
utilities : {
|
||||
mockRequest: mockRequest,
|
||||
unmockRequest: unmockRequest
|
||||
},
|
||||
|
||||
// --- Hooks
|
||||
|
||||
afterSpec : function() {
|
||||
unmockRequest()
|
||||
},
|
||||
|
||||
// --- DSLs
|
||||
|
||||
DSLs : {
|
||||
snake : {
|
||||
mock_request: mockRequest,
|
||||
unmock_request: unmockRequest,
|
||||
last_request: function(){ return lastRequest() }
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
})()
|
|
@ -0,0 +1,33 @@
|
|||
var kiwi= require('kiwi');
|
||||
kiwi.require('express')
|
||||
require.paths.unshift('spec', 'lib', 'spec/lib')
|
||||
|
||||
require("jspec")
|
||||
|
||||
print = require('sys').puts
|
||||
quit = process.exit
|
||||
readFile = require('fs').readFileSync
|
||||
|
||||
function run(specs) {
|
||||
specs.forEach(function(spec){
|
||||
JSpec.exec('spec/spec.' + spec + '.js')
|
||||
})
|
||||
}
|
||||
|
||||
specs = {
|
||||
independant: [
|
||||
'oauth',
|
||||
'sha1'
|
||||
]
|
||||
}
|
||||
|
||||
switch (process.ARGV[2]) {
|
||||
case 'all':
|
||||
run(specs.independant)
|
||||
break
|
||||
default:
|
||||
run([process.ARGV[2]])
|
||||
}
|
||||
|
||||
Express.environment = 'test'
|
||||
JSpec.run({ reporter: JSpec.reporters.Terminal, failuresOnly: true }).report()
|
|
@ -0,0 +1,14 @@
|
|||
describe 'node-oauth'
|
||||
before_each
|
||||
OAuth= require('oauth').OAuth
|
||||
end
|
||||
describe 'Auth'
|
||||
describe 'OAuth'
|
||||
it 'should generate the signature base string described in http://oauth.net/core/1.0/#sig_base_example'
|
||||
var result= new OAuth()._createSignatureBase("GET", "http://photos.example.net/photos",
|
||||
"file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original")
|
||||
result.should.eql "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
describe 'node-oauth'
|
||||
before_each
|
||||
end
|
||||
describe 'SHA1'
|
||||
describe 'HMAC-SHA1'
|
||||
it 'should produce the specified digest as described in http://oauth.net/core/1.0/#sig_base_example (A.5.2)'
|
||||
var SHA1= require('sha1');
|
||||
var hash= SHA1.HMACSHA1( "kd94hf93k423kf44&pfkkdhi9sl3r4s00",
|
||||
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal")
|
||||
|
||||
hash.should.eql ("tR3+Ty81lMeYAr/Fid0kMTYa/WM=")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue