/**
* (c) amachang
* licence http://twitter.com/amachang/statuses/199049982
* script available on http://usrb.in/svn/sbme/trunk/sbme@usrb.in/chrome/content/util.js
* slightly modified by ku.
**/
var SBME = {
VERSION: "0.0.1",
merge: function(dst) {
for (var n in dst) this[n] = dst[n];
},
debug: function(text) {
// TODO: DELETEME !! 超危険!!後で消す!
window._content.wrappedJSObject.SBME = SBME;
},
getService: function(url, interface) {
return Components.classes[url].getService(Components.interfaces[interface]);
},
createInstance: function(url, interface) {
return Components.classes[url].createInstance(Components.interfaces[interface]);
},
QI: function(instance, interface) {
return instance.QueryInterface(Components.interfaces[interface]);
},
newURI: function(uri) {
const IO_SERVICE = Components.classes['@mozilla.org/network/io-service;1']
.getService(Components.interfaces.nsIIOService);
return IO_SERVICE.newURI(uri, null, null);
},
newChannel: function(uri) {
const IO_SERVICE = Components.classes['@mozilla.org/network/io-service;1']
.getService(Components.interfaces.nsIIOService);
return IO_SERVICE.newChannelFromURI(SBME.newURI(uri, null, null));
},
setMethod: function(channel, method) {
SBME.QI(channel, 'nsIHttpChannel').requestMethod = method.toUpperCase();
},
multipart: function (channel, params) {
var separator = 'DEADBEEF_DEADBEEF'
var postdata = '';
SBME.QI(channel, 'nsIHttpChannel');
// var content_type = 'multipart/form-data; boundary=' + separator;
// channel.setRequestHeader('Content-Type', content_type, false);
for ( var n in params ) {
var v = params[n];
postdata += '--' + separator + '\r\n';
if ( v instanceof Components.interfaces.nsIInputStream ) {
postdata += 'Content-Disposition: form-data; name=' + n.qq() + '; filename="cow_ymsgr.png"' + '\r\n';
postdata += 'Content-Type: image/png\r\n';
postdata += '\r\n';
var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
bstream.setInputStream(v);
postdata += (bstream.readBytes(bstream.available()) );
} else {
postdata += 'Content-Disposition: form-data; name=' + n.qq() + '\r\n';
postdata += '\r\n';
postdata += v;
}
postdata += '\r\n';
}
postdata += '--' + separator + '--\r\n';
//channel.setRequestHeader('Content-Length', postdata.length, false);
return postdata;
},
setPostData: function(channel, data) {
var content_type = 'application/x-www-form-urlencoded';
if ( typeof data == 'object' ) {
data = this.multipart(channel, data);
content_type = 'multipart/form-data; boundary=DEADBEEF_DEADBEEF';
//content_type = channel.getRequestHeader('Content-Type');
}
//var stream = SBME.createInstance('@mozilla.org/io/string-input-stream;1', 'nsIStringInputStream');
//stream.setData(data, data.length);
var url = "data:application/octet-stream;base64," + SBME.toBase64( data );
const IO_SERVICE = Components.classes['@mozilla.org/network/io-service;1']
.getService(Components.interfaces.nsIIOService);
url = IO_SERVICE.newURI(url, null, null);
var data_channel = IO_SERVICE.newChannelFromURI( url );
var input = data_channel.open();
SBME.QI(channel, 'nsIUploadChannel').setUploadStream(input, content_type, -1);
SBME.setMethod(channel, 'POST');
},
setHeaderData: function(channel, headers) {
channel = SBME.QI(channel, 'nsIHttpChannel');
for (var n in headers) {
if (headers[n].constructor.name == 'Array') {
headers[n].forEach(function(header) {
channel.setRequestHeader(n, header, false);
});
}
else {
channel.setRequestHeader(n, headers[n], false);
}
}
},
setCookieData: function(channel, cookies) {
SBME.setHeaderData(channel, { Cookie: cookies });
},
setReferrerData: function(channel, uri) {
SBME.QI(channel, 'nsIHttpChannel').referrer = SBME.newURI(uri, null, null);
},
toBase64: function(data) {
if (typeof data == 'string') return btoa(data);
const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const base64Pad = '=';
var result = '';
var length = data.length;
for (var i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += toBase64Table[data[i+2] & 0x3f];
}
if (length%3) {
i = length - (length%3);
result += toBase64Table[data[i] >> 2];
if ((length%3) == 2) {
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += toBase64Table[(data[i+1] & 0x0f) << 2];
result += base64Pad;
}
else {
result += toBase64Table[(data[i] & 0x03) << 4];
result += base64Pad + base64Pad;
}
}
return result;
},
setWsseData: function(channel, username, password) {
var created = SBME.iso8601Date(new Date());
var nonce = (Math.random() + "").substr(2, 32);
var data = nonce + created + (password || '');
var converter = SBME.createInstance(
'@mozilla.org/intl/scriptableunicodeconverter', 'nsIScriptableUnicodeConverter');
converter.charset = 'UTF-8';
data = converter.convertToByteArray(data, {});
var ch = SBME.createInstance('@mozilla.org/security/hash;1', 'nsICryptoHash');
ch.init(ch.SHA1);
ch.update(data, data.length);
var data = ch.finish(false);
var passwordDigest = SBME.toBase64(data);
nonce = SBME.toBase64(nonce);
var wsse = 'UsernameToken Username="' + username + '", PasswordDigest="' + passwordDigest + '", Created="' + created + '", Nonce="' + nonce + '"';
SBME.setHeaderData(channel, { 'Authorization': 'WSSE profile="UsernameToken"', 'X-WSSE': wsse });
},
httpRequest: function(opts) {
var uri = opts.uri;
var post = opts.post;
var cookies = opts.cookies;
var refer = opts.refer;
var callback = opts.callback;
var wsseUsername = opts.wsseUsername;
var wssePassword = opts.wssePassword;
// var basicUsername = opts.basicUsername;
// var basicPassword = opts.basicPassword;
var headers = opts.headers;
var method = opts.method;
var channel = SBME.newChannel(uri);
if (refer) SBME.setReferrerData(channel, refer );
if (post) SBME.setPostData (channel, post );
if (cookies) SBME.setCookieData (channel, cookies);
if (wsseUsername) SBME.setWsseData (channel, wsseUsername, wssePassword);
if (method) SBME.setMethod (channel, method );
if (headers) SBME.setHeaderData (channel, headers);
if ( opts.notificationCallbacks ) {
channel.notificationCallbacks = opts.notificationCallbacks;
}
channel.asyncOpen({
onStartRequest: function(request, context) {
this.data = "";
},
onDataAvailable: function(request, context, stream, sourceOffset, length){
var sStream = SBME.createInstance('@mozilla.org/scriptableinputstream;1', 'nsIScriptableInputStream');
sStream.init(stream);
this.data += sStream.read(length);
},
onStopRequest : function (request, context, status) {
callback(this.data, request);
}
}, null);
},
newXmlRpc: function(uri, methodName) {
function parseNumber(arg) {
if (parseInt(arg) == arg) return {arg};
else return {arg};
}
function parseBoolean(arg) {
return {arg ? 1 : 0};
}
function parseString(arg) {
return {arg};
}
function parseDate(arg) {
return {SBME.iso8601Date(arg)};
}
function parseArray(arg) {
var array = ;
arg.forEach(function(e) {
array.appendChild(parse(e));
});
return array;
}
function parseObject(arg) {
var struct = ;
for (var n in arg) {
var member =
member.appendChild({n});
member.appendChild(parse(arg[n]));
struct.appendChild(member);
}
return struct;
}
function parse(arg) {
var param;
switch (typeof arg) {
case 'number': param = parseNumber(arg); break;
case 'boolean': param = parseBoolean(arg); break;
case 'string': param = parseString(arg); break;
default:
switch(arg.constructor.name) {
case 'Number': param = parseNumber(arg); break;
case 'Boolean': param = parseBoolean(arg); break;
case 'String': param = parseString(arg); break;
case 'Date': param = parseDate(arg); break;
case 'Array': param = parseArray(arg); break;
default: param = parseObject(arg); break;
}
break;
}
var result = ;
result.appendChild(param);
return result;
}
function unparse(value) {
var value = value.children()[0];
switch(value.localName()) {
case 'i4': case 'int': case 'double':
value = +(value.text().toString());
break;
case 'string':
value = value.text().toString();
break;
case 'dateTime.iso8601':
value.text().toString().match(/^(\d{4})-?(\d{2})-?(\d{2})T(\d{2}):?(\d{2}):?(\d{2})/);
new Date(Date.UTC(RegExp.$1, RegExp.$2-1, RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6));
break;
case 'array':
var a = [];
for (var n in value.value) {
a.push(unparse(value.value));
}
value = a;
break;
case 'struct':
var o = {};
for (var n in value..member) {
o[value..member[n].name.text().toString()] = unparse(value..member[n].value);
}
value = o;
break;
default:
value = undefined;
break;
}
return value;
}
return function(/* ..., opts */) {
var methodXML =
{methodName}
;
var opts = Array.pop(arguments);
switch(typeof opts) {
case 'function':
opts = { callback: opts };
break;
case 'string':
case 'number':
case 'boolean':
Array.push(arguments, opts);
opts = {};
break;
}
Array.forEach(arguments, function(arg) {
var param = ;
param.appendChild(parse(arg));
methodXML.params.appendChild(param);
});
opts.uri = uri;
opts.post = methodXML.toString();
opts.headers = {
'Content-Type': 'text/xml'
};
if(opts.callback) {
var callback = opts.callback;
opts.callback = function(data, req) {
if(data.match(/^<\?xml/)) {
data = data.replace(/<\?xml.*?\?>/, '');
}
var data = new XML(data);
var params = data.params.param;
var args = [];
for (var n in params) {
args.push(unparse(params[n].value));
}
callback.apply(null, args);
}
}
SBME.httpRequest(opts);
};
},
newAtomAPI: function(opts) {
var username = opts.username;
var password = opts.password;
var postUri = opts.postUri;
var editUri = opts.editUri;
var editFromEntryUri = opts.editFromEntryUri
if (typeof editUri == 'function') {
var editUriFactory = opts.editUri
}
else {
var editUriFactory = function(id) { return editUri + id; }
}
if (typeof editFromEntryUri == 'function') {
var editFromEntryUriFactory = opts.editFromEntryUri
}
else {
var editFromEntryUriFactory = function(uri) { return editFromEntryUri + uri; }
}
return {
_createEntryData: function(content) {
var text = null;
var tags = [ '' ];
for ( var tagname in content ) {
var pairs = [];
var attributes = content[tagname];
var contentText = null;
for ( var attr in attributes ) {
if ( attr == 'TEXT' ) {
contentText = attributes[attr];
} else {
pairs.push( attr + '=' + attributes[attr].qq() );
}
}
pairs.unshift(tagname);
tags.push( '<' + pairs.join(" ") + '>' + contentText +
'' + tagname + '>' );
}
tags.push('');
var ret = tags.join("\n");
return ret;
},
create: function(content, callback) {
var uri = postUri;
this._internal(uri, 'POST', this._createEntryData(content), callback);
},
read: function(id, callback) {
var uri = editUriFactory(id);
this._internal(uri, 'GET', undefined, callback);
},
update: function(id, opts, callback) {
var uri = editUriFactory(id);
this._internal(uri, 'PUT', this._createEntryData(opts), callback);
},
delete: function(id, callback) {
var uri = editUriFactory(id);
this._internal(uri, 'DELETE', undefined, callback);
},
readFromEntry: function(uri, callback) {
var uri = editFromEntryUriFactory(uri);
this._internal(uri, 'GET', undefined, callback);
},
_internal: function(uri, method, post, callback) {
SBME.httpRequest({
uri: uri,
post: post,
method: method,
wsseUsername: username,
wssePassword: password,
callback: function(data, req) {
if(data.match(/^<\?xml/)) {
data = data.replace(/<\?xml.*?\?>/, '');
}
try {
data = new XML(data);
}
catch(e) {
data = '';
}
callback(data, req);
}
});
}
};
},
iso8601Date: function(date) {
var datetime = date.getUTCFullYear();
var month = String(date.getUTCMonth() + 1);
datetime += (month.length == 1 ? '0' + month : month);
var day = date.getUTCDate();
datetime += (day < 10 ? '0' + day : day);
datetime += 'T';
var hour = date.getUTCHours();
datetime += (hour < 10 ? '0' + hour : hour) + ':';
var minutes = date.getUTCMinutes();
datetime += (minutes < 10 ? '0' + minutes : minutes) + ':';
var seconds = date.getUTCSeconds();
datetime += (seconds < 10 ? '0' + seconds : seconds);
return datetime;
}
};