uri: rewrite.
This commit is contained in:
parent
ac5aa5b04c
commit
cbb2fa5c54
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user