/** * (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 + '' ); } 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; } };