https
express server läuft jetzt mit https
This commit is contained in:
51
express-server/node_modules/greenlock/lib/community.js
generated
vendored
Normal file
51
express-server/node_modules/greenlock/lib/community.js
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
function addCommunityMember(opts) {
|
||||
// { name, version, email, domains, action, communityMember, telemetry }
|
||||
setTimeout(function () {
|
||||
var https = require('https');
|
||||
var req = https.request({
|
||||
hostname: 'api.ppl.family'
|
||||
, port: 443
|
||||
, path: '/api/ppl.family/public/list'
|
||||
, method: 'POST'
|
||||
, headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}, function (err, resp) {
|
||||
if (err) { return; }
|
||||
resp.on('data', function () {});
|
||||
});
|
||||
var os = require('os');
|
||||
var data = {
|
||||
address: opts.email
|
||||
// greenlock-security is transactional and security only
|
||||
, list: opts.communityMember ? (opts.name + '@ppl.family') : 'greenlock-security@ppl.family'
|
||||
, action: opts.action // reg | renew
|
||||
, package: opts.name
|
||||
// hashed for privacy, but so we can still get some telemetry and inform users
|
||||
// if abnormal things are happening (like several registrations for the same domain each day)
|
||||
, domain: (opts.domains||[]).map(function (d) {
|
||||
return require('crypto').createHash('sha1').update(d).digest('base64')
|
||||
.replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '');
|
||||
}).join(',')
|
||||
};
|
||||
if (false !== opts.telemetry) {
|
||||
data.arch = process.arch || os.arch();
|
||||
data.platform = process.platform || os.platform();
|
||||
data.release = os.release();
|
||||
data.version = opts.version;
|
||||
data.node = process.version;
|
||||
}
|
||||
req.write(JSON.stringify(data, 2, null));
|
||||
req.end();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
module.exports.add = addCommunityMember;
|
||||
|
||||
if (require.main === module) {
|
||||
//addCommunityMember('greenlock-express.js', 'reg', 'coolaj86+test42@gmail.com', ['coolaj86.com'], true);
|
||||
//addCommunityMember('greenlock.js', 'reg', 'coolaj86+test37@gmail.com', ['oneal.im'], false);
|
||||
//addCommunityMember('greenlock.js', 'reg', 'coolaj86+test11@gmail.com', ['ppl.family'], true);
|
||||
}
|
459
express-server/node_modules/greenlock/lib/core.js
generated
vendored
Normal file
459
express-server/node_modules/greenlock/lib/core.js
generated
vendored
Normal file
@ -0,0 +1,459 @@
|
||||
'use strict';
|
||||
|
||||
var PromiseA;
|
||||
try {
|
||||
PromiseA = require('bluebird');
|
||||
} catch(e) {
|
||||
PromiseA = global.Promise;
|
||||
}
|
||||
var util = require('util');
|
||||
function promisifyAll(obj) {
|
||||
var aobj = {};
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
if ('function' === typeof obj[key]) {
|
||||
aobj[key] = obj[key];
|
||||
aobj[key + 'Async'] = util.promisify(obj[key]);
|
||||
}
|
||||
});
|
||||
return aobj;
|
||||
}
|
||||
|
||||
function _log(debug) {
|
||||
if (debug) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.shift();
|
||||
args.unshift("[greenlock/lib/core.js]");
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.create = function (gl) {
|
||||
var utils = require('./utils');
|
||||
var RSA = promisifyAll(require('rsa-compat').RSA);
|
||||
var log = gl.log || _log; // allow custom log
|
||||
var pendingRegistrations = {};
|
||||
|
||||
var core = {
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
getAcmeUrlsAsync: function (args) {
|
||||
var now = Date.now();
|
||||
|
||||
// TODO check response header on request for cache time
|
||||
if ((now - gl._ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) {
|
||||
return PromiseA.resolve(gl._ipc.acmeUrls);
|
||||
}
|
||||
|
||||
return gl.acme.getAcmeUrlsAsync(args.server).then(function (data) {
|
||||
gl._ipc.acmeUrlsUpdatedAt = Date.now();
|
||||
gl._ipc.acmeUrls = data;
|
||||
|
||||
return gl._ipc.acmeUrls;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// The Main Enchilada
|
||||
//
|
||||
|
||||
//
|
||||
// Accounts
|
||||
//
|
||||
, accounts: {
|
||||
// Accounts
|
||||
registerAsync: function (args) {
|
||||
var err;
|
||||
var copy = utils.merge(args, gl);
|
||||
var disagreeTos;
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
disagreeTos = (!args.agreeTos && 'undefined' !== typeof args.agreeTos);
|
||||
if (!args.email || disagreeTos || (parseInt(args.rsaKeySize, 10) < 2048)) {
|
||||
err = new Error(
|
||||
"In order to register an account both 'email' and 'agreeTos' must be present"
|
||||
+ " and 'rsaKeySize' must be 2048 or greater."
|
||||
);
|
||||
err.code = 'E_ARGS';
|
||||
return PromiseA.reject(err);
|
||||
}
|
||||
|
||||
return utils.testEmail(args.email).then(function () {
|
||||
var promise = gl.store.accounts.checkKeypairAsync(args).then(function (keypair) {
|
||||
if (keypair) {
|
||||
return RSA.import(keypair);
|
||||
}
|
||||
|
||||
if (args.accountKeypair) {
|
||||
return gl.store.accounts.setKeypairAsync(args, RSA.import(args.accountKeypair));
|
||||
}
|
||||
|
||||
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
||||
return RSA.generateKeypairAsync(keypairOpts).then(function (keypair) {
|
||||
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
||||
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
||||
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
||||
return gl.store.accounts.setKeypairAsync(args, keypair);
|
||||
});
|
||||
});
|
||||
|
||||
return promise.then(function (keypair) {
|
||||
// Note: the ACME urls are always fetched fresh on purpose
|
||||
// TODO is this the right place for this?
|
||||
return core.getAcmeUrlsAsync(args).then(function (urls) {
|
||||
args._acmeUrls = urls;
|
||||
|
||||
return gl.acme.registerNewAccountAsync({
|
||||
email: args.email
|
||||
, newRegUrl: args._acmeUrls.newReg
|
||||
, newAuthzUrl: args._acmeUrls.newAuthz
|
||||
, agreeToTerms: function (tosUrl, agreeCb) {
|
||||
if (true === args.agreeTos || tosUrl === args.agreeTos || tosUrl === gl.agreeToTerms) {
|
||||
agreeCb(null, tosUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// args.email = email; // already there
|
||||
// args.domains = domains // already there
|
||||
args.tosUrl = tosUrl;
|
||||
gl.agreeToTerms(args, agreeCb);
|
||||
}
|
||||
, accountKeypair: keypair
|
||||
|
||||
, debug: gl.debug || args.debug
|
||||
}).then(function (receipt) {
|
||||
var reg = {
|
||||
keypair: keypair
|
||||
, receipt: receipt
|
||||
, email: args.email
|
||||
, newRegUrl: args._acmeUrls.newReg
|
||||
, newAuthzUrl: args._acmeUrls.newAuthz
|
||||
};
|
||||
|
||||
// TODO move templating of arguments to right here?
|
||||
return gl.store.accounts.setAsync(args, reg).then(function (account) {
|
||||
// should now have account.id and account.accountId
|
||||
args.account = account;
|
||||
args.accountId = account.id;
|
||||
return account;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Accounts
|
||||
, getAsync: function (args) {
|
||||
return core.accounts.checkAsync(args).then(function (account) {
|
||||
if (account) {
|
||||
return account;
|
||||
} else {
|
||||
return core.accounts.registerAsync(args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Accounts
|
||||
, checkAsync: function (args) {
|
||||
var requiredArgs = ['accountId', 'email', 'domains', 'domain'];
|
||||
if (!requiredArgs.some(function (key) { return -1 !== Object.keys(args).indexOf(key); })) {
|
||||
return PromiseA.reject(new Error(
|
||||
"In order to register or retrieve an account one of '" + requiredArgs.join("', '") + "' must be present"
|
||||
));
|
||||
}
|
||||
|
||||
var copy = utils.merge(args, gl);
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
return gl.store.accounts.checkAsync(args).then(function (account) {
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
|
||||
args.account = account;
|
||||
args.accountId = account.id;
|
||||
|
||||
return account;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
, certificates: {
|
||||
// Certificates
|
||||
registerAsync: function (args) {
|
||||
var err;
|
||||
var challengeDefaults = gl['_challengeOpts_' + (args.challengeType || gl.challengeType)] || {};
|
||||
var copy = utils.merge(args, challengeDefaults || {});
|
||||
copy = utils.merge(copy, gl);
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
if (!Array.isArray(args.domains)) {
|
||||
return PromiseA.reject(new Error('args.domains should be an array of domains'));
|
||||
}
|
||||
|
||||
if (!(args.domains.length && args.domains.every(utils.isValidDomain))) {
|
||||
// NOTE: this library can't assume to handle the http loopback
|
||||
// (or dns-01 validation may be used)
|
||||
// so we do not check dns records or attempt a loopback here
|
||||
err = new Error("invalid domain name(s): '" + args.domains + "'");
|
||||
err.code = "INVALID_DOMAIN";
|
||||
return PromiseA.reject(err);
|
||||
}
|
||||
|
||||
// If a previous request to (re)register a certificate is already underway we need
|
||||
// to return the same promise created before rather than registering things twice.
|
||||
// I'm not 100% sure how to properly handle the case where someone registers domain
|
||||
// lists with some but not all elements common, nor am I sure that's even a case that
|
||||
// is allowed to happen anyway. But for now we act like the list is completely the
|
||||
// same if any elements are the same.
|
||||
var promise;
|
||||
args.domains.some(function (name) {
|
||||
if (pendingRegistrations.hasOwnProperty(name)) {
|
||||
promise = pendingRegistrations[name];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (promise) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
promise = core.certificates._runRegistration(args);
|
||||
|
||||
// Now that the registration is actually underway we need to make sure any subsequent
|
||||
// registration attempts return the same promise until it is completed (but not after
|
||||
// it is completed).
|
||||
args.domains.forEach(function (name) {
|
||||
pendingRegistrations[name] = promise;
|
||||
});
|
||||
function clearPending() {
|
||||
args.domains.forEach(function (name) {
|
||||
delete pendingRegistrations[name];
|
||||
});
|
||||
}
|
||||
promise.then(clearPending, clearPending);
|
||||
|
||||
return promise;
|
||||
}
|
||||
, _runRegistration: function (args) {
|
||||
// TODO renewal cb
|
||||
// accountId and or email
|
||||
return core.accounts.getAsync(args).then(function (account) {
|
||||
args.account = account;
|
||||
|
||||
var promise = gl.store.certificates.checkKeypairAsync(args).then(function (keypair) {
|
||||
if (keypair) {
|
||||
return RSA.import(keypair);
|
||||
}
|
||||
|
||||
if (args.domainKeypair) {
|
||||
return gl.store.certificates.setKeypairAsync(args, RSA.import(args.domainKeypair));
|
||||
}
|
||||
|
||||
var keypairOpts = { bitlen: args.rsaKeySize, exp: 65537, public: true, pem: true };
|
||||
return RSA.generateKeypairAsync(keypairOpts).then(function (keypair) {
|
||||
keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
|
||||
keypair.publicKeyPem = RSA.exportPublicPem(keypair);
|
||||
keypair.privateKeyJwk = RSA.exportPrivateJwk(keypair);
|
||||
return gl.store.certificates.setKeypairAsync(args, keypair);
|
||||
});
|
||||
});
|
||||
|
||||
return promise.then(function (domainKeypair) {
|
||||
args.domainKeypair = domainKeypair;
|
||||
//args.registration = domainKey;
|
||||
|
||||
// Note: the ACME urls are always fetched fresh on purpose
|
||||
// TODO is this the right place for this?
|
||||
return core.getAcmeUrlsAsync(args).then(function (urls) {
|
||||
args._acmeUrls = urls;
|
||||
|
||||
var certReq = {
|
||||
debug: args.debug || gl.debug
|
||||
|
||||
, newAuthzUrl: args._acmeUrls.newAuthz
|
||||
, newCertUrl: args._acmeUrls.newCert
|
||||
|
||||
, accountKeypair: RSA.import(account.keypair)
|
||||
, domainKeypair: domainKeypair
|
||||
, domains: args.domains
|
||||
, challengeType: args.challengeType
|
||||
};
|
||||
|
||||
//
|
||||
// IMPORTANT
|
||||
//
|
||||
// setChallenge and removeChallenge are handed defaults
|
||||
// instead of args because getChallenge does not have
|
||||
// access to args
|
||||
// (args is per-request, defaults is per instance)
|
||||
//
|
||||
// Each of these fires individually for each domain,
|
||||
// even though the certificate on the whole may have many domains
|
||||
//
|
||||
certReq.setChallenge = function (domain, key, value, done) {
|
||||
log(args.debug, "setChallenge called for '" + domain + "'");
|
||||
var copy = utils.merge({ domains: [domain] }, args);
|
||||
copy = utils.merge(copy, gl);
|
||||
utils.tplCopy(copy);
|
||||
|
||||
// TODO need to save challengeType
|
||||
gl.challenges[args.challengeType].set(copy, domain, key, value, done);
|
||||
};
|
||||
certReq.removeChallenge = function (domain, key, done) {
|
||||
log(args.debug, "removeChallenge called for '" + domain + "'");
|
||||
var copy = utils.merge({ domains: [domain] }, gl);
|
||||
utils.tplCopy(copy);
|
||||
|
||||
gl.challenges[args.challengeType].remove(copy, domain, key, done);
|
||||
};
|
||||
|
||||
log(args.debug, 'calling greenlock.acme.getCertificateAsync', certReq.domains);
|
||||
|
||||
return gl.acme.getCertificateAsync(certReq).then(utils.attachCertInfo);
|
||||
});
|
||||
}).then(function (results) {
|
||||
// { cert, chain, privkey /*TODO, subject, altnames, issuedAt, expiresAt */ }
|
||||
|
||||
// args.certs.privkey = RSA.exportPrivatePem(options.domainKeypair);
|
||||
args.certs = results;
|
||||
// args.pems is deprecated
|
||||
args.pems = results;
|
||||
return gl.store.certificates.setAsync(args).then(function () {
|
||||
return results;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// Certificates
|
||||
, renewAsync: function (args, certs) {
|
||||
var renewableAt = core.certificates._getRenewableAt(args, certs);
|
||||
var err;
|
||||
//var halfLife = (certs.expiresAt - certs.issuedAt) / 2;
|
||||
//var renewable = (Date.now() - certs.issuedAt) > halfLife;
|
||||
|
||||
log(args.debug, "(Renew) Expires At", new Date(certs.expiresAt).toISOString());
|
||||
log(args.debug, "(Renew) Renewable At", new Date(renewableAt).toISOString());
|
||||
|
||||
if (!args.duplicate && Date.now() < renewableAt) {
|
||||
err = new Error(
|
||||
"[ERROR] Certificate issued at '"
|
||||
+ new Date(certs.issuedAt).toISOString() + "' and expires at '"
|
||||
+ new Date(certs.expiresAt).toISOString() + "'. Ignoring renewal attempt until '"
|
||||
+ new Date(renewableAt).toISOString() + "'. Set { duplicate: true } to force."
|
||||
);
|
||||
err.code = 'E_NOT_RENEWABLE';
|
||||
return PromiseA.reject(err);
|
||||
}
|
||||
|
||||
// Either the cert has entered its renewal period
|
||||
// or we're forcing a refresh via 'dupliate: true'
|
||||
log(args.debug, "Renewing!");
|
||||
|
||||
if (!args.domains || !args.domains.length) {
|
||||
args.domains = args.servernames || [certs.subject].concat(certs.altnames);
|
||||
}
|
||||
|
||||
return core.certificates.registerAsync(args);
|
||||
}
|
||||
// Certificates
|
||||
, _isRenewable: function (args, certs) {
|
||||
var renewableAt = core.certificates._getRenewableAt(args, certs);
|
||||
|
||||
log(args.debug, "Check Expires At", new Date(certs.expiresAt).toISOString());
|
||||
log(args.debug, "Check Renewable At", new Date(renewableAt).toISOString());
|
||||
|
||||
if (args.duplicate || Date.now() >= renewableAt) {
|
||||
log(args.debug, "certificates are renewable");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
, _getRenewableAt: function (args, certs) {
|
||||
return certs.expiresAt - (args.renewWithin || gl.renewWithin);
|
||||
}
|
||||
, checkAsync: function (args) {
|
||||
var copy = utils.merge(args, gl);
|
||||
utils.tplCopy(copy);
|
||||
|
||||
// returns pems
|
||||
return gl.store.certificates.checkAsync(copy).then(function (cert) {
|
||||
if (cert) {
|
||||
log(args.debug, 'checkAsync found existing certificates');
|
||||
return utils.attachCertInfo(cert);
|
||||
}
|
||||
|
||||
log(args.debug, 'checkAsync failed to find certificates');
|
||||
return null;
|
||||
});
|
||||
}
|
||||
// Certificates
|
||||
, getAsync: function (args) {
|
||||
var copy = utils.merge(args, gl);
|
||||
args = utils.tplCopy(copy);
|
||||
|
||||
return core.certificates.checkAsync(args).then(function (certs) {
|
||||
if (!certs) {
|
||||
// There is no cert available
|
||||
if (false !== args.securityUpdates && !args._communityMemberAdded) {
|
||||
try {
|
||||
// We will notify all greenlock users of mandatory and security updates
|
||||
// We'll keep track of versions and os so we can make sure things work well
|
||||
// { name, version, email, domains, action, communityMember, telemetry }
|
||||
require('./community').add({
|
||||
name: args._communityPackage
|
||||
, version: args._communityPackageVersion
|
||||
, email: args.email
|
||||
, domains: args.domains || args.servernames
|
||||
, action: 'reg'
|
||||
, communityMember: args.communityMember
|
||||
, telemetry: args.telemetry
|
||||
});
|
||||
} catch(e) { /* ignore */ }
|
||||
args._communityMemberAdded = true;
|
||||
}
|
||||
return core.certificates.registerAsync(args);
|
||||
}
|
||||
|
||||
if (core.certificates._isRenewable(args, certs)) {
|
||||
// it's time to renew the available cert
|
||||
if (false !== args.securityUpdates && !args._communityMemberAdded) {
|
||||
try {
|
||||
// We will notify all greenlock users of mandatory and security updates
|
||||
// We'll keep track of versions and os so we can make sure things work well
|
||||
// { name, version, email, domains, action, communityMember, telemetry }
|
||||
require('./community').add({
|
||||
name: args._communityPackage
|
||||
, version: args._communityPackageVersion
|
||||
, email: args.email
|
||||
, domains: args.domains || args.servernames
|
||||
, action: 'renew'
|
||||
, communityMember: args.communityMember
|
||||
, telemetry: args.telemetry
|
||||
});
|
||||
} catch(e) { /* ignore */ }
|
||||
args._communityMemberAdded = true;
|
||||
}
|
||||
certs.renewing = core.certificates.renewAsync(args, certs);
|
||||
if (args.waitForRenewal) {
|
||||
return certs.renewing;
|
||||
}
|
||||
}
|
||||
|
||||
// return existing unexpired (although potentially stale) certificates when available
|
||||
// there will be an additional .renewing property if the certs are being asynchronously renewed
|
||||
return certs;
|
||||
}).then(function (results) {
|
||||
// returns pems
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return core;
|
||||
};
|
68
express-server/node_modules/greenlock/lib/middleware.js
generated
vendored
Normal file
68
express-server/node_modules/greenlock/lib/middleware.js
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
function _log(debug) {
|
||||
if (debug) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.shift();
|
||||
args.unshift("[greenlock/lib/middleware.js]");
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.create = function (gl) {
|
||||
if (!gl.challenges['http-01'] || !gl.challenges['http-01'].get) {
|
||||
throw new Error("middleware requires challenge plugin with get method");
|
||||
}
|
||||
var log = gl.log || _log;
|
||||
|
||||
log(gl.debug, "created middleware");
|
||||
return function (_app) {
|
||||
if (_app && 'function' !== typeof _app) {
|
||||
throw new Error("use greenlock.middleware() or greenlock.middleware(function (req, res) {})");
|
||||
}
|
||||
var prefix = gl.acmeChallengePrefix || '/.well-known/acme-challenge/';
|
||||
|
||||
return function (req, res, next) {
|
||||
if (0 !== req.url.indexOf(prefix)) {
|
||||
log(gl.debug, "no match, skipping middleware");
|
||||
if ('function' === typeof _app) {
|
||||
_app(req, res, next);
|
||||
}
|
||||
else if ('function' === typeof next) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.statusCode = 500;
|
||||
res.end("[500] Developer Error: app.use('/', greenlock.middleware()) or greenlock.middleware(app)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log(gl.debug, "this must be tinder, 'cuz it's a match!");
|
||||
|
||||
var token = req.url.slice(prefix.length);
|
||||
var hostname = req.hostname || (req.headers.host || '').toLowerCase().replace(/:.*/, '');
|
||||
|
||||
log(gl.debug, "hostname", hostname, "token", token);
|
||||
|
||||
var copy = utils.merge({ domains: [ hostname ] }, gl);
|
||||
copy = utils.tplCopy(copy);
|
||||
|
||||
// TODO tpl copy?
|
||||
// TODO need to restore challengeType
|
||||
gl.challenges['http-01'].get(copy, hostname, token, function (err, secret) {
|
||||
if (err || !token) {
|
||||
res.statusCode = 404;
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.end('{ "error": { "message": "Error: These aren\'t the tokens you\'re looking for. Move along." } }');
|
||||
return;
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
res.end(secret);
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
127
express-server/node_modules/greenlock/lib/utils.js
generated
vendored
Normal file
127
express-server/node_modules/greenlock/lib/utils.js
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var homeRe = new RegExp("^~(\\/|\\\\|\\" + path.sep + ")");
|
||||
// very basic check. Allows *.example.com.
|
||||
var re = /^(\*\.)?[a-zA-Z0-9\.\-]+$/;
|
||||
var punycode = require('punycode');
|
||||
var promisify = (require('util').promisify || require('bluebird').promisify);
|
||||
var dnsResolveMxAsync = promisify(require('dns').resolveMx);
|
||||
|
||||
module.exports.attachCertInfo = function (results) {
|
||||
// XXX Note: Parsing the certificate info comes at a great cost (~500kb)
|
||||
var getCertInfo = require('certpem').info;
|
||||
var certInfo = getCertInfo(results.cert);
|
||||
|
||||
// subject, altnames, issuedAt, expiresAt
|
||||
Object.keys(certInfo).forEach(function (key) {
|
||||
results[key] = certInfo[key];
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
module.exports.isValidDomain = function (domain) {
|
||||
if (re.test(domain)) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
domain = punycode.toASCII(domain);
|
||||
|
||||
if (re.test(domain)) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
module.exports.merge = function (/*defaults, args*/) {
|
||||
var allDefaults = Array.prototype.slice.apply(arguments);
|
||||
var args = allDefaults.shift();
|
||||
var copy = {};
|
||||
|
||||
allDefaults.forEach(function (defaults) {
|
||||
Object.keys(defaults).forEach(function (key) {
|
||||
copy[key] = defaults[key];
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(args).forEach(function (key) {
|
||||
copy[key] = args[key];
|
||||
});
|
||||
|
||||
return copy;
|
||||
};
|
||||
|
||||
module.exports.tplCopy = function (copy) {
|
||||
var homedir = require('os').homedir();
|
||||
var tplKeys;
|
||||
|
||||
copy.hostnameGet = function (copy) {
|
||||
return (copy.domains || [])[0] || copy.domain;
|
||||
};
|
||||
|
||||
Object.keys(copy).forEach(function (key) {
|
||||
var newName;
|
||||
if (!/Get$/.test(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
newName = key.replace(/Get$/, '');
|
||||
copy[newName] = copy[newName] || copy[key](copy);
|
||||
});
|
||||
|
||||
tplKeys = Object.keys(copy);
|
||||
tplKeys.sort(function (a, b) {
|
||||
return b.length - a.length;
|
||||
});
|
||||
|
||||
tplKeys.forEach(function (key) {
|
||||
if ('string' !== typeof copy[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy[key] = copy[key].replace(homeRe, homedir + path.sep);
|
||||
});
|
||||
|
||||
tplKeys.forEach(function (key) {
|
||||
if ('string' !== typeof copy[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
tplKeys.forEach(function (tplname) {
|
||||
if (!copy[tplname]) {
|
||||
// what can't be templated now may be templatable later
|
||||
return;
|
||||
}
|
||||
copy[key] = copy[key].replace(':' + tplname, copy[tplname]);
|
||||
});
|
||||
});
|
||||
|
||||
return copy;
|
||||
};
|
||||
|
||||
module.exports.testEmail = function (email) {
|
||||
var parts = (email||'').split('@');
|
||||
var err;
|
||||
|
||||
if (2 !== parts.length || !parts[0] || !parts[1]) {
|
||||
err = new Error("malformed email address '" + email + "'");
|
||||
err.code = 'E_EMAIL';
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
return dnsResolveMxAsync(parts[1]).then(function (records) {
|
||||
// records only returns when there is data
|
||||
if (!records.length) {
|
||||
throw new Error("sanity check fail: success, but no MX records returned");
|
||||
}
|
||||
return email;
|
||||
}, function (err) {
|
||||
if ('ENODATA' === err.code) {
|
||||
err = new Error("no MX records found for '" + parts[1] + "'");
|
||||
err.code = 'E_EMAIL';
|
||||
return Promise.reject(err);
|
||||
}
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user