roster-server 2.2.9 → 2.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/index.js +2 -2
  2. package/package.json +12 -3
  3. package/tasks/lessons.md +2 -1
  4. package/test/roster-server.test.js +5 -6
  5. package/vendor/greenlock/.prettierrc +8 -0
  6. package/vendor/greenlock/LICENSE +312 -0
  7. package/vendor/greenlock/MIGRATION_GUIDE.md +403 -0
  8. package/vendor/greenlock/README.md +667 -0
  9. package/vendor/greenlock/accounts.js +218 -0
  10. package/vendor/greenlock/bin/add.js +72 -0
  11. package/vendor/greenlock/bin/certonly.js +368 -0
  12. package/vendor/greenlock/bin/config.js +77 -0
  13. package/vendor/greenlock/bin/defaults.js +58 -0
  14. package/vendor/greenlock/bin/greenlock.js +26 -0
  15. package/vendor/greenlock/bin/init.js +159 -0
  16. package/vendor/greenlock/bin/lib/cli.js +230 -0
  17. package/vendor/greenlock/bin/lib/flags.js +385 -0
  18. package/vendor/greenlock/bin/remove.js +46 -0
  19. package/vendor/greenlock/bin/tmpl/app.tmpl.js +9 -0
  20. package/vendor/greenlock/bin/tmpl/cluster.tmpl.js +30 -0
  21. package/vendor/greenlock/bin/tmpl/greenlock.tmpl.js +13 -0
  22. package/vendor/greenlock/bin/tmpl/server.tmpl.js +20 -0
  23. package/vendor/greenlock/bin/update.js +62 -0
  24. package/vendor/greenlock/certificates.js +324 -0
  25. package/vendor/greenlock/errors.js +58 -0
  26. package/vendor/greenlock/greenlock.js +621 -0
  27. package/vendor/greenlock/greenlockrc.js +169 -0
  28. package/vendor/greenlock/lib/challenges-wrapper.js +88 -0
  29. package/vendor/greenlock/lib/directory-url.js +44 -0
  30. package/vendor/greenlock/lib/init.js +191 -0
  31. package/vendor/greenlock/lib/manager-wrapper.js +625 -0
  32. package/vendor/greenlock/lib/rc.js +70 -0
  33. package/vendor/greenlock/logo/beaker-browser-301x112.png +0 -0
  34. package/vendor/greenlock/logo/from-not-secure-to-secure-url-bar.png +0 -0
  35. package/vendor/greenlock/logo/greenlock-1063x250.png +0 -0
  36. package/vendor/greenlock/logo/greenlock-850x200.png +0 -0
  37. package/vendor/greenlock/logo/ibm-301x112.png +0 -0
  38. package/vendor/greenlock/logo/telebit-301x112.png +0 -0
  39. package/vendor/greenlock/order.js +63 -0
  40. package/vendor/greenlock/package-lock.json +140 -0
  41. package/vendor/greenlock/package.json +56 -0
  42. package/vendor/greenlock/plugins.js +270 -0
  43. package/vendor/greenlock/tests/cli.sh +31 -0
  44. package/vendor/greenlock/tests/index.js +53 -0
  45. package/vendor/greenlock/user-events.js +7 -0
  46. package/vendor/greenlock/utils.js +281 -0
  47. package/vendor/greenlock-express/greenlock-shim.js +3 -1
  48. package/vendor/greenlock-express/package.json +0 -1
@@ -0,0 +1,625 @@
1
+ 'use strict';
2
+
3
+ var U = require('../utils.js');
4
+ var E = require('../errors.js');
5
+ var log = require('lemonlog')('greenlock-manager');
6
+
7
+ var warned = {};
8
+
9
+ // The purpose of this file is to try to auto-build
10
+ // partial managers so that the external API can be smaller.
11
+
12
+ module.exports.wrap = function(greenlock, gconf) {
13
+ var myFind = gconf.find;
14
+ delete gconf.find;
15
+
16
+ var mega = mergeManager(greenlock, gconf);
17
+
18
+ greenlock.manager = {};
19
+ greenlock.sites = {};
20
+ //greenlock.accounts = {};
21
+ //greenlock.certs = {};
22
+
23
+ greenlock.manager._modulename = gconf.manager.module;
24
+ if ('/' === String(gconf.manager.module)[0]) {
25
+ greenlock.manager._modulename = require('path').relative(
26
+ gconf.packageRoot,
27
+ greenlock.manager._modulename
28
+ );
29
+ if ('.' !== String(greenlock.manager._modulename)[0]) {
30
+ greenlock.manager._modulename =
31
+ './' + greenlock.manager._modulename;
32
+ }
33
+ }
34
+
35
+ var allowed = [
36
+ 'accountKeyType', //: ["P-256", "RSA-2048"],
37
+ 'serverKeyType', //: ["RSA-2048", "P-256"],
38
+ 'store', // : { module, specific opts },
39
+ 'challenges', // : { "http-01", "dns-01", "tls-alpn-01" },
40
+ 'subscriberEmail',
41
+ 'agreeToTerms',
42
+ 'agreeTos',
43
+ 'customerEmail',
44
+ 'renewOffset',
45
+ 'renewStagger',
46
+ 'module', // not allowed, just ignored
47
+ 'manager'
48
+ ];
49
+
50
+ // get / set default site settings such as
51
+ // subscriberEmail, store, challenges, renewOffset, renewStagger
52
+ greenlock.manager.defaults = function(conf) {
53
+ return greenlock._init().then(function() {
54
+ if (!conf) {
55
+ return mega.defaults();
56
+ }
57
+
58
+ if (conf.sites) {
59
+ throw new Error('cannot set sites as global config');
60
+ }
61
+ if (conf.routes) {
62
+ throw new Error('cannot set routes as global config');
63
+ }
64
+
65
+ // disallow keys we know to be bad
66
+ [
67
+ 'subject',
68
+ 'deletedAt',
69
+ 'altnames',
70
+ 'lastAttemptAt',
71
+ 'expiresAt',
72
+ 'issuedAt',
73
+ 'renewAt',
74
+ 'sites',
75
+ 'routes'
76
+ ].some(function(k) {
77
+ if (k in conf) {
78
+ throw new Error(
79
+ '`' + k + '` not allowed as a default setting'
80
+ );
81
+ }
82
+ });
83
+ Object.keys(conf).forEach(function(k) {
84
+ if (!allowed.includes(k) && !warned[k]) {
85
+ warned[k] = true;
86
+ log.warn("Unknown config key '%s'; open an issue if you need it", k);
87
+ }
88
+ });
89
+
90
+ Object.keys(conf).forEach(function(k) {
91
+ if (-1 !== ['module', 'manager'].indexOf(k)) {
92
+ return;
93
+ }
94
+
95
+ if ('undefined' === typeof k) {
96
+ throw new Error(
97
+ "'" +
98
+ k +
99
+ "' should be set to a value, or `null`, but not left `undefined`"
100
+ );
101
+ }
102
+ });
103
+
104
+ return mega.defaults(conf);
105
+ });
106
+ };
107
+
108
+ greenlock.manager._defaults = function(opts) {
109
+ return mega.defaults(opts);
110
+ };
111
+
112
+ greenlock.manager.add = function(args) {
113
+ if (!args || !Array.isArray(args.altnames) || !args.altnames.length) {
114
+ throw new Error(
115
+ 'you must specify `altnames` when adding a new site'
116
+ );
117
+ }
118
+ if (args.renewAt) {
119
+ throw new Error(
120
+ 'you cannot specify `renewAt` when adding a new site'
121
+ );
122
+ }
123
+
124
+ return greenlock.manager.set(args);
125
+ };
126
+
127
+ // TODO agreeToTerms should be handled somewhere... maybe?
128
+
129
+ // Add and update remains because I said I had locked the API
130
+ greenlock.manager.set = greenlock.manager.update = function(args) {
131
+ return greenlock._init().then(function() {
132
+ // The goal is to make this decently easy to manage by hand without mistakes
133
+ // but also reasonably easy to error check and correct
134
+ // and to make deterministic auto-corrections
135
+
136
+ args.subject = checkSubject(args);
137
+
138
+ //var subscriberEmail = args.subscriberEmail;
139
+
140
+ // TODO shortcut the other array checks when not necessary
141
+ if (Array.isArray(args.altnames)) {
142
+ args.altnames = checkAltnames(args.subject, args);
143
+ }
144
+
145
+ // at this point we know that subject is the first of altnames
146
+ return Promise.all(
147
+ (args.altnames || []).map(function(d) {
148
+ d = d.replace('*.', '');
149
+ return U._validDomain(d);
150
+ })
151
+ ).then(function() {
152
+ if (!U._uniqueNames(args.altnames || [])) {
153
+ throw E.NOT_UNIQUE(
154
+ 'add',
155
+ "'" + args.altnames.join("' '") + "'"
156
+ );
157
+ }
158
+
159
+ // durations
160
+ if (args.renewOffset) {
161
+ args.renewOffset = U._parseDuration(args.renewOffset);
162
+ }
163
+ if (args.renewStagger) {
164
+ args.renewStagger = U._parseDuration(args.renewStagger);
165
+ }
166
+
167
+ return mega.set(args).then(function(result) {
168
+ if (!gconf._bin_mode) {
169
+ greenlock.renew({}).catch(function(err) {
170
+ if (!err.context) {
171
+ err.contxt = 'renew';
172
+ }
173
+ greenlock._notify('error', err);
174
+ });
175
+ }
176
+ return result;
177
+ });
178
+ });
179
+ });
180
+ };
181
+
182
+ greenlock.manager.get = greenlock.sites.get = function(args) {
183
+ return Promise.resolve().then(function() {
184
+ if (args.subject) {
185
+ throw new Error(
186
+ 'get({ servername }) searches certificates by altnames, not by subject specifically'
187
+ );
188
+ }
189
+ if (args.servernames || args.altnames || args.renewBefore) {
190
+ throw new Error(
191
+ 'get({ servername }) does not take arguments that could lead to multiple results'
192
+ );
193
+ }
194
+ return mega.get(args);
195
+ });
196
+ };
197
+
198
+ greenlock.manager.remove = function(args) {
199
+ return Promise.resolve().then(function() {
200
+ args.subject = checkSubject(args);
201
+ if (args.servername) {
202
+ throw new Error(
203
+ 'remove() should be called with `subject` only, if you wish to remove altnames use `update()`'
204
+ );
205
+ }
206
+ if (args.altnames) {
207
+ throw new Error(
208
+ 'remove() should be called with `subject` only, not `altnames`'
209
+ );
210
+ }
211
+ // TODO check no altnames
212
+ return mega.remove(args);
213
+ });
214
+ };
215
+
216
+ /*
217
+ {
218
+ subject: site.subject,
219
+ altnames: site.altnames,
220
+ //issuedAt: site.issuedAt,
221
+ //expiresAt: site.expiresAt,
222
+ renewOffset: site.renewOffset,
223
+ renewStagger: site.renewStagger,
224
+ renewAt: site.renewAt,
225
+ subscriberEmail: site.subscriberEmail,
226
+ customerEmail: site.customerEmail,
227
+ challenges: site.challenges,
228
+ store: site.store
229
+ };
230
+ */
231
+
232
+ // no transaction promise here because it calls set
233
+ greenlock._find = async function(args) {
234
+ args = _mangleFindArgs(args);
235
+ var ours = await mega.find(args);
236
+ if (!myFind) {
237
+ return ours;
238
+ }
239
+
240
+ // if the user has an overlay find function we'll do a diff
241
+ // between the managed state and the overlay, and choose
242
+ // what was found.
243
+ var theirs = await myFind(args);
244
+ theirs = theirs.filter(function(site) {
245
+ if (!site || 'string' !== typeof site.subject) {
246
+ throw new Error('found site is missing subject');
247
+ }
248
+ if (
249
+ !Array.isArray(site.altnames) ||
250
+ !site.altnames.length ||
251
+ !site.altnames[0] ||
252
+ site.altnames[0] !== site.subject
253
+ ) {
254
+ throw new Error('missing or malformed altnames');
255
+ }
256
+ ['renewAt', 'issuedAt', 'expiresAt'].forEach(function(k) {
257
+ if (site[k]) {
258
+ throw new Error(
259
+ '`' +
260
+ k +
261
+ '` should be updated by `set()`, not by `find()`'
262
+ );
263
+ }
264
+ });
265
+ if (!site) {
266
+ return;
267
+ }
268
+ if (args.subject && site.subject !== args.subject) {
269
+ return false;
270
+ }
271
+
272
+ var servernames = args.servernames || args.altnames;
273
+ if (
274
+ servernames &&
275
+ !site.altnames.some(function(altname) {
276
+ return servernames.includes(altname);
277
+ })
278
+ ) {
279
+ return false;
280
+ }
281
+
282
+ return site.renewAt < (args.renewBefore || Infinity);
283
+ });
284
+ return _mergeFind(ours, theirs);
285
+ };
286
+
287
+ function _mergeFind(ours, theirs) {
288
+ var toUpdate = [];
289
+ theirs.forEach(function(_newer) {
290
+ var hasCurrent = ours.some(function(_older) {
291
+ var changed = false;
292
+ if (_newer.subject !== _older.subject) {
293
+ return false;
294
+ }
295
+
296
+ // BE SURE TO SET THIS UNDEFINED AFTERWARDS
297
+ _older._exists = true;
298
+
299
+ _newer.deletedAt = _newer.deletedAt || 0;
300
+ Object.keys(_newer).forEach(function(k) {
301
+ if (_older[k] !== _newer[k]) {
302
+ changed = true;
303
+ _older[k] = _newer[k];
304
+ }
305
+ });
306
+ if (changed) {
307
+ toUpdate.push(_older);
308
+ }
309
+
310
+ // handled the (only) match
311
+ return true;
312
+ });
313
+ if (!hasCurrent) {
314
+ toUpdate.push(_newer);
315
+ }
316
+ });
317
+
318
+ // delete the things that are gone
319
+ ours.forEach(function(_older) {
320
+ if (!_older._exists) {
321
+ _older.deletedAt = Date.now();
322
+ toUpdate.push(_older);
323
+ }
324
+ _older._exists = undefined;
325
+ });
326
+
327
+ Promise.all(
328
+ toUpdate.map(function(site) {
329
+ return greenlock.sites.update(site).catch(function(err) {
330
+ log.error('Cannot update sites from user find():', err);
331
+ });
332
+ })
333
+ );
334
+
335
+ // ours is updated from theirs
336
+ return ours;
337
+ }
338
+
339
+ greenlock.manager.init = mega.init;
340
+ };
341
+
342
+ function checkSubject(args) {
343
+ if (!args || !args.subject) {
344
+ throw new Error('you must specify `subject` when configuring a site');
345
+ }
346
+ /*
347
+ if (!args.subject) {
348
+ throw E.NO_SUBJECT('add');
349
+ }
350
+ */
351
+
352
+ var subject = (args.subject || '').toLowerCase();
353
+ if (subject !== args.subject) {
354
+ log.warn('subject must be lowercase:', args.subject);
355
+ }
356
+
357
+ return U._encodeName(subject);
358
+ }
359
+
360
+ function checkAltnames(subject, args) {
361
+ // the things we have to check and get right
362
+ var altnames = (args.altnames || []).map(function(name) {
363
+ return String(name || '').toLowerCase();
364
+ });
365
+
366
+ // punycode BEFORE validation
367
+ // (set, find, remove)
368
+ if (altnames.join() !== args.altnames.join()) {
369
+ log.warn('altnames must be lowercase:', args.altnames);
370
+ }
371
+
372
+ args.altnames = args.altnames.map(U._encodeName);
373
+ if (
374
+ !args.altnames.every(function(d) {
375
+ return U._validName(d);
376
+ })
377
+ ) {
378
+ throw E.INVALID_HOSTNAME('add', "'" + args.altnames.join("' '") + "'");
379
+ }
380
+
381
+ if (subject && subject !== args.altnames[0]) {
382
+ throw E.BAD_ORDER(
383
+ 'add',
384
+ '(' + args.subject + ") '" + args.altnames.join("' '") + "'"
385
+ );
386
+ }
387
+ /*
388
+ if (subject && subject !== altnames[0]) {
389
+ throw new Error(
390
+ '`subject` must be the first domain in `altnames`',
391
+ args.subject,
392
+ altnames.join(' ')
393
+ );
394
+ }
395
+ */
396
+
397
+ return altnames;
398
+ }
399
+
400
+ function loadManager(gconf) {
401
+ var m;
402
+ // 1. Get the manager
403
+ // 2. Figure out if we need to wrap it
404
+
405
+ /*
406
+ if (!gconf.manager) {
407
+ gconf.manager = '@greenlock/manager';
408
+ }
409
+
410
+ if ('string' !== typeof gconf.manager) {
411
+ throw new Error(
412
+ '`manager` should be a string representing the npm name or file path of the module'
413
+ );
414
+ }
415
+ */
416
+
417
+ try {
418
+ m = require(gconf.manager.module).create(gconf.manager);
419
+ } catch (e) {
420
+ log.error('Error loading manager:', e.code, e.message);
421
+ }
422
+
423
+ if (!m) {
424
+ log.error('Failed to load manager plugin:', gconf.manager);
425
+ process.exit(1);
426
+ }
427
+
428
+ return m;
429
+ }
430
+
431
+ function mergeManager(greenlock, gconf) {
432
+ var mng;
433
+ function m() {
434
+ if (mng) {
435
+ return mng;
436
+ }
437
+ mng = require('@greenlock/manager').create(gconf);
438
+ return mng;
439
+ }
440
+
441
+ var mini = loadManager(gconf);
442
+ var mega = {};
443
+ // optional
444
+ if (mini.defaults) {
445
+ mega.defaults = function(opts) {
446
+ return mini.defaults(opts);
447
+ };
448
+ } else {
449
+ mega.defaults = m().defaults;
450
+ }
451
+
452
+ // optional
453
+ if (mini.remove) {
454
+ mega.remove = function(opts) {
455
+ return mini.remove(opts);
456
+ };
457
+ } else {
458
+ mega.remove = function(opts) {
459
+ mega.get(opts).then(function(site) {
460
+ if (!site) {
461
+ return null;
462
+ }
463
+ site.deletedAt = Date.now();
464
+ return mega.set(site).then(function() {
465
+ return site;
466
+ });
467
+ });
468
+ };
469
+ }
470
+
471
+ if (mini.find) {
472
+ // without this there cannot be fully automatic renewal
473
+ mega.find = function(opts) {
474
+ return mini.find(opts);
475
+ };
476
+ }
477
+
478
+ // set and (find and/or get) should be from the same set
479
+ if (mini.set) {
480
+ mega.set = function(opts) {
481
+ if (!mini.find) {
482
+ // TODO create the list so that find can be implemented
483
+ }
484
+ return mini.set(opts);
485
+ };
486
+ } else {
487
+ mega.set = m().set;
488
+ mega.get = m().get;
489
+ }
490
+
491
+ if (mini.get) {
492
+ mega.get = async function(opts) {
493
+ if (mini.set) {
494
+ return mini.get(opts);
495
+ }
496
+
497
+ if (!mega._get) {
498
+ mega._get = m().get;
499
+ }
500
+
501
+ var existing = await mega._get(opts);
502
+ var site = await mini.get(opts);
503
+ if (!existing) {
504
+ // Add
505
+ if (!site) {
506
+ return;
507
+ }
508
+ site.renewAt = 1;
509
+ site.deletedAt = 0;
510
+ await mega.set(site);
511
+ existing = await mega._get(opts);
512
+ } else if (!site) {
513
+ // Delete
514
+ existing.deletedAt = site.deletedAt || Date.now();
515
+ await mega.set(existing);
516
+ existing = null;
517
+ } else if (
518
+ site.subject !== existing.subject ||
519
+ site.altnames.join(' ') !== existing.altnames.join(' ')
520
+ ) {
521
+ // Update
522
+ site.renewAt = 1;
523
+ site.deletedAt = 0;
524
+ await mega.set(site);
525
+ existing = await mega._get(opts);
526
+ if (!existing) {
527
+ throw new Error('failed to `get` after `set`');
528
+ }
529
+ }
530
+
531
+ return existing;
532
+ };
533
+ } else if (mini.find) {
534
+ mega.get = function(opts) {
535
+ var servername = opts.servername;
536
+ delete opts.servername;
537
+ opts.servernames = (servername && [servername]) || undefined;
538
+ return mini.find(opts).then(function(sites) {
539
+ return sites.filter(function(site) {
540
+ return site.altnames.include(servername);
541
+ })[0];
542
+ });
543
+ };
544
+ } else if (mini.set) {
545
+ throw new Error(
546
+ gconf.manager.module +
547
+ ' implements `set()`, but not `get()` or `find()`'
548
+ );
549
+ } else {
550
+ mega.find = m().find;
551
+ mega.get = m().get;
552
+ }
553
+
554
+ if (!mega.find) {
555
+ mega._nofind = false;
556
+ mega.find = async function(opts) {
557
+ if (!mega._nofind) {
558
+ log.warn('Manager %s does not implement find({})', greenlock.manager._modulename);
559
+ mega._nofind = true;
560
+ }
561
+ return [];
562
+ };
563
+ }
564
+
565
+ if (!mega.get) {
566
+ mega.get = function(opts) {
567
+ var servername = opts.servername;
568
+ delete opts.servername;
569
+ opts.servernames = (servername && [servername]) || undefined;
570
+ return mega.find(opts).then(function(sites) {
571
+ return sites.filter(function(site) {
572
+ return site.altnames.include(servername);
573
+ })[0];
574
+ });
575
+ };
576
+ }
577
+
578
+ mega.init = function(deps) {
579
+ if (mini.init) {
580
+ return mini.init(deps).then(function() {
581
+ if (mng) {
582
+ return mng.init(deps);
583
+ }
584
+ });
585
+ } else if (mng) {
586
+ return mng.init(deps);
587
+ } else {
588
+ return Promise.resolve(null);
589
+ }
590
+ };
591
+
592
+ return mega;
593
+ }
594
+
595
+ function _mangleFindArgs(args) {
596
+ var servernames = (args.servernames || [])
597
+ .concat(args.altnames || [])
598
+ .filter(Boolean)
599
+ .slice(0);
600
+ var modified = servernames.slice(0);
601
+
602
+ // servername, wildname, and altnames are all the same
603
+ ['wildname', 'servername'].forEach(function(k) {
604
+ var altname = args[k] || '';
605
+ if (altname && !modified.includes(altname)) {
606
+ modified.push(altname);
607
+ }
608
+ });
609
+
610
+ if (modified.length) {
611
+ servernames = modified;
612
+ servernames = servernames.map(U._encodeName);
613
+ args.altnames = servernames;
614
+ args.servernames = args.altnames = checkAltnames(false, args);
615
+ }
616
+
617
+ // documented as args.servernames
618
+ // preserved as args.altnames for v3 beta backwards compat
619
+ // my only hesitancy in this choice is that a "servername"
620
+ // may NOT contain '*.', in which case `altnames` is a better choice.
621
+ // However, `altnames` is ambiguous - as if it means to find a
622
+ // certificate by that specific collection of altnames.
623
+ // ... perhaps `domains` could work?
624
+ return args;
625
+ }
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var Rc = module.exports;
4
+ var log = require('lemonlog')('greenlock-rc');
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+
8
+ // This is only called if packageRoot is specified
9
+ // (which it should be most of the time)
10
+ Rc._initSync = function(dirname, manager, configDir) {
11
+ if (!dirname) {
12
+ return {};
13
+ }
14
+
15
+ // dirname / opts.packageRoot
16
+ var rcpath = path.resolve(dirname, '.greenlockrc');
17
+ var rc;
18
+
19
+ try {
20
+ rc = JSON.parse(fs.readFileSync(rcpath));
21
+ } catch (e) {
22
+ if ('ENOENT' !== e.code) {
23
+ throw e;
24
+ }
25
+ rc = {};
26
+ }
27
+
28
+ var changed = true;
29
+
30
+ // In the general case the manager should be specified in the
31
+ // config file, which is in the config dir, but for the specific
32
+ // case in which all custom plugins are being used and no config
33
+ // dir is needed, we allow the manager to be read from the rc.
34
+ // ex: manager: { module: 'name', xxxx: 'xxxx' }
35
+ if (manager) {
36
+ if (rc.manager) {
37
+ if (
38
+ ('string' === typeof rc.manager && rc.manager !== manager) ||
39
+ ('string' !== typeof rc.manager &&
40
+ rc.manager.module !== manager.module)
41
+ ) {
42
+ changed = true;
43
+ log.info('Changing manager from %s to %s', rc.manager.module || rc.manager, manager.module || manager);
44
+ }
45
+ }
46
+ rc.manager = manager;
47
+ }
48
+
49
+ if (!configDir) {
50
+ configDir = rc.configDir;
51
+ }
52
+
53
+ if (configDir && configDir !== rc.configDir) {
54
+ if (rc.configDir) {
55
+ log.info('Changing configDir from %s to %s', rc.configDir, configDir);
56
+ }
57
+ changed = true;
58
+ rc.configDir = configDir;
59
+ } else if (!rc.configDir) {
60
+ changed = true;
61
+ configDir = './greenlock.d';
62
+ rc.configDir = configDir;
63
+ }
64
+
65
+ if (changed) {
66
+ fs.writeFileSync(rcpath, JSON.stringify(rc));
67
+ }
68
+
69
+ return rc;
70
+ };