2020-04-22 04:33:27 -04:00
/ * *
* @ description MeshCentral SMS gateway communication module
* @ author Ylian Saint - Hilaire
2022-01-24 02:21:24 -05:00
* @ copyright Intel Corporation 2018 - 2022
2020-04-22 04:33:27 -04:00
* @ license Apache - 2.0
* @ version v0 . 0.1
* /
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2020-04-22 18:29:26 -04:00
/ *
// For Twilio, add this in config.json
"sms" : {
"provider" : "twilio" ,
"sid" : "ACxxxxxxxxx" ,
"auth" : "xxxxxxx" ,
"from" : "+15555555555"
} ,
// For Plivo, add this in config.json
"sms" : {
"provider" : "plivo" ,
"id" : "xxxxxxx" ,
"token" : "xxxxxxx" ,
"from" : "15555555555"
}
2021-11-27 14:06:56 -05:00
// For Telnyx, add this in config.json
"sms" : {
"provider" : "telnyx" ,
"apikey" : "xxxxxxx" ,
"from" : "15555555555"
}
2020-04-22 18:29:26 -04:00
* /
2020-04-22 04:33:27 -04:00
// Construct a MeshAgent object, called upon connection
module . exports . CreateMeshSMS = function ( parent ) {
var obj = { } ;
obj . parent = parent ;
obj . provider = null ;
// SMS gateway provider setup
switch ( parent . config . sms . provider ) {
case 'twilio' : {
// Validate Twilio configuration values
if ( typeof parent . config . sms . sid != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider sid.' ) ; return null ; }
if ( typeof parent . config . sms . auth != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider auth.' ) ; return null ; }
if ( typeof parent . config . sms . from != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider from.' ) ; return null ; }
// Setup Twilio
var Twilio = require ( 'twilio' ) ;
obj . provider = new Twilio ( parent . config . sms . sid , parent . config . sms . auth ) ;
break ;
}
2020-04-22 18:29:26 -04:00
case 'plivo' : {
// Validate Plivo configuration values
if ( typeof parent . config . sms . id != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider id.' ) ; return null ; }
if ( typeof parent . config . sms . token != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider token.' ) ; return null ; }
if ( typeof parent . config . sms . from != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider from.' ) ; return null ; }
2020-04-23 01:15:51 -04:00
// Setup Plivo
2020-04-22 18:29:26 -04:00
var plivo = require ( 'plivo' ) ;
obj . provider = new plivo . Client ( parent . config . sms . id , parent . config . sms . token ) ;
break ;
}
2021-11-27 14:06:56 -05:00
case 'telnyx' : {
// Validate Telnyx configuration values
if ( typeof parent . config . sms . apikey != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider apikey.' ) ; return null ; }
if ( typeof parent . config . sms . from != 'string' ) { console . log ( 'Invalid or missing SMS gateway provider from.' ) ; return null ; }
// Setup Telnyx
obj . provider = require ( 'telnyx' ) ( parent . config . sms . apikey ) ;
break ;
}
2022-09-01 16:31:10 -04:00
case 'url' : {
// Validate URL configuration values
2022-09-02 18:06:30 -04:00
if ( parent . config . sms . url != 'console' ) {
if ( typeof parent . config . sms . url != 'string' ) { console . log ( 'Invalid or missing SMS gateway URL value.' ) ; return null ; }
if ( ! parent . config . sms . url . toLowerCase ( ) . startsWith ( 'http://' ) && ! parent . config . sms . url . toLowerCase ( ) . startsWith ( 'https://' ) ) { console . log ( 'Invalid or missing SMS gateway, URL must start with http:// or https://.' ) ; return null ; }
if ( parent . config . sms . url . indexOf ( '{{message}}' ) == - 1 ) { console . log ( 'Invalid or missing SMS gateway, URL must include {{message}}.' ) ; return null ; }
if ( parent . config . sms . url . indexOf ( '{{phone}}' ) == - 1 ) { console . log ( 'Invalid or missing SMS gateway, URL must include {{phone}}.' ) ; return null ; }
}
2022-09-01 16:31:10 -04:00
break ;
}
2020-04-22 04:33:27 -04:00
default : {
// Unknown SMS gateway provider
console . log ( 'Unknown SMS gateway provider: ' + parent . config . sms . provider ) ;
return null ;
}
}
// Send an SMS message
obj . sendSMS = function ( to , msg , func ) {
parent . debug ( 'email' , 'Sending SMS to: ' + to + ': ' + msg ) ;
2020-04-22 18:29:26 -04:00
if ( parent . config . sms . provider == 'twilio' ) { // Twilio
2020-04-22 04:33:27 -04:00
obj . provider . messages . create ( {
from : parent . config . sms . from ,
to : to ,
body : msg
} , function ( err , result ) {
2020-04-22 18:29:26 -04:00
if ( err != null ) { parent . debug ( 'email' , 'SMS error: ' + err . message ) ; } else { parent . debug ( 'email' , 'SMS result: ' + JSON . stringify ( result ) ) ; }
if ( func != null ) { func ( ( err == null ) && ( result . status == 'queued' ) , err ? err . message : null , result ) ; }
2020-04-22 04:33:27 -04:00
} ) ;
2020-04-22 18:29:26 -04:00
} else if ( parent . config . sms . provider == 'plivo' ) { // Plivo
if ( to . split ( '-' ) . join ( '' ) . split ( ' ' ) . join ( '' ) . split ( '+' ) . join ( '' ) . length == 10 ) { to = '1' + to ; } // If we only have 10 digits, add a 1 in front.
obj . provider . messages . create (
parent . config . sms . from ,
to ,
msg
) . then ( function ( result ) {
parent . debug ( 'email' , 'SMS result: ' + JSON . stringify ( result ) ) ;
if ( func != null ) { func ( ( result != null ) && ( result . messageUuid != null ) , null , result ) ; }
}
) . catch ( function ( err ) {
var msg = null ;
if ( ( err != null ) && err . message ) { msg = JSON . parse ( err . message ) . error ; }
parent . debug ( 'email' , 'SMS error: ' + msg ) ;
if ( func != null ) { func ( false , msg , null ) ; }
}
) ;
2021-11-27 14:06:56 -05:00
} else if ( parent . config . sms . provider == 'telnyx' ) { // Telnyx
obj . provider . messages . create ( {
from : parent . config . sms . from ,
to : to ,
text : msg
} , function ( err , result ) {
if ( err != null ) { parent . debug ( 'email' , 'SMS error: ' + err . type ) ; } else { parent . debug ( 'email' , 'SMS result: ' + JSON . stringify ( result ) ) ; }
if ( func != null ) { func ( ( err == null ) , err ? err . type : null , result ) ; }
} ) ;
2022-09-01 16:31:10 -04:00
} else if ( parent . config . sms . provider == 'url' ) { // URL
2022-09-02 18:06:30 -04:00
if ( parent . config . sms . url == 'console' ) {
// This is for debugging, just display the SMS to the console
console . log ( 'SMS (' + to + '): ' + msg ) ;
if ( func != null ) { func ( true , null , null ) ; }
2022-09-01 16:31:10 -04:00
} else {
2022-09-02 18:06:30 -04:00
var sms = parent . config . sms . url . split ( '{{phone}}' ) . join ( encodeURIComponent ( to ) ) . split ( '{{message}}' ) . join ( encodeURIComponent ( msg ) ) ;
parent . debug ( 'email' , 'SMS URL: ' + sms ) ;
sms = require ( 'url' ) . parse ( sms ) ;
if ( sms . protocol == 'https:' ) {
// HTTPS GET request
const options = { hostname : sms . hostname , port : sms . port ? sms . port : 443 , path : sms . path , method : 'GET' , rejectUnauthorized : false } ;
const request = require ( 'https' ) . request ( options , function ( res ) { parent . debug ( 'email' , 'SMS result: ' + res . statusCode ) ; if ( func != null ) { func ( res . statusCode == 200 , ( res . statusCode == 200 ) ? null : res . statusCode , null ) ; } res . on ( 'data' , function ( d ) { } ) ; } ) ;
request . on ( 'error' , function ( err ) { parent . debug ( 'email' , 'SMS error: ' + err ) ; if ( func != null ) { func ( false , err , null ) ; } } ) ;
request . end ( ) ;
} else {
// HTTP GET request
const options = { hostname : sms . hostname , port : sms . port ? sms . port : 80 , path : sms . path , method : 'GET' } ;
const request = require ( 'http' ) . request ( options , function ( res ) { parent . debug ( 'email' , 'SMS result: ' + res . statusCode ) ; if ( func != null ) { func ( res . statusCode == 200 , ( res . statusCode == 200 ) ? null : res . statusCode , null ) ; } res . on ( 'data' , function ( d ) { } ) ; } ) ;
request . on ( 'error' , function ( err ) { parent . debug ( 'email' , 'SMS error: ' + err ) ; if ( func != null ) { func ( false , err , null ) ; } } ) ;
request . end ( ) ;
}
2022-09-01 16:31:10 -04:00
}
2020-04-22 04:33:27 -04:00
}
}
// Get the correct SMS template
function getTemplate ( templateNumber , domain , lang ) {
parent . debug ( 'email' , 'Getting SMS template #' + templateNumber + ', lang: ' + lang ) ;
if ( Array . isArray ( lang ) ) { lang = lang [ 0 ] ; } // TODO: For now, we only use the first language given.
var r = { } , emailsPath = null ;
if ( ( domain != null ) && ( domain . webemailspath != null ) ) { emailsPath = domain . webemailspath ; }
else if ( obj . parent . webEmailsOverridePath != null ) { emailsPath = obj . parent . webEmailsOverridePath ; }
else if ( obj . parent . webEmailsPath != null ) { emailsPath = obj . parent . webEmailsPath ; }
if ( ( emailsPath == null ) || ( obj . parent . fs . existsSync ( emailsPath ) == false ) ) { return null }
// Get the non-english email if needed
var txtfile = null ;
if ( ( lang != null ) && ( lang != 'en' ) ) {
var translationsPath = obj . parent . path . join ( emailsPath , 'translations' ) ;
var translationsPathTxt = obj . parent . path . join ( emailsPath , 'translations' , 'sms-messages_' + lang + '.txt' ) ;
if ( obj . parent . fs . existsSync ( translationsPath ) && obj . parent . fs . existsSync ( translationsPathTxt ) ) {
txtfile = obj . parent . fs . readFileSync ( translationsPathTxt ) . toString ( ) ;
}
}
// Get the english email
2020-04-22 05:23:02 -04:00
if ( txtfile == null ) {
2020-04-22 04:33:27 -04:00
var pathTxt = obj . parent . path . join ( emailsPath , 'sms-messages.txt' ) ;
if ( obj . parent . fs . existsSync ( pathTxt ) ) {
txtfile = obj . parent . fs . readFileSync ( pathTxt ) . toString ( ) ;
}
}
// No email templates
if ( txtfile == null ) { return null ; }
// Decode the TXT file
2020-04-22 05:23:02 -04:00
var lines = txtfile . split ( '\r\n' ) . join ( '\n' ) . split ( '\n' )
if ( lines . length <= templateNumber ) return null ;
2020-04-22 04:33:27 -04:00
return lines [ templateNumber ] ;
}
// Send phone number verification SMS
obj . sendPhoneCheck = function ( domain , phoneNumber , verificationCode , language , func ) {
parent . debug ( 'email' , "Sending verification SMS to " + phoneNumber ) ;
2020-04-22 05:23:02 -04:00
var sms = getTemplate ( 0 , domain , language ) ;
if ( sms == null ) { parent . debug ( 'email' , "Error: Failed to get SMS template" ) ; return ; } // No SMS template found
2020-04-22 04:33:27 -04:00
// Setup the template
2020-04-22 05:23:02 -04:00
sms = sms . split ( '[[0]]' ) . join ( domain . title ? domain . title : 'MeshCentral' ) ;
sms = sms . split ( '[[1]]' ) . join ( verificationCode ) ;
2020-04-22 04:33:27 -04:00
// Send the SMS
2020-04-22 05:23:02 -04:00
obj . sendSMS ( phoneNumber , sms , func ) ;
2020-04-22 04:33:27 -04:00
} ;
2020-04-22 18:29:26 -04:00
// Send phone number verification SMS
obj . sendToken = function ( domain , phoneNumber , verificationCode , language , func ) {
parent . debug ( 'email' , "Sending login token SMS to " + phoneNumber ) ;
var sms = getTemplate ( 1 , domain , language ) ;
if ( sms == null ) { parent . debug ( 'email' , "Error: Failed to get SMS template" ) ; return ; } // No SMS template found
// Setup the template
sms = sms . split ( '[[0]]' ) . join ( domain . title ? domain . title : 'MeshCentral' ) ;
sms = sms . split ( '[[1]]' ) . join ( verificationCode ) ;
// Send the SMS
obj . sendSMS ( phoneNumber , sms , func ) ;
} ;
2020-04-22 04:33:27 -04:00
return obj ;
} ;