2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
SettingsError : SettingsError ,
2017-02-07 10:30:52 -08:00
initialize : initialize ,
uninitialize : uninitialize ,
2017-03-09 15:43:13 -08:00
getEmailStatus : getEmailStatus ,
2016-12-09 09:19:23 +01:00
2015-07-20 00:09:47 -07:00
getAutoupdatePattern : getAutoupdatePattern ,
setAutoupdatePattern : setAutoupdatePattern ,
getTimeZone : getTimeZone ,
setTimeZone : setTimeZone ,
getCloudronName : getCloudronName ,
setCloudronName : setCloudronName ,
getCloudronAvatar : getCloudronAvatar ,
setCloudronAvatar : setCloudronAvatar ,
2015-07-23 12:42:52 +02:00
getDeveloperMode : getDeveloperMode ,
setDeveloperMode : setDeveloperMode ,
2015-10-26 00:44:54 -07:00
getDnsConfig : getDnsConfig ,
setDnsConfig : setDnsConfig ,
2017-01-02 13:05:48 +01:00
getDynamicDnsConfig : getDynamicDnsConfig ,
setDynamicDnsConfig : setDynamicDnsConfig ,
2015-11-07 18:02:45 -08:00
getBackupConfig : getBackupConfig ,
setBackupConfig : setBackupConfig ,
2015-12-11 22:25:22 -08:00
getTlsConfig : getTlsConfig ,
2015-12-11 22:32:34 -08:00
setTlsConfig : setTlsConfig ,
2015-12-11 22:25:22 -08:00
2016-01-23 05:06:09 -08:00
getUpdateConfig : getUpdateConfig ,
setUpdateConfig : setUpdateConfig ,
2016-07-26 14:31:07 +02:00
getAppstoreConfig : getAppstoreConfig ,
setAppstoreConfig : setAppstoreConfig ,
2016-08-30 19:09:13 -07:00
getMailConfig : getMailConfig ,
setMailConfig : setMailConfig ,
2015-07-20 00:09:47 -07:00
getDefaultSync : getDefaultSync ,
getAll : getAll ,
AUTOUPDATE _PATTERN _KEY : 'autoupdate_pattern' ,
TIME _ZONE _KEY : 'time_zone' ,
CLOUDRON _NAME _KEY : 'cloudron_name' ,
2015-07-23 12:42:52 +02:00
DEVELOPER _MODE _KEY : 'developer_mode' ,
2015-10-26 00:59:20 -07:00
DNS _CONFIG _KEY : 'dns_config' ,
2017-01-02 13:05:48 +01:00
DYNAMIC _DNS _KEY : 'dynamic_dns' ,
2015-11-07 18:02:45 -08:00
BACKUP _CONFIG _KEY : 'backup_config' ,
2015-12-11 22:27:00 -08:00
TLS _CONFIG _KEY : 'tls_config' ,
2016-01-23 05:06:09 -08:00
UPDATE _CONFIG _KEY : 'update_config' ,
2016-07-26 14:31:07 +02:00
APPSTORE _CONFIG _KEY : 'appstore_config' ,
2016-08-30 19:09:13 -07:00
MAIL _CONFIG _KEY : 'mail_config' ,
2015-07-20 00:09:47 -07:00
2017-02-07 10:30:52 -08:00
events : null
2015-07-20 00:09:47 -07:00
} ;
var assert = require ( 'assert' ) ,
2017-02-01 19:09:08 -08:00
async = require ( 'async' ) ,
2016-10-11 11:36:25 +02:00
backups = require ( './backups.js' ) ,
2017-04-20 17:23:31 -07:00
BackupsError = backups . BackupsError ,
2015-07-20 00:09:47 -07:00
config = require ( './config.js' ) ,
2016-12-14 14:54:17 +01:00
constants = require ( './constants.js' ) ,
2015-07-20 00:09:47 -07:00
CronJob = require ( 'cron' ) . CronJob ,
DatabaseError = require ( './databaseerror.js' ) ,
2016-07-03 21:37:17 -05:00
debug = require ( 'debug' ) ( 'box:settings' ) ,
2017-05-26 15:15:01 -07:00
dig = require ( './dig.js' ) ,
2016-12-09 09:19:23 +01:00
cloudron = require ( './cloudron.js' ) ,
CloudronError = cloudron . CloudronError ,
2016-06-02 13:36:47 -07:00
moment = require ( 'moment-timezone' ) ,
2017-03-09 15:43:13 -08:00
net = require ( 'net' ) ,
2015-07-20 00:09:47 -07:00
paths = require ( './paths.js' ) ,
safe = require ( 'safetydance' ) ,
settingsdb = require ( './settingsdb.js' ) ,
2016-12-09 09:19:23 +01:00
subdomains = require ( './subdomains.js' ) ,
SubdomainError = subdomains . SubdomainError ,
2016-08-01 15:10:45 +02:00
superagent = require ( 'superagent' ) ,
2016-07-03 21:37:17 -05:00
sysinfo = require ( './sysinfo.js' ) ,
2015-07-20 00:09:47 -07:00
util = require ( 'util' ) ,
_ = require ( 'underscore' ) ;
var gDefaults = ( function ( ) {
var result = { } ;
result [ exports . AUTOUPDATE _PATTERN _KEY ] = '00 00 1,3,5,23 * * *' ;
2015-09-18 12:02:19 -07:00
result [ exports . TIME _ZONE _KEY ] = 'America/Los_Angeles' ;
2015-07-20 00:09:47 -07:00
result [ exports . CLOUDRON _NAME _KEY ] = 'Cloudron' ;
2016-06-01 18:36:42 -07:00
result [ exports . DEVELOPER _MODE _KEY ] = true ;
2017-01-02 13:05:48 +01:00
result [ exports . DYNAMIC _DNS _KEY ] = false ;
2016-12-14 13:24:45 -08:00
result [ exports . DNS _CONFIG _KEY ] = { provider : 'manual' } ;
2016-10-21 12:48:48 +02:00
result [ exports . BACKUP _CONFIG _KEY ] = {
provider : 'filesystem' ,
key : '' ,
2017-04-22 20:27:52 -07:00
backupFolder : '/var/backups' ,
retentionSecs : 172800
2016-10-21 12:48:48 +02:00
} ;
2016-10-21 13:36:26 +02:00
result [ exports . TLS _CONFIG _KEY ] = { provider : 'letsencrypt-prod' } ;
2016-01-23 05:07:12 -08:00
result [ exports . UPDATE _CONFIG _KEY ] = { prerelease : false } ;
2016-10-21 12:48:48 +02:00
result [ exports . APPSTORE _CONFIG _KEY ] = { } ;
2016-08-30 19:09:13 -07:00
result [ exports . MAIL _CONFIG _KEY ] = { enabled : false } ;
2015-07-20 00:09:47 -07:00
return result ;
} ) ( ) ;
2017-04-25 15:18:09 -07:00
var NOOP _CALLBACK = function ( error ) { if ( error ) debug ( error ) ; } ;
2015-07-20 00:09:47 -07:00
function SettingsError ( reason , errorOrMessage ) {
assert . strictEqual ( typeof reason , 'string' ) ;
assert ( errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined' ) ;
Error . call ( this ) ;
Error . captureStackTrace ( this , this . constructor ) ;
this . name = this . constructor . name ;
this . reason = reason ;
if ( typeof errorOrMessage === 'undefined' ) {
this . message = reason ;
} else if ( typeof errorOrMessage === 'string' ) {
this . message = errorOrMessage ;
} else {
this . message = 'Internal error' ;
this . nestedError = errorOrMessage ;
}
}
util . inherits ( SettingsError , Error ) ;
SettingsError . INTERNAL _ERROR = 'Internal Error' ;
2016-08-01 15:10:45 +02:00
SettingsError . EXTERNAL _ERROR = 'External Error' ;
2015-07-20 00:09:47 -07:00
SettingsError . NOT _FOUND = 'Not Found' ;
SettingsError . BAD _FIELD = 'Bad Field' ;
2017-02-07 10:30:52 -08:00
function initialize ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
exports . events = new ( require ( 'events' ) . EventEmitter ) ( ) ;
callback ( ) ;
}
function uninitialize ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
exports . events = null ;
callback ( ) ;
}
2017-03-09 15:43:13 -08:00
function getEmailStatus ( callback ) {
2016-12-09 09:19:23 +01:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-05-26 15:15:01 -07:00
var digOptions = { server : '127.0.0.1' , port : 53 , timeout : 5000 } ;
2017-03-09 15:43:13 -08:00
var records = { } , outboundPort25 = { } ;
2016-12-09 09:19:23 +01:00
var dkimKey = cloudron . readDkimPublicKeySync ( ) ;
if ( ! dkimKey ) return callback ( new CloudronError ( CloudronError . INTERNAL _ERROR , new Error ( 'Failed to read dkim public key' ) ) ) ;
2017-02-02 15:19:48 -08:00
function checkDkim ( callback ) {
2017-02-01 19:09:08 -08:00
records . dkim = {
2017-02-01 23:02:49 -08:00
domain : constants . DKIM _SELECTOR + '._domainkey.' + config . fqdn ( ) ,
2017-02-01 19:09:08 -08:00
type : 'TXT' ,
2017-05-26 15:15:01 -07:00
expected : '"v=DKIM1; t=s; p=' + dkimKey + '"' ,
2017-02-01 19:09:08 -08:00
value : null ,
status : false
} ;
2017-05-26 15:15:01 -07:00
dig . resolve ( records . dkim . domain , records . dkim . type , digOptions , function ( error , txtRecords ) {
2017-02-01 19:09:08 -08:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( null ) ; // not setup
if ( error ) return callback ( error ) ;
2016-12-14 14:54:17 +01:00
2017-02-08 14:02:28 -08:00
if ( Array . isArray ( txtRecords ) && txtRecords . length !== 0 ) {
2017-05-26 15:15:01 -07:00
records . dkim . value = txtRecords [ 0 ] ;
2017-02-08 14:02:28 -08:00
records . dkim . status = ( records . dkim . value === records . dkim . expected ) ;
}
2016-12-15 12:00:51 -08:00
2017-02-01 19:09:08 -08:00
callback ( ) ;
} ) ;
}
2016-12-14 14:54:17 +01:00
2017-02-02 15:19:48 -08:00
function checkSpf ( callback ) {
2017-02-01 19:09:08 -08:00
records . spf = {
2017-02-01 23:02:49 -08:00
domain : config . fqdn ( ) ,
2017-02-01 19:09:08 -08:00
type : 'TXT' ,
value : null ,
2017-05-26 15:15:01 -07:00
expected : '"v=spf1 a:' + config . adminFqdn ( ) + ' ~all"' ,
2017-02-01 19:09:08 -08:00
status : false
} ;
2016-12-09 09:19:23 +01:00
2017-02-15 18:27:37 -08:00
// https://agari.zendesk.com/hc/en-us/articles/202952749-How-long-can-my-SPF-record-be-
2017-05-26 15:15:01 -07:00
dig . resolve ( records . spf . domain , records . spf . type , digOptions , function ( error , txtRecords ) {
2017-02-01 19:09:08 -08:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( null ) ; // not setup
2016-12-09 09:19:23 +01:00
if ( error ) return callback ( error ) ;
2016-12-14 14:54:17 +01:00
2017-02-08 14:02:28 -08:00
if ( ! Array . isArray ( txtRecords ) ) return callback ( ) ;
2016-12-14 15:23:06 +01:00
2016-12-09 09:19:23 +01:00
var i ;
for ( i = 0 ; i < txtRecords . length ; i ++ ) {
2017-05-26 15:15:01 -07:00
if ( txtRecords [ i ] . indexOf ( '"v=spf1 ' ) !== 0 ) continue ; // not SPF
records . spf . value = txtRecords [ i ] ;
2017-02-15 18:27:37 -08:00
records . spf . status = records . spf . value . indexOf ( ' a:' + config . adminFqdn ( ) ) !== - 1 ;
2016-12-09 09:19:23 +01:00
break ;
}
if ( records . spf . status ) {
records . spf . expected = records . spf . value ;
2017-02-02 14:20:25 -08:00
} else if ( i !== txtRecords . length ) {
2017-05-26 15:15:01 -07:00
records . spf . expected = '"v=spf1 a:' + config . adminFqdn ( ) + ' ' + records . spf . value . slice ( '"v=spf1 ' . length ) ;
2016-12-09 09:19:23 +01:00
}
2016-12-14 14:54:17 +01:00
2017-02-01 19:09:08 -08:00
callback ( ) ;
2016-12-09 09:19:23 +01:00
} ) ;
2017-02-01 19:09:08 -08:00
}
2017-02-02 15:19:48 -08:00
function checkMx ( callback ) {
2017-02-01 19:09:08 -08:00
records . mx = {
2017-02-01 23:02:49 -08:00
domain : config . fqdn ( ) ,
2017-02-01 19:09:08 -08:00
type : 'MX' ,
value : null ,
2017-05-26 15:15:01 -07:00
expected : '10 ' + config . mailFqdn ( ) + '.' ,
2017-02-01 19:09:08 -08:00
status : false
} ;
2017-05-26 15:15:01 -07:00
dig . resolve ( records . mx . domain , records . mx . type , digOptions , function ( error , mxRecords ) {
2017-02-01 19:09:08 -08:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( null ) ; // not setup
if ( error ) return callback ( error ) ;
2017-02-08 14:02:28 -08:00
if ( Array . isArray ( mxRecords ) && mxRecords . length !== 0 ) {
2017-05-26 15:15:01 -07:00
records . mx . status = mxRecords . length == 1 && mxRecords [ 0 ] . exchange === ( config . mailFqdn ( ) + '.' ) ;
2017-02-08 14:02:28 -08:00
records . mx . value = mxRecords . map ( function ( r ) { return r . priority + ' ' + r . exchange ; } ) . join ( ' ' ) ;
}
2017-02-01 19:09:08 -08:00
callback ( ) ;
} ) ;
}
2017-02-02 15:19:48 -08:00
function checkDmarc ( callback ) {
2017-02-01 19:09:08 -08:00
records . dmarc = {
2017-02-01 23:02:49 -08:00
domain : '_dmarc.' + config . fqdn ( ) ,
2017-02-01 19:09:08 -08:00
type : 'TXT' ,
value : null ,
2017-05-26 15:15:01 -07:00
expected : '"v=DMARC1; p=reject; pct=100"' ,
2017-02-01 19:09:08 -08:00
status : false
} ;
2017-05-26 15:15:01 -07:00
dig . resolve ( records . dmarc . domain , records . dmarc . type , digOptions , function ( error , txtRecords ) {
2017-02-01 19:09:08 -08:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( null ) ; // not setup
if ( error ) return callback ( error ) ;
2017-02-08 14:02:28 -08:00
if ( Array . isArray ( txtRecords ) && txtRecords . length !== 0 ) {
2017-05-26 15:15:01 -07:00
records . dmarc . value = txtRecords [ 0 ] ;
2017-02-08 16:55:08 +01:00
records . dmarc . status = ( records . dmarc . value === records . dmarc . expected ) ;
}
2017-02-01 19:09:08 -08:00
callback ( ) ;
} ) ;
}
function checkPtr ( callback ) {
records . ptr = {
2017-02-01 23:02:49 -08:00
domain : null ,
2017-02-01 19:09:08 -08:00
type : 'PTR' ,
value : null ,
2017-05-26 15:15:01 -07:00
expected : config . mailFqdn ( ) + '.' ,
2017-02-01 19:09:08 -08:00
status : false
} ;
2017-02-23 22:03:44 -08:00
sysinfo . getPublicIp ( function ( error , ip ) {
2017-02-01 19:09:08 -08:00
if ( error ) return callback ( error ) ;
2017-02-01 23:02:49 -08:00
records . ptr . domain = ip . split ( '.' ) . reverse ( ) . join ( '.' ) + '.in-addr.arpa' ;
2017-05-26 15:15:01 -07:00
dig . resolve ( ip , 'PTR' , digOptions , function ( error , ptrRecords ) {
2017-02-01 19:09:08 -08:00
if ( error && error . code === 'ENOTFOUND' ) return callback ( null ) ; // not setup
if ( error ) return callback ( error ) ;
2017-02-08 14:02:28 -08:00
if ( Array . isArray ( ptrRecords ) && ptrRecords . length !== 0 ) {
records . ptr . value = ptrRecords . join ( ' ' ) ;
2017-05-26 15:15:01 -07:00
records . ptr . status = ptrRecords . some ( function ( v ) { return v === records . ptr . expected ; } ) ;
2017-02-08 14:02:28 -08:00
}
2017-02-01 19:09:08 -08:00
return callback ( ) ;
} ) ;
} ) ;
}
2017-03-09 15:43:13 -08:00
function checkOutbound25 ( callback ) {
2017-04-19 14:35:19 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-03-09 15:43:13 -08:00
var smtpServer = _ . sample ( [
'smtp.gmail.com' ,
'smtp.live.com' ,
'smtp.mail.yahoo.com' ,
'smtp.o2.ie' ,
'smtp.comcast.net' ,
'outgoing.verizon.net'
] ) ;
outboundPort25 = {
value : 'OK' ,
status : false
} ;
var client = new net . Socket ( ) ;
client . setTimeout ( 5000 ) ;
client . connect ( 25 , smtpServer ) ;
client . on ( 'connect' , function ( ) {
outboundPort25 . status = true ;
outboundPort25 . value = 'OK' ;
2017-04-19 09:14:00 -07:00
client . destroy ( ) ; // do not use end() because it still triggers timeout
2017-03-09 15:43:13 -08:00
callback ( ) ;
} ) ;
client . on ( 'timeout' , function ( ) {
outboundPort25 . status = false ;
outboundPort25 . value = 'Connect to ' + smtpServer + ' timed out' ;
client . destroy ( ) ;
callback ( new Error ( 'Timeout' ) ) ;
} ) ;
client . on ( 'error' , function ( error ) {
outboundPort25 . status = false ;
outboundPort25 . value = 'Connect to ' + smtpServer + ' failed: ' + error . message ;
client . destroy ( ) ;
callback ( error ) ;
} ) ;
}
2017-02-02 11:31:25 -08:00
function ignoreError ( what , func ) {
2017-02-01 23:38:06 -08:00
return function ( callback ) {
func ( function ( error ) {
2017-02-06 21:53:29 -08:00
if ( error ) debug ( 'Ignored error - ' + what + ':' , error ) ;
2017-02-01 23:38:06 -08:00
callback ( ) ;
} ) ;
} ;
}
2017-02-02 11:28:35 -08:00
async . parallel ( [
2017-02-02 15:19:48 -08:00
ignoreError ( 'mx' , checkMx ) ,
ignoreError ( 'spf' , checkSpf ) ,
ignoreError ( 'dmarc' , checkDmarc ) ,
ignoreError ( 'dkim' , checkDkim ) ,
2017-03-09 15:43:13 -08:00
ignoreError ( 'ptr' , checkPtr ) ,
ignoreError ( 'port25' , checkOutbound25 )
2017-02-01 23:38:06 -08:00
] , function ( ) {
2017-03-09 15:43:13 -08:00
callback ( null , { dns : records , outboundPort25 : outboundPort25 } ) ;
2016-12-09 09:19:23 +01:00
} ) ;
}
2015-07-20 00:09:47 -07:00
function setAutoupdatePattern ( pattern , callback ) {
assert . strictEqual ( typeof pattern , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-01-26 15:36:24 -08:00
if ( pattern !== constants . AUTOUPDATE _PATTERN _NEVER ) { // check if pattern is valid
2015-07-20 00:09:47 -07:00
var job = safe . safeCall ( function ( ) { return new CronJob ( pattern ) ; } ) ;
if ( ! job ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'Invalid pattern' ) ) ;
}
settingsdb . set ( exports . AUTOUPDATE _PATTERN _KEY , pattern , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . AUTOUPDATE _PATTERN _KEY , pattern ) ;
return callback ( null ) ;
} ) ;
}
function getAutoupdatePattern ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . AUTOUPDATE _PATTERN _KEY , function ( error , pattern ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . AUTOUPDATE _PATTERN _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , pattern ) ;
} ) ;
}
function setTimeZone ( tz , callback ) {
assert . strictEqual ( typeof tz , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-06-02 13:36:47 -07:00
if ( moment . tz . names ( ) . indexOf ( tz ) === - 1 ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'Bad timeZone' ) ) ;
2015-07-20 00:09:47 -07:00
settingsdb . set ( exports . TIME _ZONE _KEY , tz , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . TIME _ZONE _KEY , tz ) ;
return callback ( null ) ;
} ) ;
}
function getTimeZone ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . TIME _ZONE _KEY , function ( error , tz ) {
2016-05-03 12:09:58 -07:00
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . TIME _ZONE _KEY ] ) ;
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , tz ) ;
} ) ;
}
function getCloudronName ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . CLOUDRON _NAME _KEY , function ( error , name ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . CLOUDRON _NAME _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , name ) ;
} ) ;
}
function setCloudronName ( name , callback ) {
assert . strictEqual ( typeof name , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-06-02 12:51:39 -07:00
if ( ! name ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'name is empty' ) ) ;
// some arbitrary restrictions (for sake of ui layout)
if ( name . length > 32 ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'name cannot exceed 32 characters' ) ) ;
2015-07-20 00:09:47 -07:00
settingsdb . set ( exports . CLOUDRON _NAME _KEY , name , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . CLOUDRON _NAME _KEY , name ) ;
return callback ( null ) ;
} ) ;
}
function getCloudronAvatar ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
var avatar = safe . fs . readFileSync ( paths . CLOUDRON _AVATAR _FILE ) ;
if ( avatar ) return callback ( null , avatar ) ;
// try default fallback
avatar = safe . fs . readFileSync ( paths . CLOUDRON _DEFAULT _AVATAR _FILE ) ;
if ( avatar ) return callback ( null , avatar ) ;
callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , safe . error ) ) ;
}
function setCloudronAvatar ( avatar , callback ) {
assert ( util . isBuffer ( avatar ) ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
if ( ! safe . fs . writeFileSync ( paths . CLOUDRON _AVATAR _FILE , avatar ) ) {
return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , safe . error ) ) ;
}
return callback ( null ) ;
}
2015-07-23 12:42:52 +02:00
function getDeveloperMode ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . DEVELOPER _MODE _KEY , function ( error , enabled ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . DEVELOPER _MODE _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
// settingsdb holds string values only
callback ( null , ! ! enabled ) ;
} ) ;
}
function setDeveloperMode ( enabled , callback ) {
assert . strictEqual ( typeof enabled , 'boolean' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
// settingsdb takes string values only
settingsdb . set ( exports . DEVELOPER _MODE _KEY , enabled ? 'enabled' : '' , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . DEVELOPER _MODE _KEY , enabled ) ;
return callback ( null ) ;
} ) ;
}
2015-10-26 00:44:54 -07:00
function getDnsConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2015-10-26 00:59:20 -07:00
settingsdb . get ( exports . DNS _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . DNS _CONFIG _KEY ] ) ;
2015-10-26 00:44:54 -07:00
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
2017-01-12 11:19:27 -08:00
callback ( null , JSON . parse ( value ) ) ;
2015-10-26 00:44:54 -07:00
} ) ;
}
2017-01-10 16:23:01 -08:00
function setDnsConfig ( dnsConfig , domain , callback ) {
2015-10-26 00:44:54 -07:00
assert . strictEqual ( typeof dnsConfig , 'object' ) ;
2017-01-10 16:23:01 -08:00
assert . strictEqual ( typeof domain , 'string' ) ;
2015-10-26 00:44:54 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-02-23 22:03:44 -08:00
sysinfo . getPublicIp ( function ( error , ip ) {
2017-01-10 11:32:44 +01:00
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , 'Error getting IP:' + error . message ) ) ;
2016-07-03 21:37:17 -05:00
2017-01-10 16:23:01 -08:00
subdomains . verifyDnsConfig ( dnsConfig , domain , ip , function ( error , result ) {
2017-01-10 11:32:44 +01:00
if ( error && error . reason === SubdomainError . ACCESS _DENIED ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'Error adding A record. Access denied' ) ) ;
if ( error && error . reason === SubdomainError . NOT _FOUND ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'Zone not found' ) ) ;
if ( error && error . reason === SubdomainError . EXTERNAL _ERROR ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'Error adding A record:' + error . message ) ) ;
if ( error && error . reason === SubdomainError . BAD _FIELD ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , error . message ) ) ;
if ( error && error . reason === SubdomainError . INVALID _PROVIDER ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , error . message ) ) ;
2016-07-03 21:37:17 -05:00
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
2015-10-26 00:44:54 -07:00
2017-01-10 11:32:44 +01:00
settingsdb . set ( exports . DNS _CONFIG _KEY , JSON . stringify ( result ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . DNS _CONFIG _KEY , dnsConfig ) ;
2015-10-29 16:21:35 -07:00
2017-04-25 15:18:09 -07:00
cloudron . configureWebadmin ( NOOP _CALLBACK ) ; // do not block
2017-01-10 11:32:44 +01:00
callback ( null ) ;
} ) ;
2016-07-03 21:37:17 -05:00
} ) ;
2015-10-26 00:44:54 -07:00
} ) ;
}
2017-01-02 13:05:48 +01:00
function getDynamicDnsConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . DYNAMIC _DNS _KEY , function ( error , enabled ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . DYNAMIC _DNS _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
// settingsdb holds string values only
callback ( null , ! ! enabled ) ;
} ) ;
}
function setDynamicDnsConfig ( enabled , callback ) {
assert . strictEqual ( typeof enabled , 'boolean' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
// settingsdb takes string values only
settingsdb . set ( exports . DYNAMIC _DNS _KEY , enabled ? 'enabled' : '' , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . DYNAMIC _DNS _KEY , enabled ) ;
return callback ( null ) ;
} ) ;
}
2015-12-11 22:14:53 -08:00
function getTlsConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . TLS _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . TLS _CONFIG _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , JSON . parse ( value ) ) ; // provider
} ) ;
}
2015-12-11 22:32:34 -08:00
function setTlsConfig ( tlsConfig , callback ) {
assert . strictEqual ( typeof tlsConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-12-05 17:01:23 +01:00
if ( tlsConfig . provider !== 'fallback' && tlsConfig . provider !== 'caas' && tlsConfig . provider . indexOf ( 'le-' ) !== 0 ) {
return callback ( new SettingsError ( SettingsError . BAD _FIELD , 'provider must be caas, fallback or le-*' ) ) ;
2015-12-11 22:32:34 -08:00
}
settingsdb . set ( exports . TLS _CONFIG _KEY , JSON . stringify ( tlsConfig ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . TLS _CONFIG _KEY , tlsConfig ) ;
callback ( null ) ;
} ) ;
}
2015-11-07 18:02:45 -08:00
function getBackupConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . BACKUP _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . BACKUP _CONFIG _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , JSON . parse ( value ) ) ; // provider, token, key, region, prefix, bucket
} ) ;
}
function setBackupConfig ( backupConfig , callback ) {
assert . strictEqual ( typeof backupConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-10-11 11:47:33 +02:00
backups . testConfig ( backupConfig , function ( error ) {
2017-04-20 17:23:31 -07:00
if ( error && error . reason === BackupsError . BAD _FIELD ) return callback ( new SettingsError ( SettingsError . BAD _FIELD , error . message ) ) ;
if ( error && error . reason === BackupsError . EXTERNAL _ERROR ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , error . message ) ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
2015-11-07 18:02:45 -08:00
2016-12-19 12:41:35 -08:00
settingsdb . set ( exports . BACKUP _CONFIG _KEY , JSON . stringify ( backupConfig ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
2016-10-11 15:56:07 +02:00
2016-12-19 12:41:35 -08:00
exports . events . emit ( exports . BACKUP _CONFIG _KEY , backupConfig ) ;
2016-10-11 15:56:07 +02:00
2016-12-19 12:41:35 -08:00
callback ( null ) ;
2016-10-11 11:36:25 +02:00
} ) ;
2015-11-07 18:02:45 -08:00
} ) ;
}
2016-01-23 05:06:09 -08:00
function getUpdateConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . UPDATE _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . UPDATE _CONFIG _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , JSON . parse ( value ) ) ; // { prerelease }
} ) ;
}
function setUpdateConfig ( updateConfig , callback ) {
assert . strictEqual ( typeof updateConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . set ( exports . UPDATE _CONFIG _KEY , JSON . stringify ( updateConfig ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . UPDATE _CONFIG _KEY , updateConfig ) ;
callback ( null ) ;
} ) ;
}
2016-08-30 19:09:13 -07:00
function getMailConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . MAIL _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . MAIL _CONFIG _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , JSON . parse ( value ) ) ;
} ) ;
}
function setMailConfig ( mailConfig , callback ) {
assert . strictEqual ( typeof mailConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . set ( exports . MAIL _CONFIG _KEY , JSON . stringify ( mailConfig ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . MAIL _CONFIG _KEY , mailConfig ) ;
callback ( null ) ;
} ) ;
}
2016-07-26 14:31:07 +02:00
function getAppstoreConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . get ( exports . APPSTORE _CONFIG _KEY , function ( error , value ) {
if ( error && error . reason === DatabaseError . NOT _FOUND ) return callback ( null , gDefaults [ exports . APPSTORE _CONFIG _KEY ] ) ;
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
callback ( null , JSON . parse ( value ) ) ;
} ) ;
}
function setAppstoreConfig ( appstoreConfig , callback ) {
assert . strictEqual ( typeof appstoreConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-08-01 15:10:45 +02:00
getAppstoreConfig ( function ( error , oldConfig ) {
if ( error ) return callback ( error ) ;
2016-07-26 14:31:07 +02:00
2016-08-01 15:10:45 +02:00
var cloudronId = oldConfig . cloudronId ;
2016-07-26 14:31:07 +02:00
2016-08-01 15:10:45 +02:00
function setNewConfig ( ) {
var data = {
userId : appstoreConfig . userId ,
token : appstoreConfig . token ,
cloudronId : cloudronId
} ;
settingsdb . set ( exports . APPSTORE _CONFIG _KEY , JSON . stringify ( data ) , function ( error ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
exports . events . emit ( exports . APPSTORE _CONFIG _KEY , appstoreConfig ) ;
callback ( null ) ;
} ) ;
}
function registerCloudron ( ) {
const url = config . apiServerOrigin ( ) + '/api/v1/users/' + appstoreConfig . userId + '/cloudrons' ;
const data = {
domain : config . fqdn ( )
} ;
2016-09-12 12:53:51 -07:00
superagent . post ( url ) . send ( data ) . query ( { accessToken : appstoreConfig . token } ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
2016-08-01 15:10:45 +02:00
if ( error && ! error . response ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , error . message ) ) ;
if ( result . statusCode === 401 ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , 'invalid appstore token' ) ) ;
if ( result . statusCode !== 201 ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , 'unable to register cloudron' ) ) ;
cloudronId = result . body . cloudron . id ;
setNewConfig ( ) ;
} ) ;
}
if ( ! cloudronId ) return registerCloudron ( ) ;
// verify that cloudron belongs to this user
const url = config . apiServerOrigin ( ) + '/api/v1/users/' + appstoreConfig . userId + '/cloudrons/' + oldConfig . cloudronId ;
2016-09-12 12:53:51 -07:00
superagent . get ( url ) . query ( { accessToken : appstoreConfig . token } ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
2016-08-01 15:10:45 +02:00
if ( error && ! error . response ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , error . message ) ) ;
if ( result . statusCode === 401 ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , 'invalid appstore token' ) ) ;
if ( result . statusCode === 403 ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , 'wrong user' ) ) ;
if ( result . statusCode === 404 ) return registerCloudron ( ) ;
if ( result . statusCode !== 200 ) return callback ( new SettingsError ( SettingsError . EXTERNAL _ERROR , 'unknown error' ) ) ;
setNewConfig ( ) ;
} ) ;
2016-07-26 14:31:07 +02:00
} ) ;
2016-08-01 15:10:45 +02:00
2016-07-26 14:31:07 +02:00
}
2015-07-20 00:09:47 -07:00
function getDefaultSync ( name ) {
assert . strictEqual ( typeof name , 'string' ) ;
return gDefaults [ name ] ;
}
function getAll ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settingsdb . getAll ( function ( error , settings ) {
if ( error ) return callback ( new SettingsError ( SettingsError . INTERNAL _ERROR , error ) ) ;
var result = _ . extend ( { } , gDefaults ) ;
settings . forEach ( function ( setting ) { result [ setting . name ] = setting . value ; } ) ;
2017-01-02 13:47:49 +01:00
// convert booleans
2017-01-02 14:15:20 +01:00
result [ exports . DEVELOPER _MODE _KEY ] = ! ! result [ exports . DEVELOPER _MODE _KEY ] ;
result [ exports . DYNAMIC _DNS _KEY ] = ! ! result [ exports . DYNAMIC _DNS _KEY ] ;
2017-01-02 13:47:49 +01:00
2017-01-10 15:18:43 +01:00
// convert JSON objects
2017-01-31 16:36:04 -08:00
[ exports . DNS _CONFIG _KEY , exports . TLS _CONFIG _KEY , exports . BACKUP _CONFIG _KEY , exports . MAIL _CONFIG _KEY ] . forEach ( function ( key ) {
2017-01-10 15:18:43 +01:00
result [ key ] = typeof result [ key ] === 'object' ? result [ key ] : safe . JSON . parse ( result [ key ] ) ;
} ) ;
2015-07-20 00:09:47 -07:00
callback ( null , result ) ;
} ) ;
}