| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var PromiseA = require('bluebird'); | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  | var RSA = PromiseA.promisifyAll(require('rsa-compat').RSA); | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  | var mkdirpAsync = PromiseA.promisify(require('mkdirp')); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | var path = require('path'); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:41:17 -08:00
										 |  |  | var fs = PromiseA.promisifyAll(require('fs')); | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  | var sfs = require('safe-replace'); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | var LE = require('../'); | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  | var LeCore = PromiseA.promisifyAll(require('letiny-core')); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:44:41 +00:00
										 |  |  | var Accounts = require('./accounts'); | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  | var merge = require('./common').merge; | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  | var tplCopy = require('./common').tplCopy; | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | var fetchFromConfigLiveDir = require('./common').fetchFromDisk; | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | var ipc = {}; // in-process cache
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getAcmeUrls(args) { | 
					
						
							|  |  |  |   var now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO check response header on request for cache time
 | 
					
						
							|  |  |  |   if ((now - ipc.acmeUrlsUpdatedAt) < 10 * 60 * 1000) { | 
					
						
							|  |  |  |     return PromiseA.resolve(ipc.acmeUrls); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  |   return LeCore.getAcmeUrlsAsync(args.server).then(function (data) { | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |     ipc.acmeUrlsUpdatedAt = Date.now(); | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  |     ipc.acmeUrls = data; | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return ipc.acmeUrls; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | function readRenewalConfig(args) { | 
					
						
							|  |  |  |   var pyconf = PromiseA.promisifyAll(require('pyconf')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return pyconf.readFileAsync(args.renewalPath).then(function (pyobj) { | 
					
						
							|  |  |  |     return pyobj; | 
					
						
							|  |  |  |   }, function () { | 
					
						
							|  |  |  |     return pyconf.readFileAsync(path.join(__dirname, 'renewal.conf.tpl')).then(function (pyobj) { | 
					
						
							|  |  |  |       return pyobj; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function writeRenewalConfig(args) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   function log() { | 
					
						
							|  |  |  |     if (args.debug) { | 
					
						
							|  |  |  |       console.log.apply(console, arguments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   var pyobj = args.pyobj; | 
					
						
							|  |  |  |   pyobj.checkpoints = parseInt(pyobj.checkpoints, 10) || 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var pyconf = PromiseA.promisifyAll(require('pyconf')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var certPath = args.certPath || pyobj.cert || path.join(liveDir, 'cert.pem'); | 
					
						
							|  |  |  |   var fullchainPath = args.fullchainPath || pyobj.fullchain || path.join(liveDir, 'fullchain.pem'); | 
					
						
							|  |  |  |   var chainPath = args.chainPath || pyobj.chain || path.join(liveDir, 'chain.pem'); | 
					
						
							|  |  |  |   var privkeyPath = args.privkeyPath || pyobj.privkey | 
					
						
							|  |  |  |     //|| args.domainPrivateKeyPath || args.domainKeyPath || pyobj.keyPath
 | 
					
						
							|  |  |  |     || path.join(liveDir, 'privkey.pem'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   log('[le/core.js] privkeyPath', privkeyPath); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   var updates = { | 
					
						
							|  |  |  |     account: args.account.id | 
					
						
							|  |  |  |   , configDir: args.configDir | 
					
						
							|  |  |  |   , domains: args.domains | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , email: args.email | 
					
						
							|  |  |  |   , tos: args.agreeTos && true | 
					
						
							|  |  |  |     // yes, it's an array. weird, right?
 | 
					
						
							|  |  |  |   , webrootPath: args.webrootPath && [args.webrootPath] || [] | 
					
						
							|  |  |  |   , server: args.server || args.acmeDiscoveryUrl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , privkey: privkeyPath | 
					
						
							|  |  |  |   , fullchain: fullchainPath | 
					
						
							|  |  |  |   , cert: certPath | 
					
						
							|  |  |  |   , chain: chainPath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   , http01Port: args.http01Port | 
					
						
							|  |  |  |   , keyPath: args.domainPrivateKeyPath || args.privkeyPath | 
					
						
							| 
									
										
										
										
											2016-08-05 09:02:51 -06:00
										 |  |  |   , rsaKeySize: args.rsaKeySize || 2048 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   , checkpoints: pyobj.checkpoints | 
					
						
							|  |  |  |     /* // TODO XXX what's the deal with these? they don't make sense | 
					
						
							|  |  |  |     // are they just old junk? or do they have a meaning that I don't know about?
 | 
					
						
							|  |  |  |   , fullchainPath: path.join(args.configDir, 'chain.pem') | 
					
						
							|  |  |  |   , certPath: path.join(args.configDir, 'cert.pem') | 
					
						
							|  |  |  |   , chainPath: path.join(args.configDir, 'chain.pem') | 
					
						
							|  |  |  |     */ // TODO XXX end | 
					
						
							|  |  |  |   , workDir: args.workDir | 
					
						
							|  |  |  |   , logsDir: args.logsDir | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   // final section is completely dynamic
 | 
					
						
							|  |  |  |   // :hostname = :webroot_path
 | 
					
						
							|  |  |  |   args.domains.forEach(function (hostname) { | 
					
						
							|  |  |  |     updates[hostname] = args.webrootPath; | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   // must write back to the original pyobject or
 | 
					
						
							|  |  |  |   // annotations will be lost
 | 
					
						
							|  |  |  |   Object.keys(updates).forEach(function (key) { | 
					
						
							|  |  |  |     pyobj[key] = updates[key]; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return mkdirpAsync(path.dirname(args.renewalPath)).then(function () { | 
					
						
							|  |  |  |     return pyconf.writeFileAsync(args.renewalPath, pyobj); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							| 
									
										
										
										
											2016-02-11 13:47:06 -05:00
										 |  |  |     // NOTE
 | 
					
						
							|  |  |  |     // writing twice seems to causes a bug,
 | 
					
						
							|  |  |  |     // so instead we re-read the file from the disk
 | 
					
						
							|  |  |  |     return pyconf.readFileAsync(args.renewalPath); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | function getOrCreateRenewal(args) { | 
					
						
							|  |  |  |   return readRenewalConfig(args).then(function (pyobj) { | 
					
						
							|  |  |  |     var minver = pyobj.checkpoints >= 0; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     args.pyobj = pyobj; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     if (!minver) { | 
					
						
							|  |  |  |       args.checkpoints = 0; | 
					
						
							|  |  |  |       pyobj.checkpoints = 0; | 
					
						
							|  |  |  |       return writeRenewalConfig(args); | 
					
						
							| 
									
										
										
										
											2015-12-20 03:31:05 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     // args.account.id = pyobj.account
 | 
					
						
							|  |  |  |     // args.configDir = args.configDir || pyobj.configDir;
 | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     args.checkpoints = pyobj.checkpoints; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     args.agreeTos = (args.agreeTos || pyobj.tos) && true; | 
					
						
							|  |  |  |     args.email = args.email || pyobj.email; | 
					
						
							|  |  |  |     args.domains = args.domains || pyobj.domains; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // yes, it's an array. weird, right?
 | 
					
						
							|  |  |  |     args.webrootPath = args.webrootPath || pyobj.webrootPath[0]; | 
					
						
							|  |  |  |     args.server = args.server || args.acmeDiscoveryUrl || pyobj.server; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     args.certPath = args.certPath || pyobj.cert; | 
					
						
							|  |  |  |     args.privkeyPath = args.privkeyPath || pyobj.privkey; | 
					
						
							|  |  |  |     args.chainPath = args.chainPath || pyobj.chain; | 
					
						
							|  |  |  |     args.fullchainPath = args.fullchainPath || pyobj.fullchain; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //, workDir: args.workDir
 | 
					
						
							|  |  |  |   //, logsDir: args.logsDir
 | 
					
						
							| 
									
										
										
										
											2016-08-05 09:02:51 -06:00
										 |  |  |     args.rsaKeySize = args.rsaKeySize || pyobj.rsaKeySize || 2048; | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     args.http01Port = args.http01Port || pyobj.http01Port; | 
					
						
							|  |  |  |     args.domainKeyPath = args.domainPrivateKeyPath || args.domainKeyPath || args.keyPath || pyobj.keyPath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return writeRenewalConfig(args); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function writeCertificateAsync(args, defaults, handlers) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   function log() { | 
					
						
							|  |  |  |     if (args.debug) { | 
					
						
							|  |  |  |       console.log.apply(console, arguments); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   log("[le/core.js] got certificate!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   var obj = args.pyobj; | 
					
						
							|  |  |  |   var result = args.pems; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |   result.fullchain = result.cert + '\n' + (result.chain || result.ca); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   obj.checkpoints = parseInt(obj.checkpoints, 10) || 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var certPath = args.certPath || obj.cert || path.join(liveDir, 'cert.pem'); | 
					
						
							|  |  |  |   var fullchainPath = args.fullchainPath || obj.fullchain || path.join(liveDir, 'fullchain.pem'); | 
					
						
							|  |  |  |   var chainPath = args.chainPath || obj.chain || path.join(liveDir, 'chain.pem'); | 
					
						
							|  |  |  |   var privkeyPath = args.privkeyPath || obj.privkey | 
					
						
							|  |  |  |     //|| args.domainPrivateKeyPath || args.domainKeyPath || obj.keyPath
 | 
					
						
							|  |  |  |     || path.join(liveDir, 'privkey.pem'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   log('[le/core.js] privkeyPath', privkeyPath); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   var archiveDir = args.archiveDir || path.join(args.configDir, 'archive', args.domains[0]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var checkpoints = obj.checkpoints.toString(); | 
					
						
							|  |  |  |   var certArchive = path.join(archiveDir, 'cert' + checkpoints + '.pem'); | 
					
						
							|  |  |  |   var fullchainArchive = path.join(archiveDir, 'fullchain' + checkpoints + '.pem'); | 
					
						
							|  |  |  |   var chainArchive = path.join(archiveDir, 'chain'+ checkpoints + '.pem'); | 
					
						
							|  |  |  |   var privkeyArchive = path.join(archiveDir, 'privkey' + checkpoints + '.pem'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return mkdirpAsync(archiveDir).then(function () { | 
					
						
							|  |  |  |     return PromiseA.all([ | 
					
						
							|  |  |  |       sfs.writeFileAsync(certArchive, result.cert, 'ascii') | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |     , sfs.writeFileAsync(chainArchive, (result.chain || result.ca), 'ascii') | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     , sfs.writeFileAsync(fullchainArchive, result.fullchain, 'ascii') | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |     , sfs.writeFileAsync( | 
					
						
							|  |  |  |         privkeyArchive | 
					
						
							|  |  |  |         // TODO nix args.key, args.domainPrivateKeyPem ??
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:00:41 -04:00
										 |  |  |       , (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |       , 'ascii' | 
					
						
							|  |  |  |       ) | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     return mkdirpAsync(liveDir); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     return PromiseA.all([ | 
					
						
							|  |  |  |       sfs.writeFileAsync(certPath, result.cert, 'ascii') | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |     , sfs.writeFileAsync(chainPath, (result.chain || result.ca), 'ascii') | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     , sfs.writeFileAsync(fullchainPath, result.fullchain, 'ascii') | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |     , sfs.writeFileAsync( | 
					
						
							|  |  |  |         privkeyPath | 
					
						
							|  |  |  |         // TODO nix args.key, args.domainPrivateKeyPem ??
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:00:41 -04:00
										 |  |  |       , (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |       , 'ascii' | 
					
						
							|  |  |  |       ) | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     obj.checkpoints += 1; | 
					
						
							|  |  |  |     args.checkpoints += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return writeRenewalConfig(args); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							| 
									
										
										
										
											2016-08-04 12:06:31 -04:00
										 |  |  |     var getCertInfo = require('./cert-info').getCertInfo; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 12:23:40 -04:00
										 |  |  |     // XXX Note: Parsing the certificate info comes at a great cost (~500kb)
 | 
					
						
							| 
									
										
										
										
											2016-08-04 12:06:31 -04:00
										 |  |  |     var certInfo = getCertInfo(result.cert); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       certPath: certPath | 
					
						
							|  |  |  |     , chainPath: chainPath | 
					
						
							|  |  |  |     , fullchainPath: fullchainPath | 
					
						
							|  |  |  |     , privkeyPath: privkeyPath | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |       // TODO nix keypair
 | 
					
						
							|  |  |  |     , keypair: args.domainKeypair | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |       // TODO nix args.key, args.domainPrivateKeyPem ??
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       // some ambiguity here...
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:00:41 -04:00
										 |  |  |     , privkey: (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |     , fullchain: result.fullchain || (result.cert + '\n' + result.chain) | 
					
						
							|  |  |  |     , chain:  (result.chain || result.ca) | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       // especially this one... might be cert only, might be fullchain
 | 
					
						
							|  |  |  |     , cert: result.cert | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 12:06:31 -04:00
										 |  |  |     , issuedAt: Date(certInfo.notBefore.value).valueOf() // Date.now()
 | 
					
						
							|  |  |  |     , expiresAt: Date(certInfo.notAfter.value).valueOf() | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     , lifetime: defaults.lifetime || handlers.lifetime | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | function getCertificateAsync(args, defaults, handlers) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   function log() { | 
					
						
							|  |  |  |     if (args.debug || defaults.debug) { | 
					
						
							|  |  |  |       console.log.apply(console, arguments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |   var account = args.account; | 
					
						
							| 
									
										
										
										
											2015-12-20 02:41:17 -08:00
										 |  |  |   var promise; | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |   var keypairOpts = { public: true, pem: true }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   log('[le/core.js] domainKeyPath:', args.domainKeyPath); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   promise = fs.readFileAsync(args.domainKeyPath, 'ascii').then(function (pem) { | 
					
						
							|  |  |  |     return RSA.import({ privateKeyPem: pem }); | 
					
						
							|  |  |  |   }, function (/*err*/) { | 
					
						
							| 
									
										
										
										
											2016-08-05 09:02:51 -06:00
										 |  |  |     return RSA.generateKeypairAsync(args.rsaKeySize || 2048, 65537, keypairOpts).then(function (keypair) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |       return mkdirpAsync(path.dirname(args.domainKeyPath)).then(function () { | 
					
						
							|  |  |  |         return fs.writeFileAsync(args.domainKeyPath, keypair.privateKeyPem, 'ascii').then(function () { | 
					
						
							|  |  |  |           return keypair; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:00:41 -04:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:41:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |   return promise.then(function (domainKeypair) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |     log("[le/core.js] get certificate"); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |     args.domainKeypair = domainKeypair; | 
					
						
							| 
									
										
										
										
											2015-12-19 17:43:02 -08:00
										 |  |  |     //args.registration = domainKey;
 | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  |     return LeCore.getCertificateAsync({ | 
					
						
							| 
									
										
										
										
											2015-12-19 22:26:12 +00:00
										 |  |  |       debug: args.debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     , newAuthzUrl: args._acmeUrls.newAuthz | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  |     , newCertUrl: args._acmeUrls.newCert | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |     , accountKeypair: RSA.import(account.keypair) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:11:42 -04:00
										 |  |  |     , domainKeypair: domainKeypair | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  |     , domains: args.domains | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 09:02:51 -06:00
										 |  |  |     , challengeType: args.challengeType || 'http-01' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |       //
 | 
					
						
							|  |  |  |       // 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)
 | 
					
						
							|  |  |  |       //
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     , setChallenge: function (domain, key, value, done) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |         var copy = merge(defaults, { domains: [domain] }); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |         tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |         args.domains = [domain]; | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |         args.webrootPath = args.webrootPath; | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         if (4 === handlers.setChallenge.length) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |           handlers.setChallenge(copy, key, value, done); | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (5 === handlers.setChallenge.length) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |           handlers.setChallenge(copy, domain, key, value, done); | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           done(new Error("handlers.setChallenge receives the wrong number of arguments")); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     , removeChallenge: function (domain, key, done) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |         var copy = merge(defaults, { domains: [domain] }); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |         tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         if (3 === handlers.removeChallenge.length) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |           handlers.removeChallenge(copy, key, done); | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (4 === handlers.removeChallenge.length) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |           handlers.removeChallenge(copy, domain, key, done); | 
					
						
							| 
									
										
										
										
											2015-12-17 04:44:28 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           done(new Error("handlers.removeChallenge receives the wrong number of arguments")); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  |   }).then(function (results) { | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  |     // { cert, chain, fullchain, privkey }
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     args.pems = results; | 
					
						
							|  |  |  |     return writeCertificateAsync(args, defaults, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | function getOrCreateDomainCertificate(args, defaults, handlers) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   if (args.duplicate) { | 
					
						
							|  |  |  |     // we're forcing a refresh via 'dupliate: true'
 | 
					
						
							|  |  |  |     return getCertificateAsync(args, defaults, handlers); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |   return fetchFromConfigLiveDir(args).then(function (certs) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |     var halfLife = (certs.expiresAt - certs.issuedAt) / 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!certs || (Date.now() - certs.issuedAt) > halfLife) { | 
					
						
							|  |  |  |       // There is no cert available
 | 
					
						
							|  |  |  |       // Or the cert is more than half-expired
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       return getCertificateAsync(args, defaults, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return PromiseA.reject(new Error( | 
					
						
							|  |  |  |         "[ERROR] Certificate issued at '" | 
					
						
							|  |  |  |       + new Date(certs.issuedAt).toISOString() + "' and expires at '" | 
					
						
							|  |  |  |       + new Date(certs.expiresAt).toISOString() + "'. Ignoring renewal attempt until half-life at '" | 
					
						
							|  |  |  |       + new Date(certs.issuedA + halfLife).toISOString() + "'. Set { duplicate: true } to force." | 
					
						
							|  |  |  |     )); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:49:54 -04:00
										 |  |  | // returns 'account' from lib/accounts { meta, regr, keypair, accountId (id) }
 | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  | function getOrCreateAcmeAccount(args, defaults, handlers) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |   function log() { | 
					
						
							|  |  |  |     if (args.debug) { | 
					
						
							|  |  |  |       console.log.apply(console, arguments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |   var pyconf = PromiseA.promisifyAll(require('pyconf')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 22:26:12 +00:00
										 |  |  |   return pyconf.readFileAsync(args.renewalPath).then(function (renewal) { | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     var accountId = renewal.account; | 
					
						
							|  |  |  |     renewal = renewal.account; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return accountId; | 
					
						
							|  |  |  |   }, function (err) { | 
					
						
							| 
									
										
										
										
											2015-12-16 10:07:00 +00:00
										 |  |  |     if ("ENOENT" === err.code) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |       log("[le/core.js] try email"); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       return Accounts.getAccountIdByEmail(args, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     return PromiseA.reject(err); | 
					
						
							|  |  |  |   }).then(function (accountId) { | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     // Note: the ACME urls are always fetched fresh on purpose
 | 
					
						
							|  |  |  |     return getAcmeUrls(args).then(function (urls) { | 
					
						
							|  |  |  |       args._acmeUrls = urls; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (accountId) { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |         log('[le/core.js] use account'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |         args.accountId = accountId; | 
					
						
							|  |  |  |         return Accounts.getAccount(args, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |         log('[le/core.js] create account'); | 
					
						
							| 
									
										
										
										
											2015-12-17 05:44:41 +00:00
										 |  |  |         return Accounts.createAccount(args, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }).then(function (account) { | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |     if (renewal.account !== account) { | 
					
						
							|  |  |  |       // the account has become corrupt, re-register
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2016-08-04 14:26:49 -04:00
										 |  |  |     log('[le/core.js] created account'); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  |     return account; | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |   return fs.readdirAsync(accountsDir, function (nodes) { | 
					
						
							|  |  |  |     return PromiseA.all(nodes.map(function (node) { | 
					
						
							|  |  |  |       var reMd5 = /[a-f0-9]{32}/i; | 
					
						
							|  |  |  |       if (reMd5.test(node)) { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | */ | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports.create = function (defaults, handlers) { | 
					
						
							|  |  |  |   defaults.server = defaults.server || LE.liveServer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var wrapped = { | 
					
						
							|  |  |  |     registerAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2015-12-19 11:49:34 -08:00
										 |  |  |       var copy; | 
					
						
							|  |  |  |       // TODO move these defaults elsewhere?
 | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  |       //args.renewalDir = args.renewalDir || ':config/renewal/';
 | 
					
						
							| 
									
										
										
										
											2015-12-19 11:49:34 -08:00
										 |  |  |       args.renewalPath = args.renewalPath || ':config/renewal/:hostname.conf'; | 
					
						
							|  |  |  |       // Note: the /directory is part of the server url and, as such, bleeds into the pathname
 | 
					
						
							|  |  |  |       // So :config/accounts/:server/directory is *incorrect*, but the following *is* correct:
 | 
					
						
							|  |  |  |       args.accountsDir = args.accountsDir || ':config/accounts/:server'; | 
					
						
							|  |  |  |       copy = merge(args, defaults); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       var url = require('url'); | 
					
						
							|  |  |  |       var acmeLocation = url.parse(copy.server); | 
					
						
							|  |  |  |       var acmeHostpath = path.join(acmeLocation.hostname, acmeLocation.pathname); | 
					
						
							|  |  |  |       copy.renewalPath = copy.renewalPath || path.join(copy.configDir, 'renewal', copy.domains[0] + '.conf'); | 
					
						
							|  |  |  |       copy.accountsDir = copy.accountsDir || path.join(copy.configDir, 'accounts', acmeHostpath); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       return getOrCreateAcmeAccount(copy, defaults, handlers).then(function (account) { | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |         copy.account = account; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return getOrCreateRenewal(copy).then(function (pyobj) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           copy.pyobj = pyobj; | 
					
						
							|  |  |  |           return getOrCreateDomainCertificate(copy, defaults, handlers); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-02-10 15:41:15 -05:00
										 |  |  |       }).then(function (result) { | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |       }, function (err) { | 
					
						
							|  |  |  |         return PromiseA.reject(err); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   , fetchAsync: function (args) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |       var copy = merge(args, defaults); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       tplCopy(copy); | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return fetchFromConfigLiveDir(copy, defaults); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-20 02:41:17 -08:00
										 |  |  |   , configureAsync: function (hargs) { | 
					
						
							|  |  |  |       hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; | 
					
						
							|  |  |  |       var copy = merge(hargs, defaults); | 
					
						
							|  |  |  |       tplCopy(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-21 10:27:57 -07:00
										 |  |  |       return getOrCreateAcmeAccount(copy, defaults, handlers).then(function (account) { | 
					
						
							|  |  |  |         copy.account = account; | 
					
						
							|  |  |  |         return getOrCreateRenewal(copy); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2015-12-20 02:41:17 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   , getConfigAsync: function (hargs) { | 
					
						
							|  |  |  |       hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; | 
					
						
							|  |  |  |       hargs.domains = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var copy = merge(hargs, defaults); | 
					
						
							|  |  |  |       tplCopy(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return readRenewalConfig(copy).then(function (pyobj) { | 
					
						
							|  |  |  |         var exists = pyobj.checkpoints >= 0; | 
					
						
							|  |  |  |         if (!exists) { | 
					
						
							|  |  |  |           return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return pyobj; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , getConfigsAsync: function (hargs) { | 
					
						
							|  |  |  |       hargs.renewalDir = hargs.renewalDir || ':config/renewal/'; | 
					
						
							|  |  |  |       hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; | 
					
						
							|  |  |  |       hargs.domains = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var copy = merge(hargs, defaults); | 
					
						
							|  |  |  |       tplCopy(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return fs.readdirAsync(copy.renewalDir).then(function (nodes) { | 
					
						
							|  |  |  |         nodes = nodes.filter(function (node) { | 
					
						
							|  |  |  |           return /^[a-z0-9]+.*\.conf$/.test(node); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return PromiseA.all(nodes.map(function (node) { | 
					
						
							|  |  |  |           copy.domains = [node.replace(/\.conf$/, '')]; | 
					
						
							|  |  |  |           return wrapped.getConfigAsync(copy); | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return wrapped; | 
					
						
							|  |  |  | }; |