1189 lines
34 KiB
JavaScript
1189 lines
34 KiB
JavaScript
module.exports = (function() {
|
|
var __MODS__ = {};
|
|
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
|
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
|
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
|
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
|
__DEFINE__(1738455898207, function(require, module, exports) {
|
|
var Client = require('./client')
|
|
, Server = require('./server')
|
|
, CustomType = require('./customtype')
|
|
, dateFormatter = require('./date_formatter')
|
|
|
|
var xmlrpc = exports
|
|
|
|
/**
|
|
* Creates an XML-RPC client.
|
|
*
|
|
* @param {Object} options - server options to make the HTTP request to
|
|
* - {String} host
|
|
* - {Number} port
|
|
* - {String} url
|
|
* - {Boolean} cookies
|
|
* @return {Client}
|
|
* @see Client
|
|
*/
|
|
xmlrpc.createClient = function(options) {
|
|
return new Client(options, false)
|
|
}
|
|
|
|
/**
|
|
* Creates an XML-RPC client that makes calls using HTTPS.
|
|
*
|
|
* @param {Object} options - server options to make the HTTP request to
|
|
* - {String} host
|
|
* - {Number} port
|
|
* - {String} url
|
|
* - {Boolean} cookies
|
|
* @return {Client}
|
|
* @see Client
|
|
*/
|
|
xmlrpc.createSecureClient = function(options) {
|
|
return new Client(options, true)
|
|
}
|
|
|
|
/**
|
|
* Creates an XML-RPC server.
|
|
*
|
|
* @param {Object}options - the HTTP server options
|
|
* - {String} host
|
|
* - {Number} port
|
|
* @return {Server}
|
|
* @see Server
|
|
*/
|
|
xmlrpc.createServer = function(options, callback) {
|
|
return new Server(options, false, callback)
|
|
}
|
|
|
|
/**
|
|
* Creates an XML-RPC server that uses HTTPS.
|
|
*
|
|
* @param {Object}options - the HTTP server options
|
|
* - {String} host
|
|
* - {Number} port
|
|
* @return {Server}
|
|
* @see Server
|
|
*/
|
|
xmlrpc.createSecureServer = function(options, callback) {
|
|
return new Server(options, true, callback)
|
|
}
|
|
|
|
xmlrpc.CustomType = CustomType
|
|
xmlrpc.dateFormatter = dateFormatter
|
|
|
|
}, function(modId) {var map = {"./client":1738455898208,"./server":1738455898214,"./customtype":1738455898211,"./date_formatter":1738455898210}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898208, function(require, module, exports) {
|
|
var http = require('http')
|
|
, https = require('https')
|
|
, url = require('url')
|
|
, Serializer = require('./serializer')
|
|
, Deserializer = require('./deserializer')
|
|
, Cookies = require('./cookies')
|
|
|
|
/**
|
|
* Creates a Client object for making XML-RPC method calls.
|
|
*
|
|
* @constructor
|
|
* @param {Object|String} options - Server options to make the HTTP request to.
|
|
* Either a URI string
|
|
* (e.g. 'http://localhost:9090') or an object
|
|
* with fields:
|
|
* - {String} host - (optional)
|
|
* - {Number} port
|
|
* - {String} url - (optional) - may be used instead of host/port pair
|
|
* - {Boolean} cookies - (optional) - if true then cookies returned by server will be stored and sent back on the next calls.
|
|
* Also it will be possible to access/manipulate cookies via #setCookie/#getCookie methods
|
|
* @param {Boolean} isSecure - True if using https for making calls,
|
|
* otherwise false.
|
|
* @return {Client}
|
|
*/
|
|
function Client(options, isSecure) {
|
|
|
|
// Invokes with new if called without
|
|
if (false === (this instanceof Client)) {
|
|
return new Client(options, isSecure)
|
|
}
|
|
|
|
// If a string URI is passed in, converts to URI fields
|
|
if (typeof options === 'string') {
|
|
options = url.parse(options)
|
|
options.host = options.hostname
|
|
options.path = options.pathname
|
|
}
|
|
|
|
if (typeof options.url !== 'undefined') {
|
|
var parsedUrl = url.parse(options.url);
|
|
options.host = parsedUrl.hostname;
|
|
options.path = parsedUrl.pathname;
|
|
options.port = parsedUrl.port;
|
|
}
|
|
|
|
// Set the HTTP request headers
|
|
var headers = {
|
|
'User-Agent' : 'NodeJS XML-RPC Client'
|
|
, 'Content-Type' : 'text/xml'
|
|
, 'Accept' : 'text/xml'
|
|
, 'Accept-Charset' : 'UTF8'
|
|
, 'Connection' : 'Keep-Alive'
|
|
}
|
|
options.headers = options.headers || {}
|
|
|
|
if (options.headers.Authorization == null &&
|
|
options.basic_auth != null &&
|
|
options.basic_auth.user != null &&
|
|
options.basic_auth.pass != null)
|
|
{
|
|
var auth = options.basic_auth.user + ':' + options.basic_auth.pass
|
|
options.headers['Authorization'] = 'Basic ' + new Buffer(auth).toString('base64')
|
|
}
|
|
|
|
for (var attribute in headers) {
|
|
if (options.headers[attribute] === undefined) {
|
|
options.headers[attribute] = headers[attribute]
|
|
}
|
|
}
|
|
|
|
options.method = 'POST'
|
|
this.options = options
|
|
|
|
this.isSecure = isSecure
|
|
this.headersProcessors = {
|
|
processors: [],
|
|
composeRequest: function(headers) {
|
|
this.processors.forEach(function(p) {p.composeRequest(headers);})
|
|
},
|
|
parseResponse: function(headers) {
|
|
this.processors.forEach(function(p) {p.parseResponse(headers);})
|
|
}
|
|
};
|
|
if (options.cookies) {
|
|
this.cookies = new Cookies();
|
|
this.headersProcessors.processors.unshift(this.cookies);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes an XML-RPC call to the server specified by the constructor's options.
|
|
*
|
|
* @param {String} method - The method name.
|
|
* @param {Array} params - Params to send in the call.
|
|
* @param {Function} callback - function(error, value) { ... }
|
|
* - {Object|null} error - Any errors when making the call, otherwise null.
|
|
* - {mixed} value - The value returned in the method response.
|
|
*/
|
|
Client.prototype.methodCall = function methodCall(method, params, callback) {
|
|
var options = this.options
|
|
var xml = Serializer.serializeMethodCall(method, params, options.encoding)
|
|
var transport = this.isSecure ? https : http
|
|
|
|
options.headers['Content-Length'] = Buffer.byteLength(xml, 'utf8')
|
|
this.headersProcessors.composeRequest(options.headers)
|
|
var request = transport.request(options, function(response) {
|
|
|
|
var body = []
|
|
response.on('data', function (chunk) { body.push(chunk) })
|
|
|
|
function __enrichError (err) {
|
|
Object.defineProperty(err, 'req', { value: request })
|
|
Object.defineProperty(err, 'res', { value: response })
|
|
Object.defineProperty(err, 'body', { value: body.join('') })
|
|
return err
|
|
}
|
|
|
|
if (response.statusCode == 404) {
|
|
callback(__enrichError(new Error('Not Found')))
|
|
}
|
|
else {
|
|
this.headersProcessors.parseResponse(response.headers)
|
|
|
|
var deserializer = new Deserializer(options.responseEncoding)
|
|
|
|
deserializer.deserializeMethodResponse(response, function(err, result) {
|
|
if (err) {
|
|
err = __enrichError(err)
|
|
}
|
|
callback(err, result)
|
|
})
|
|
}
|
|
}.bind(this))
|
|
|
|
request.on('error', callback)
|
|
request.write(xml, 'utf8')
|
|
request.end()
|
|
}
|
|
|
|
/**
|
|
* Gets the cookie value by its name. The latest value received from servr with 'Set-Cookie' header is returned
|
|
* Note that method throws an error if cookies were not turned on during client creation (see comments for constructor)
|
|
*
|
|
* @param {String} name name of the cookie to be obtained or changed
|
|
* @return {*} cookie's value
|
|
*/
|
|
Client.prototype.getCookie = function getCookie(name) {
|
|
if (!this.cookies) {
|
|
throw 'Cookies support is not turned on for this client instance';
|
|
}
|
|
return this.cookies.get(name);
|
|
}
|
|
|
|
/**
|
|
* Sets the cookie value by its name. The cookie will be sent to the server during the next xml-rpc call.
|
|
* The method returns client itself, so it is possible to chain calls like the following:
|
|
*
|
|
* <code>
|
|
* client.cookie('login', 'alex').cookie('password', '123');
|
|
* </code>
|
|
*
|
|
* Note that method throws an error if cookies were not turned on during client creation (see comments for constructor)
|
|
*
|
|
* @param {String} name name of the cookie to be changed
|
|
* @param {String} value value to be set.
|
|
* @return {*} client object itself
|
|
*/
|
|
Client.prototype.setCookie = function setCookie(name, value) {
|
|
if (!this.cookies) {
|
|
throw 'Cookies support is not turned on for this client instance';
|
|
}
|
|
this.cookies.set(name, value);
|
|
return this;
|
|
}
|
|
|
|
module.exports = Client
|
|
|
|
|
|
}, function(modId) { var map = {"./serializer":1738455898209,"./deserializer":1738455898212,"./cookies":1738455898213}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898209, function(require, module, exports) {
|
|
var xmlBuilder = require('xmlbuilder')
|
|
, dateFormatter = require('./date_formatter')
|
|
, CustomType = require('./customtype')
|
|
|
|
/**
|
|
* Creates the XML for an XML-RPC method call.
|
|
*
|
|
* @param {String} method - The method name.
|
|
* @param {Array} params - Params to pass in the call.
|
|
* @param {Function} callback - function (error, xml) { ... }
|
|
* - {Object|null} error - Any errors that occurred while building the XML,
|
|
* otherwise null.
|
|
* - {String} xml - The method call XML.
|
|
*/
|
|
exports.serializeMethodCall = function(method, params, encoding) {
|
|
var params = params || []
|
|
|
|
var options = { version: '1.0', allowSurrogateChars: true }
|
|
|
|
if (encoding) {
|
|
options.encoding = encoding
|
|
}
|
|
|
|
var xml = xmlBuilder.create('methodCall', options)
|
|
.ele('methodName')
|
|
.txt(method)
|
|
.up()
|
|
.ele('params')
|
|
|
|
params.forEach(function(param) {
|
|
serializeValue(param, xml.ele('param'))
|
|
})
|
|
|
|
// Includes the <?xml ...> declaration
|
|
return xml.doc().toString()
|
|
}
|
|
|
|
/**
|
|
* Creates the XML for an XML-RPC method response.
|
|
*
|
|
* @param {mixed} value - The value to pass in the response.
|
|
* @param {Function} callback - function (error, xml) { ... }
|
|
* - {Object|null} error - Any errors that occurred while building the XML,
|
|
* otherwise null.
|
|
* - {String} xml - The method response XML.
|
|
*/
|
|
exports.serializeMethodResponse = function(result) {
|
|
var xml = xmlBuilder.create('methodResponse', { version: '1.0', allowSurrogateChars: true })
|
|
.ele('params')
|
|
.ele('param')
|
|
|
|
serializeValue(result, xml)
|
|
|
|
// Includes the <?xml ...> declaration
|
|
return xml.doc().toString()
|
|
}
|
|
|
|
exports.serializeFault = function(fault) {
|
|
var xml = xmlBuilder.create('methodResponse', { version: '1.0', allowSurrogateChars: true })
|
|
.ele('fault')
|
|
|
|
serializeValue(fault, xml)
|
|
|
|
// Includes the <?xml ...> declaration
|
|
return xml.doc().toString()
|
|
}
|
|
|
|
function serializeValue(value, xml) {
|
|
var stack = [ { value: value, xml: xml } ]
|
|
, current = null
|
|
, valueNode = null
|
|
, next = null
|
|
|
|
while (stack.length > 0) {
|
|
current = stack[stack.length - 1]
|
|
|
|
if (current.index !== undefined) {
|
|
// Iterating a compound
|
|
next = getNextItemsFrame(current)
|
|
if (next) {
|
|
stack.push(next)
|
|
}
|
|
else {
|
|
stack.pop()
|
|
}
|
|
}
|
|
else {
|
|
// we're about to add a new value (compound or simple)
|
|
valueNode = current.xml.ele('value')
|
|
switch(typeof current.value) {
|
|
case 'boolean':
|
|
appendBoolean(current.value, valueNode)
|
|
stack.pop()
|
|
break
|
|
case 'string':
|
|
appendString(current.value, valueNode)
|
|
stack.pop()
|
|
break
|
|
case 'number':
|
|
appendNumber(current.value, valueNode)
|
|
stack.pop()
|
|
break
|
|
case 'object':
|
|
if (current.value === null) {
|
|
valueNode.ele('nil')
|
|
stack.pop()
|
|
}
|
|
else if (current.value instanceof Date) {
|
|
appendDatetime(current.value, valueNode)
|
|
stack.pop()
|
|
}
|
|
else if (Buffer.isBuffer(current.value)) {
|
|
appendBuffer(current.value, valueNode)
|
|
stack.pop()
|
|
}
|
|
else if (current.value instanceof CustomType) {
|
|
current.value.serialize(valueNode)
|
|
stack.pop()
|
|
}
|
|
else {
|
|
if (Array.isArray(current.value)) {
|
|
current.xml = valueNode.ele('array').ele('data')
|
|
}
|
|
else {
|
|
current.xml = valueNode.ele('struct')
|
|
current.keys = Object.keys(current.value)
|
|
}
|
|
current.index = 0
|
|
next = getNextItemsFrame(current)
|
|
if (next) {
|
|
stack.push(next)
|
|
}
|
|
else {
|
|
stack.pop()
|
|
}
|
|
}
|
|
break
|
|
default:
|
|
stack.pop()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function getNextItemsFrame(frame) {
|
|
var nextFrame = null
|
|
|
|
if (frame.keys) {
|
|
if (frame.index < frame.keys.length) {
|
|
var key = frame.keys[frame.index++]
|
|
, member = frame.xml.ele('member').ele('name').text(key).up()
|
|
nextFrame = {
|
|
value: frame.value[key]
|
|
, xml: member
|
|
}
|
|
}
|
|
}
|
|
else if (frame.index < frame.value.length) {
|
|
nextFrame = {
|
|
value: frame.value[frame.index]
|
|
, xml: frame.xml
|
|
}
|
|
frame.index++
|
|
}
|
|
|
|
return nextFrame
|
|
}
|
|
|
|
function appendBoolean(value, xml) {
|
|
xml.ele('boolean').txt(value ? 1 : 0)
|
|
}
|
|
|
|
var illegalChars = /^(?![^<&]*]]>[^<&]*)[^<&]*$/
|
|
function appendString(value, xml) {
|
|
if (value.length === 0) {
|
|
xml.ele('string')
|
|
}
|
|
else if (!illegalChars.test(value)) {
|
|
xml.ele('string').d(value)
|
|
}
|
|
else {
|
|
xml.ele('string').txt(value)
|
|
}
|
|
}
|
|
|
|
function appendNumber(value, xml) {
|
|
if (value % 1 == 0) {
|
|
xml.ele('int').txt(value)
|
|
}
|
|
else {
|
|
xml.ele('double').txt(value)
|
|
}
|
|
}
|
|
|
|
function appendDatetime(value, xml) {
|
|
xml.ele('dateTime.iso8601').txt(dateFormatter.encodeIso8601(value))
|
|
}
|
|
|
|
function appendBuffer(value, xml) {
|
|
xml.ele('base64').txt(value.toString('base64'))
|
|
}
|
|
|
|
}, function(modId) { var map = {"./date_formatter":1738455898210,"./customtype":1738455898211}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898210, function(require, module, exports) {
|
|
/**
|
|
* @class DateFormatter
|
|
* The DateFormatter supports decoding from and encoding to
|
|
* ISO8601 formatted strings. Accepts formats with and without
|
|
* hyphen/colon separators and correctly parses zoning info.
|
|
*/
|
|
var DateFormatter = function (opts) {
|
|
this.opts = {}
|
|
this.setOpts(opts)
|
|
}
|
|
|
|
/**
|
|
* Default options for DateFormatter
|
|
* @static
|
|
* @see DateFormatter#setOpts
|
|
*/
|
|
DateFormatter.DEFAULT_OPTIONS = {
|
|
colons: true
|
|
, hyphens: false
|
|
, local: true
|
|
, ms: false
|
|
, offset: false
|
|
}
|
|
|
|
/**
|
|
* Regular Expression that disects ISO 8601 formatted strings into
|
|
* an array of parts.
|
|
* @static
|
|
*/
|
|
DateFormatter.ISO8601 = new RegExp(
|
|
'([0-9]{4})([-]?([0-9]{2}))([-]?([0-9]{2}))'
|
|
+ '(T([0-9]{2})(((:?([0-9]{2}))?((:?([0-9]{2}))?(\.([0-9]+))?))?)'
|
|
+ '(Z|([+-]([0-9]{2}(:?([0-9]{2}))?)))?)?'
|
|
)
|
|
|
|
/**
|
|
* Sets options for encoding Date objects to ISO8601 strings.
|
|
* Omitting the 'opts' argument will reset all options to the default.
|
|
*
|
|
* @param {Object} opts - Options (optional)
|
|
* @param {Boolean} opts.colons - Enable/disable formatting the time portion
|
|
* with a colon as separator (default: true)
|
|
* @param {Boolean} opts.hyphens - Enable/disable formatting the date portion
|
|
* with a hyphen as separator (default: false)
|
|
* @param {Boolean} opts.local - Encode as local time instead of UTC
|
|
* (default: true)
|
|
* @param {Boolean} opts.ms - Enable/Disable output of milliseconds
|
|
* (default: false)
|
|
* @param {Boolean} opts.offset - Enable/Disable output of UTC offset
|
|
* (default: false)
|
|
*/
|
|
DateFormatter.prototype.setOpts = function (opts) {
|
|
if (!opts) opts = DateFormatter.DEFAULT_OPTIONS
|
|
|
|
var ctx = this
|
|
Object.keys(DateFormatter.DEFAULT_OPTIONS).forEach(function (k) {
|
|
ctx.opts[k] = opts.hasOwnProperty(k) ?
|
|
opts[k] : DateFormatter.DEFAULT_OPTIONS[k]
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Converts a date time stamp following the ISO8601 format to a JavaScript Date
|
|
* object.
|
|
*
|
|
* @param {String} time - String representation of timestamp.
|
|
* @return {Date} - Date object from timestamp.
|
|
*/
|
|
DateFormatter.prototype.decodeIso8601 = function(time) {
|
|
var dateParts = time.toString().match(DateFormatter.ISO8601)
|
|
if (!dateParts) {
|
|
throw new Error('Expected a ISO8601 datetime but got \'' + time + '\'')
|
|
}
|
|
|
|
var date = [
|
|
[dateParts[1], dateParts[3] || '01', dateParts[5] || '01'].join('-')
|
|
, 'T'
|
|
, [
|
|
dateParts[7] || '00'
|
|
, dateParts[11] || '00'
|
|
, dateParts[14] || '00'
|
|
].join(':')
|
|
, '.'
|
|
, dateParts[16] || '000'
|
|
].join('')
|
|
|
|
date += (dateParts[17] !== undefined) ?
|
|
dateParts[17] +
|
|
((dateParts[19] && dateParts[20] === undefined) ? '00' : '') :
|
|
DateFormatter.formatCurrentOffset(new Date(date))
|
|
|
|
return new Date(date)
|
|
}
|
|
|
|
/**
|
|
* Converts a JavaScript Date object to an ISO8601 timestamp.
|
|
*
|
|
* @param {Date} date - Date object.
|
|
* @return {String} - String representation of timestamp.
|
|
*/
|
|
DateFormatter.prototype.encodeIso8601 = function(date) {
|
|
var parts = this.opts.local ?
|
|
DateFormatter.getLocalDateParts(date) :
|
|
DateFormatter.getUTCDateParts(date)
|
|
|
|
return [
|
|
[parts[0],parts[1],parts[2]].join(this.opts.hyphens ? '-' : '')
|
|
, 'T'
|
|
, [parts[3],parts[4],parts[5]].join(this.opts.colons ? ':' : '')
|
|
, (this.opts.ms) ? '.' + parts[6] : ''
|
|
, (this.opts.local) ? ((this.opts.offset) ?
|
|
DateFormatter.formatCurrentOffset(date) : '') : 'Z'
|
|
].join('')
|
|
}
|
|
|
|
/**
|
|
* Helper function to get an array of zero-padded date parts,
|
|
* in UTC
|
|
*
|
|
* @param {Date} date - Date Object
|
|
* @return {String[]}
|
|
*/
|
|
DateFormatter.getUTCDateParts = function (date) {
|
|
return [
|
|
date.getUTCFullYear()
|
|
, DateFormatter.zeroPad(date.getUTCMonth()+1,2)
|
|
, DateFormatter.zeroPad(date.getUTCDate(),2)
|
|
, DateFormatter.zeroPad(date.getUTCHours(), 2)
|
|
, DateFormatter.zeroPad(date.getUTCMinutes(), 2)
|
|
, DateFormatter.zeroPad(date.getUTCSeconds(), 2)
|
|
, DateFormatter.zeroPad(date.getUTCMilliseconds(), 3)]
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function to get an array of zero-padded date parts,
|
|
* in the local time zone
|
|
*
|
|
* @param {Date} date - Date Object
|
|
* @return {String[]}
|
|
*/
|
|
DateFormatter.getLocalDateParts = function (date) {
|
|
return [
|
|
date.getFullYear()
|
|
, DateFormatter.zeroPad(date.getMonth()+1,2)
|
|
, DateFormatter.zeroPad(date.getDate(),2)
|
|
, DateFormatter.zeroPad(date.getHours(), 2)
|
|
, DateFormatter.zeroPad(date.getMinutes(), 2)
|
|
, DateFormatter.zeroPad(date.getSeconds(), 2)
|
|
, DateFormatter.zeroPad(date.getMilliseconds(), 3)]
|
|
}
|
|
|
|
/**
|
|
* Helper function to pad the digits with 0s to meet date formatting
|
|
* requirements.
|
|
*
|
|
* @param {Number} digit - The number to pad.
|
|
* @param {Number} length - Length of digit string, prefix with 0s if not
|
|
* already length.
|
|
* @return {String} - String with the padded digit
|
|
*/
|
|
DateFormatter.zeroPad = function (digit, length) {
|
|
var padded = '' + digit
|
|
while (padded.length < length) {
|
|
padded = '0' + padded
|
|
}
|
|
|
|
return padded
|
|
}
|
|
|
|
/**
|
|
* Helper function to get the current timezone to default decoding to
|
|
* rather than UTC. (for backward compatibility)
|
|
*
|
|
* @return {String} - in the format /Z|[+-]\d{2}:\d{2}/
|
|
*/
|
|
DateFormatter.formatCurrentOffset = function (d) {
|
|
var offset = (d || new Date()).getTimezoneOffset()
|
|
return (offset === 0) ? 'Z' : [
|
|
(offset < 0) ? '+' : '-'
|
|
, DateFormatter.zeroPad(Math.abs(Math.floor(offset/60)),2)
|
|
, ':'
|
|
, DateFormatter.zeroPad(Math.abs(offset%60),2)
|
|
].join('')
|
|
}
|
|
|
|
// export an instance of DateFormatter only.
|
|
module.exports = new DateFormatter()
|
|
|
|
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898211, function(require, module, exports) {
|
|
var CustomType = module.exports = function(raw) {
|
|
this.raw = raw
|
|
}
|
|
|
|
CustomType.prototype.serialize = function(xml) {
|
|
return xml.ele(this.tagName).txt(this.raw)
|
|
}
|
|
|
|
CustomType.prototype.tagName = 'customType'
|
|
|
|
|
|
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898212, function(require, module, exports) {
|
|
var sax = require('sax')
|
|
, dateFormatter = require('./date_formatter')
|
|
|
|
var Deserializer = function(encoding) {
|
|
this.type = null
|
|
this.responseType = null
|
|
this.stack = []
|
|
this.marks = []
|
|
this.data = []
|
|
this.methodname = null
|
|
this.encoding = encoding || 'utf8'
|
|
this.value = false
|
|
this.callback = null
|
|
this.error = null
|
|
|
|
this.parser = sax.createStream()
|
|
this.parser.on('opentag', this.onOpentag.bind(this))
|
|
this.parser.on('closetag', this.onClosetag.bind(this))
|
|
this.parser.on('text', this.onText.bind(this))
|
|
this.parser.on('cdata', this.onCDATA.bind(this))
|
|
this.parser.on('end', this.onDone.bind(this))
|
|
this.parser.on('error', this.onError.bind(this))
|
|
}
|
|
|
|
Deserializer.prototype.deserializeMethodResponse = function(stream, callback) {
|
|
var that = this
|
|
|
|
this.callback = function(error, result) {
|
|
if (error) {
|
|
callback(error)
|
|
}
|
|
else if (result.length > 1) {
|
|
callback(new Error('Response has more than one param'))
|
|
}
|
|
else if (that.type !== 'methodresponse') {
|
|
callback(new Error('Not a method response'))
|
|
}
|
|
else if (!that.responseType) {
|
|
callback(new Error('Invalid method response'))
|
|
}
|
|
else {
|
|
callback(null, result[0])
|
|
}
|
|
}
|
|
|
|
stream.setEncoding(this.encoding)
|
|
stream.on('error', this.onError.bind(this))
|
|
stream.pipe(this.parser)
|
|
}
|
|
|
|
Deserializer.prototype.deserializeMethodCall = function(stream, callback) {
|
|
var that = this
|
|
|
|
this.callback = function(error, result) {
|
|
if (error) {
|
|
callback(error)
|
|
}
|
|
else if (that.type !== 'methodcall') {
|
|
callback(new Error('Not a method call'))
|
|
}
|
|
else if (!that.methodname) {
|
|
callback(new Error('Method call did not contain a method name'))
|
|
}
|
|
else {
|
|
callback(null, that.methodname, result)
|
|
}
|
|
}
|
|
|
|
stream.setEncoding(this.encoding)
|
|
stream.on('error', this.onError.bind(this))
|
|
stream.pipe(this.parser)
|
|
}
|
|
|
|
Deserializer.prototype.onDone = function() {
|
|
var that = this
|
|
|
|
if (!this.error) {
|
|
if (this.type === null || this.marks.length) {
|
|
this.callback(new Error('Invalid XML-RPC message'))
|
|
}
|
|
else if (this.responseType === 'fault') {
|
|
var createFault = function(fault) {
|
|
var error = new Error('XML-RPC fault' + (fault.faultString ? ': ' + fault.faultString : ''))
|
|
error.code = fault.faultCode
|
|
error.faultCode = fault.faultCode
|
|
error.faultString = fault.faultString
|
|
return error
|
|
}
|
|
this.callback(createFault(this.stack[0]))
|
|
}
|
|
else {
|
|
this.callback(undefined, this.stack)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO:
|
|
// Error handling needs a little thinking. There are two different kinds of
|
|
// errors:
|
|
// 1. Low level errors like network, stream or xml errors. These don't
|
|
// require special treatment. They only need to be forwarded. The IO
|
|
// is already stopped in these cases.
|
|
// 2. Protocol errors: Invalid tags, invalid values &c. These happen in
|
|
// our code and we should tear down the IO and stop parsing.
|
|
// Currently all errors end here. Guess I'll split it up.
|
|
Deserializer.prototype.onError = function(msg) {
|
|
if (!this.error) {
|
|
if (typeof msg === 'string') {
|
|
this.error = new Error(msg)
|
|
}
|
|
else {
|
|
this.error = msg
|
|
}
|
|
this.callback(this.error)
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.push = function(value) {
|
|
this.stack.push(value)
|
|
}
|
|
|
|
//==============================================================================
|
|
// SAX Handlers
|
|
//==============================================================================
|
|
|
|
Deserializer.prototype.onOpentag = function(node) {
|
|
if (node.name === 'ARRAY' || node.name === 'STRUCT') {
|
|
this.marks.push(this.stack.length)
|
|
}
|
|
this.data = []
|
|
this.value = (node.name === 'VALUE')
|
|
}
|
|
|
|
Deserializer.prototype.onText = function(text) {
|
|
this.data.push(text)
|
|
}
|
|
|
|
Deserializer.prototype.onCDATA = function(cdata) {
|
|
this.data.push(cdata)
|
|
}
|
|
|
|
Deserializer.prototype.onClosetag = function(el) {
|
|
var data = this.data.join('')
|
|
try {
|
|
switch(el) {
|
|
case 'BOOLEAN':
|
|
this.endBoolean(data)
|
|
break
|
|
case 'INT':
|
|
case 'I4':
|
|
this.endInt(data)
|
|
break
|
|
case 'I8':
|
|
this.endI8(data)
|
|
break
|
|
case 'DOUBLE':
|
|
this.endDouble(data)
|
|
break
|
|
case 'STRING':
|
|
case 'NAME':
|
|
this.endString(data)
|
|
break
|
|
case 'ARRAY':
|
|
this.endArray(data)
|
|
break
|
|
case 'STRUCT':
|
|
this.endStruct(data)
|
|
break
|
|
case 'BASE64':
|
|
this.endBase64(data)
|
|
break
|
|
case 'DATETIME.ISO8601':
|
|
this.endDateTime(data)
|
|
break
|
|
case 'VALUE':
|
|
this.endValue(data)
|
|
break
|
|
case 'PARAMS':
|
|
this.endParams(data)
|
|
break
|
|
case 'FAULT':
|
|
this.endFault(data)
|
|
break
|
|
case 'METHODRESPONSE':
|
|
this.endMethodResponse(data)
|
|
break
|
|
case 'METHODNAME':
|
|
this.endMethodName(data)
|
|
break
|
|
case 'METHODCALL':
|
|
this.endMethodCall(data)
|
|
break
|
|
case 'NIL':
|
|
this.endNil(data)
|
|
break
|
|
case 'DATA':
|
|
case 'PARAM':
|
|
case 'MEMBER':
|
|
// Ignored by design
|
|
break
|
|
default:
|
|
this.onError('Unknown XML-RPC tag \'' + el + '\'')
|
|
break
|
|
}
|
|
}
|
|
catch (e) {
|
|
this.onError(e)
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.endNil = function(data) {
|
|
this.push(null)
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endBoolean = function(data) {
|
|
if (data === '1') {
|
|
this.push(true)
|
|
}
|
|
else if (data === '0') {
|
|
this.push(false)
|
|
}
|
|
else {
|
|
throw new Error('Illegal boolean value \'' + data + '\'')
|
|
}
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endInt = function(data) {
|
|
var value = parseInt(data, 10)
|
|
if (isNaN(value)) {
|
|
throw new Error('Expected an integer but got \'' + data + '\'')
|
|
}
|
|
else {
|
|
this.push(value)
|
|
this.value = false
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.endDouble = function(data) {
|
|
var value = parseFloat(data)
|
|
if (isNaN(value)) {
|
|
throw new Error('Expected a double but got \'' + data + '\'')
|
|
}
|
|
else {
|
|
this.push(value)
|
|
this.value = false
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.endString = function(data) {
|
|
this.push(data)
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endArray = function(data) {
|
|
var mark = this.marks.pop()
|
|
this.stack.splice(mark, this.stack.length - mark, this.stack.slice(mark))
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endStruct = function(data) {
|
|
var mark = this.marks.pop()
|
|
, struct = {}
|
|
, items = this.stack.slice(mark)
|
|
, i = 0
|
|
|
|
for (; i < items.length; i += 2) {
|
|
struct[items[i]] = items[i + 1]
|
|
}
|
|
this.stack.splice(mark, this.stack.length - mark, struct)
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endBase64 = function(data) {
|
|
var buffer = new Buffer(data, 'base64')
|
|
this.push(buffer)
|
|
this.value = false
|
|
}
|
|
|
|
Deserializer.prototype.endDateTime = function(data) {
|
|
var date = dateFormatter.decodeIso8601(data)
|
|
this.push(date)
|
|
this.value = false
|
|
}
|
|
|
|
var isInteger = /^-?\d+$/
|
|
Deserializer.prototype.endI8 = function(data) {
|
|
if (!isInteger.test(data)) {
|
|
throw new Error('Expected integer (I8) value but got \'' + data + '\'')
|
|
}
|
|
else {
|
|
this.endString(data)
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.endValue = function(data) {
|
|
if (this.value) {
|
|
this.endString(data)
|
|
}
|
|
}
|
|
|
|
Deserializer.prototype.endParams = function(data) {
|
|
this.responseType = 'params'
|
|
}
|
|
|
|
Deserializer.prototype.endFault = function(data) {
|
|
this.responseType = 'fault'
|
|
}
|
|
|
|
Deserializer.prototype.endMethodResponse = function(data) {
|
|
this.type = 'methodresponse'
|
|
}
|
|
|
|
Deserializer.prototype.endMethodName = function(data) {
|
|
this.methodname = data
|
|
}
|
|
|
|
Deserializer.prototype.endMethodCall = function(data) {
|
|
this.type = 'methodcall'
|
|
}
|
|
|
|
module.exports = Deserializer
|
|
|
|
|
|
}, function(modId) { var map = {"./date_formatter":1738455898210}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898213, function(require, module, exports) {
|
|
/**
|
|
* Creates object for cookies manipulation on client side.
|
|
* Allows to parse server's response in order to get cookies and compose http request to transfer cookies to the server
|
|
* @constructor
|
|
*/
|
|
function Cookies() {
|
|
this.cookies = {}
|
|
}
|
|
|
|
Cookies.prototype = {
|
|
/**
|
|
* Obtains value of the cookie with specified name.
|
|
* This call checks expiration dates and does not return expired cookies.
|
|
* @param {String} name cookie name
|
|
* @return {String} cookie value or null
|
|
*/
|
|
get: function(name) {
|
|
var cookie = this.cookies[name]
|
|
if (cookie && this.checkNotExpired(name)) {
|
|
return this.cookies[name].value
|
|
}
|
|
return null
|
|
},
|
|
|
|
/**
|
|
* Sets cookie's value and optional options
|
|
* @param {String} name cookie's name
|
|
* @param {String} value value
|
|
* @param {Object} options with the following fields:
|
|
* - {Boolean} secure - is cookie secure or not (does not mean anything for now)
|
|
* - {Date} expires - cookie's expiration date. If specified then cookie will disappear after that date
|
|
*/
|
|
set: function(name, value, options) {
|
|
var cookie = typeof options == 'object'
|
|
? {value: value, expires: options.expires, secure: options.secure || false, new: options.new || false}
|
|
: {value: value}
|
|
if (this.checkNotExpired(name, cookie)) {
|
|
this.cookies[name] = cookie
|
|
}
|
|
},
|
|
|
|
// For testing purposes
|
|
getExpirationDate: function(name) {
|
|
return this.cookies[name] ? this.cookies[name].expires : null
|
|
},
|
|
|
|
// Internal function
|
|
checkNotExpired: function(name, cookie) {
|
|
if (typeof cookie === 'undefined') {
|
|
cookie = this.cookies[name]
|
|
}
|
|
var now = new Date()
|
|
if (cookie && cookie.expires && now > cookie.expires) {
|
|
delete this.cookies[name]
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
|
|
|
|
/**
|
|
* Parses headers from server's response for 'set-cookie' header and store cookie's values.
|
|
* Also parses expiration date
|
|
* @param headers
|
|
*/
|
|
parseResponse: function(headers) {
|
|
var cookies = headers['set-cookie']
|
|
if (cookies) {
|
|
cookies.forEach(function(c) {
|
|
var cookiesParams = c.split(';')
|
|
var cookiePair = cookiesParams.shift().split('=')
|
|
var options = {}
|
|
cookiesParams.forEach(function(param) {
|
|
param = param.trim()
|
|
if (param.toLowerCase().indexOf('expires') == 0) {
|
|
var date = param.split('=')[1].trim()
|
|
options.expires = new Date(date)
|
|
}
|
|
})
|
|
this.set(cookiePair[0].trim(), cookiePair[1].trim(), options)
|
|
}.bind(this))
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds cookies to the provided headers as array. Does nothing if there are no cookies stored.
|
|
* This call checks expiration dates and does not add expired cookies.
|
|
* @param headers
|
|
*/
|
|
composeRequest: function(headers) {
|
|
if (Object.keys(this.cookies).length == 0) {
|
|
return
|
|
}
|
|
headers['Cookie'] = this.toString()
|
|
},
|
|
|
|
|
|
/**
|
|
*
|
|
* @return {String} cookies as 'name=value' pairs joined by semicolon
|
|
*/
|
|
toString: function() {
|
|
return Object.keys(this.cookies)
|
|
.filter(this.checkNotExpired.bind(this))
|
|
.map(function(name) {
|
|
return name + '=' + this.cookies[name].value
|
|
}.bind(this)).join(';')
|
|
}
|
|
}
|
|
|
|
module.exports = Cookies
|
|
|
|
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
|
__DEFINE__(1738455898214, function(require, module, exports) {
|
|
var http = require('http')
|
|
, https = require('https')
|
|
, url = require('url')
|
|
, EventEmitter = require('events').EventEmitter
|
|
, Serializer = require('./serializer')
|
|
, Deserializer = require('./deserializer')
|
|
|
|
/**
|
|
* Creates a new Server object. Also creates an HTTP server to start listening
|
|
* for XML-RPC method calls. Will emit an event with the XML-RPC call's method
|
|
* name when receiving a method call.
|
|
*
|
|
* @constructor
|
|
* @param {Object|String} options - The HTTP server options. Either a URI string
|
|
* (e.g. 'http://localhost:9090') or an object
|
|
* with fields:
|
|
* - {String} host - (optional)
|
|
* - {Number} port
|
|
* @param {Boolean} isSecure - True if using https for making calls,
|
|
* otherwise false.
|
|
* @return {Server}
|
|
*/
|
|
function Server(options, isSecure, onListening) {
|
|
|
|
if (false === (this instanceof Server)) {
|
|
return new Server(options, isSecure)
|
|
}
|
|
onListening = onListening || function() {}
|
|
var that = this
|
|
|
|
// If a string URI is passed in, converts to URI fields
|
|
if (typeof options === 'string') {
|
|
options = url.parse(options)
|
|
options.host = options.hostname
|
|
options.path = options.pathname
|
|
}
|
|
|
|
function handleMethodCall(request, response) {
|
|
var deserializer = new Deserializer()
|
|
deserializer.deserializeMethodCall(request, function(error, methodName, params) {
|
|
if (Object.prototype.hasOwnProperty.call(that._events, methodName)) {
|
|
that.emit(methodName, null, params, function(error, value) {
|
|
var xml = null
|
|
if (error !== null) {
|
|
xml = Serializer.serializeFault(error)
|
|
}
|
|
else {
|
|
xml = Serializer.serializeMethodResponse(value)
|
|
}
|
|
response.writeHead(200, {'Content-Type': 'text/xml'})
|
|
response.end(xml)
|
|
})
|
|
}
|
|
else {
|
|
that.emit('NotFound', methodName, params)
|
|
response.writeHead(404)
|
|
response.end()
|
|
}
|
|
})
|
|
}
|
|
|
|
this.httpServer = isSecure ? https.createServer(options, handleMethodCall)
|
|
: http.createServer(handleMethodCall)
|
|
|
|
process.nextTick(function() {
|
|
this.httpServer.listen(options.port, options.host, onListening)
|
|
}.bind(this))
|
|
this.close = function(callback) {
|
|
this.httpServer.once('close', callback)
|
|
this.httpServer.close()
|
|
}.bind(this)
|
|
}
|
|
|
|
// Inherit from EventEmitter to emit and listen
|
|
Server.prototype.__proto__ = EventEmitter.prototype
|
|
|
|
module.exports = Server
|
|
|
|
|
|
}, function(modId) { var map = {"./serializer":1738455898209,"./deserializer":1738455898212}; return __REQUIRE__(map[modId], modId); })
|
|
return __REQUIRE__(1738455898207);
|
|
})()
|
|
//miniprogram-npm-outsideDeps=["http","https","url","xmlbuilder","sax","events"]
|
|
//# sourceMappingURL=index.js.map
|