| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var PromiseA = require('bluebird'); | 
					
						
							| 
									
										
										
										
											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-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-16 09:25:39 +00:00
										 |  |  | var leCrypto = PromiseA.promisifyAll(LeCore.leCrypto); | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							|  |  |  |   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'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  |   if (args.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |     console.log('################   privkeyPath   ################'); | 
					
						
							|  |  |  |     console.log(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 | 
					
						
							|  |  |  |   , rsaKeySize: args.rsaKeySize | 
					
						
							|  |  |  |   , 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 () { | 
					
						
							|  |  |  |     return pyobj; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |     args.rsaKeySize = args.rsaKeySize || pyobj.rsaKeySize; | 
					
						
							|  |  |  |     args.http01Port = args.http01Port || pyobj.http01Port; | 
					
						
							|  |  |  |     args.domainKeyPath = args.domainPrivateKeyPath || args.domainKeyPath || args.keyPath || pyobj.keyPath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return writeRenewalConfig(args); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function writeCertificateAsync(args, defaults, handlers) { | 
					
						
							|  |  |  |   if (args.debug) { | 
					
						
							|  |  |  |     console.log("got certificate!"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var obj = args.pyobj; | 
					
						
							|  |  |  |   var result = args.pems; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   result.fullchain = result.cert + '\n' + result.ca; | 
					
						
							|  |  |  |   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'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (args.debug) { | 
					
						
							|  |  |  |     console.log('################   privkeyPath   ################'); | 
					
						
							|  |  |  |     console.log(privkeyPath); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(chainArchive, result.ca || result.chain, 'ascii') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(fullchainArchive, result.fullchain, 'ascii') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(privkeyArchive, result.key || result.privkey || args.domainPrivateKeyPem, 'ascii') | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     return mkdirpAsync(liveDir); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     return PromiseA.all([ | 
					
						
							|  |  |  |       sfs.writeFileAsync(certPath, result.cert, 'ascii') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(chainPath, result.ca || result.chain, 'ascii') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(fullchainPath, result.fullchain, 'ascii') | 
					
						
							|  |  |  |     , sfs.writeFileAsync(privkeyPath, result.key || result.privkey || args.domainPrivateKeyPem, 'ascii') | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  |     obj.checkpoints += 1; | 
					
						
							|  |  |  |     args.checkpoints += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return writeRenewalConfig(args); | 
					
						
							|  |  |  |   }).then(function () { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       certPath: certPath | 
					
						
							|  |  |  |     , chainPath: chainPath | 
					
						
							|  |  |  |     , fullchainPath: fullchainPath | 
					
						
							|  |  |  |     , privkeyPath: privkeyPath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // some ambiguity here...
 | 
					
						
							|  |  |  |     , privkey: result.key || result.privkey || args.domainPrivateKeyPem | 
					
						
							|  |  |  |     , fullchain: result.fullchain || result.cert | 
					
						
							|  |  |  |     , chain: result.ca || result.chain | 
					
						
							|  |  |  |       // especially this one... might be cert only, might be fullchain
 | 
					
						
							|  |  |  |     , cert: result.cert | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     , issuedAt: Date.now() | 
					
						
							|  |  |  |     , 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) { | 
					
						
							|  |  |  |   var account = args.account; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |   return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537).then(function (domainKey) { | 
					
						
							| 
									
										
										
										
											2015-12-19 22:26:12 +00:00
										 |  |  |     if (args.debug) { | 
					
						
							|  |  |  |       console.log("get certificate"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-20 02:01:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     args.domainPrivateKeyPem = domainKey.privateKeyPem; | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 15:21:27 +00:00
										 |  |  |     , accountPrivateKeyPem: account.privateKeyPem | 
					
						
							| 
									
										
										
										
											2015-12-19 11:49:34 -08:00
										 |  |  |     , domainPrivateKeyPem: domainKey.privateKeyPem | 
					
						
							| 
									
										
										
										
											2015-12-16 01:11:31 -08:00
										 |  |  |     , domains: args.domains | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |   return fetchFromConfigLiveDir(args).then(function (certs) { | 
					
						
							|  |  |  |     // if nothing, register and save
 | 
					
						
							|  |  |  |     // if something, check date (don't register unless 30+ days)
 | 
					
						
							|  |  |  |     // if good, don't bother registering
 | 
					
						
							|  |  |  |     // (but if we get to the point that we're actually calling
 | 
					
						
							|  |  |  |     // this function, that shouldn't be the case, right?)
 | 
					
						
							|  |  |  |     //console.log(certs);
 | 
					
						
							|  |  |  |     if (!certs) { | 
					
						
							|  |  |  |       // no certs, seems like a good time to get some
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       return getCertificateAsync(args, defaults, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (certs.issuedAt > (27 * 24 * 60 * 60 * 1000)) { | 
					
						
							|  |  |  |       // cert is at least 27 days old we can renew that
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       return getCertificateAsync(args, defaults, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (args.duplicate) { | 
					
						
							|  |  |  |       // YOLO! I be gettin' fresh certs 'erday! Yo!
 | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  |       return getCertificateAsync(args, defaults, handlers); | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       console.warn('[WARN] Ignoring renewal attempt for certificate less than 27 days old. Use args.duplicate to force.'); | 
					
						
							|  |  |  |       // We're happy with what we have
 | 
					
						
							|  |  |  |       return certs; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-12-20 05:13:41 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | function getOrCreateAcmeAccount(args, defaults, handlers) { | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |       if (args.debug) { | 
					
						
							|  |  |  |         console.log("[LE] try email"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       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) { | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |         if (args.debug) { | 
					
						
							|  |  |  |           console.log('[LE] 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 { | 
					
						
							| 
									
										
										
										
											2015-12-20 00:27:48 +00:00
										 |  |  |         if (args.debug) { | 
					
						
							|  |  |  |           console.log('[LE] 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											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
										 |  |  |       if (copy.debug) { | 
					
						
							| 
									
										
										
										
											2015-12-17 08:46:40 +00:00
										 |  |  |         console.log('[LE DEBUG] reg domains', args.domains); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											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
										 |  |  |         //console.log("account", account);
 | 
					
						
							|  |  |  |         copy.account = account; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return getOrCreateRenewal(copy).then(function (pyobj) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           copy.pyobj = pyobj; | 
					
						
							|  |  |  |           return getOrCreateDomainCertificate(copy, defaults, handlers); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  |       if (args.debug) { | 
					
						
							|  |  |  |         console.log('[LE DEBUG] fetch domains', copy); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return fetchFromConfigLiveDir(copy, defaults); | 
					
						
							| 
									
										
										
										
											2015-12-15 03:37:39 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return wrapped; | 
					
						
							|  |  |  | }; |