uri: rewrite.

This commit is contained in:
Christopher Jeffrey 2016-08-24 01:16:48 -07:00
parent ac5aa5b04c
commit cbb2fa5c54
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -1,93 +1,192 @@
/** /**
* bitcoin uri parsing * uri.js - bitcoin uri parsing for bcoin
* @module uri
* @license
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin * https://github.com/bcoin-org/bcoin
*/ */
'use strict'; 'use strict';
var url = require('url'); var bcoin = require('../env');
var querystring = require('querystring');
var utils = require('../utils/utils'); var utils = require('../utils/utils');
var assert = utils.assert; var assert = utils.assert;
/** function URI(options) {
* Parse a bitcoin URI. if (!(this instanceof URI))
* @param {String} uri - Bitcoin URI. return new URI(options);
* @returns {ParsedURI}
* @throws on non-bitcoin uri
*/
exports.parse = function parse(uri) { this.address = new bcoin.address();
var data = url.parse(uri); this.amount = -1;
var query = querystring.parse(data.query || ''); this.label = null;
this.message = null;
this.request = null;
assert(data.protocol === 'bitcoin:', 'Not a bitcoin URI.'); if (options)
assert(data.hostname, 'No address present.'); this.fromOptions(options);
}
return { URI.prototype.fromOptions = function fromOptions(options) {
address: data.hostname, if (typeof options === 'string')
amount: query.amount ? utils.satoshi(query.amount) : null, return this.fromString(options);
label: query.label || null,
message: query.message || null,
request: query.r || null
};
};
/** if (options.address)
* Test whether an object is a bitcoin URI. this.address.fromOptions(options.address);
* @param {String} uri
* @returns {Boolean}
*/
exports.validate = function validate(uri) { if (options.amount != null) {
try { assert(utils.isNumber(options.amount));
exports.parse(uri); this.amount = options.amount;
return true;
} catch (e) {
return false;
} }
if (options.label) {
assert(typeof options.label === 'string');
this.label = options.label;
}
if (options.message) {
assert(typeof options.message === 'string');
this.message = options.message;
}
if (options.r) {
assert(typeof options.request === 'string');
this.request = options.r;
}
return this;
}; };
/** URI.fromOptions = function fromOptions(options) {
* Encode data as a bitcoin URI. return new URI().fromOptions(options);
* @param {ParsedURI|Base58Address} data/address };
* @param {Amount?} amount
* @returns {String} URI URI.prototype.fromString = function fromString(str) {
* @throws when no address provided var prefix, index, address, query;
assert(typeof str === 'string');
assert(str.length > 8, 'Not a bitcoin URI.');
prefix = str.substring(0, 8);
if (prefix !== 'bitcoin:')
throw new Error('Not a bitcoin URI.');
str = str.substring(8);
index = str.indexOf('?');
if (index === -1) {
address = str;
} else {
address = str.substring(0, index);
query = str.substring(index + 1);
}
this.address.fromBase58(address);
if (!query)
return this;
query = parsePairs(query);
if (query.amount)
this.amount = utils.satoshi(query.amount);
if (query.label)
this.label = query.label;
if (query.message)
this.message = query.message;
if (query.r)
this.request = query.r;
return this;
};
URI.fromString = function fromString(str) {
return new URI().fromString(str);
};
URI.prototype.toString = function toString() {
var str = 'bitcoin:';
var query = [];
str += this.address.toBase58();
if (this.amount !== -1)
query.push('amount=' + utils.btc(this.amount));
if (this.label)
query.push('label=' + escape(this.label));
if (this.message)
query.push('message=' + escape(this.message));
if (this.request)
query.push('r=' + escape(this.request));
if (query.length > 0)
str += '?' + query.join('&');
return str;
};
URI.prototype.inspect = function inspect() {
return '<URI: ' + this.toString() + '>';
};
/*
* Helpers
*/ */
exports.stringify = function stringify(address, amount) { function parsePairs(str) {
var query = {}; var parts = str.split('&');
var data = address; var data = {};
var uri; var i, index, pair, key, value;
if (typeof address === 'string') for (i = 0; i < parts.length; i++) {
data = { address: address, amount: amount }; pair = parts[i];
index = pair.indexOf('=');
assert(data.address, 'Address is required for a bitcoin URI.'); if (index === -1) {
key = pair;
value = '';
} else {
key = pair.substring(0, index);
value = pair.substring(index + 1);
}
uri = 'bitcoin:' + data.address; key = unescape(key);
if (data.amount) if (key.length === 0)
query.amount = utils.btc(data.amount); continue;
if (data.label) if (value.length > 0)
query.label = data.label; value = unescape(value);
if (data.message) data[key] = value;
query.message = data.message; }
if (data.request) return data;
query.r = data.request; }
query = querystring.stringify(query); function unescape(str) {
try {
str = decodeURIComponent(str).replace(/\+/g, ' ');
} finally {
return str.replace(/\0/g, '');
}
}
if (query.length === 0) function escape(str) {
return uri; try {
str = encodeURIComponent(str).replace(/%20/g, '+');
} finally {
return str;
}
}
return uri + '?' + query; /*
}; * Expose
*/
module.exports = URI;