2016-10-04 22:02:51 +00:00
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
# pragma once
2017-09-22 08:29:37 +00:00
# include <mutex>
# include <condition_variable>
2016-10-04 22:02:51 +00:00
2017-01-23 11:37:13 +00:00
# include "event_loop_dispatcher.hpp"
2016-10-20 00:55:46 +00:00
# include "platform.hpp"
2016-10-04 22:02:51 +00:00
# include "js_class.hpp"
# include "js_collection.hpp"
2016-10-20 00:55:46 +00:00
# include "sync/sync_manager.hpp"
# include "sync/sync_config.hpp"
# include "sync/sync_session.hpp"
# include "sync/sync_user.hpp"
2018-02-20 12:40:54 +00:00
# include "sync/partial_sync.hpp"
2016-10-04 22:02:51 +00:00
# include "realm/util/logger.hpp"
# include "realm/util/uri.hpp"
2018-04-25 09:21:52 +00:00
# if REALM_ANDROID
# include <jni.h>
# include "./android/io_realm_react_RealmReactModule.h"
# include "./android/jni_utils.hpp"
extern jclass ssl_helper_class ;
# endif
2016-10-04 22:02:51 +00:00
namespace realm {
namespace js {
2017-09-27 20:31:27 +00:00
inline realm : : SyncManager & syncManagerShared ( ) {
2017-10-11 12:54:52 +00:00
static std : : once_flag flag ;
std : : call_once ( flag , [ ] {
2017-09-27 18:16:44 +00:00
ensure_directory_exists_for_file ( default_realm_file_directory ( ) ) ;
2017-09-27 19:02:09 +00:00
SyncManager : : shared ( ) . configure_file_system ( default_realm_file_directory ( ) , SyncManager : : MetadataMode : : NoEncryption ) ;
2017-10-11 12:54:52 +00:00
} ) ;
2017-09-27 19:02:09 +00:00
return SyncManager : : shared ( ) ;
2017-09-27 18:16:44 +00:00
}
2017-02-01 13:18:59 +00:00
using SharedUser = std : : shared_ptr < realm : : SyncUser > ;
using WeakSession = std : : weak_ptr < realm : : SyncSession > ;
2016-10-20 00:55:46 +00:00
template < typename T >
class UserClass : public ClassDefinition < T , SharedUser > {
using GlobalContextType = typename T : : GlobalContext ;
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using Function = js : : Function < T > ;
using ReturnValue = js : : ReturnValue < T > ;
2018-01-11 15:00:31 +00:00
using Arguments = js : : Arguments < T > ;
2016-10-20 00:55:46 +00:00
public :
std : : string const name = " User " ;
static FunctionType create_constructor ( ContextType ) ;
static void get_server ( ContextType , ObjectType , ReturnValue & ) ;
static void get_identity ( ContextType , ObjectType , ReturnValue & ) ;
static void get_token ( ContextType , ObjectType , ReturnValue & ) ;
static void is_admin ( ContextType , ObjectType , ReturnValue & ) ;
2018-01-11 13:47:54 +00:00
static void is_admin_token ( ContextType , ObjectType , ReturnValue & ) ;
2016-10-20 00:55:46 +00:00
PropertyMap < T > const properties = {
{ " server " , { wrap < get_server > , nullptr } } ,
{ " identity " , { wrap < get_identity > , nullptr } } ,
{ " token " , { wrap < get_token > , nullptr } } ,
{ " isAdmin " , { wrap < is_admin > , nullptr } } ,
2018-01-11 13:47:54 +00:00
{ " isAdminToken " , { wrap < is_admin_token > , nullptr } } ,
2016-10-20 00:55:46 +00:00
} ;
2018-07-25 12:35:06 +00:00
static void create_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void admin_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void get_existing_user ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
2016-10-20 00:55:46 +00:00
MethodMap < T > const static_methods = {
2017-08-31 19:38:10 +00:00
{ " createUser " , wrap < create_user > } ,
2018-01-11 15:00:31 +00:00
{ " _adminUser " , wrap < admin_user > } ,
{ " _getExistingUser " , wrap < get_existing_user > } ,
2016-10-20 00:55:46 +00:00
} ;
2016-10-20 01:59:59 +00:00
2017-01-31 13:07:29 +00:00
/*static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);*/
2018-07-25 12:35:06 +00:00
static void all_users ( ContextType ctx , ObjectType object , ReturnValue & return_value ) ;
2016-10-20 01:59:59 +00:00
PropertyMap < T > const static_properties = {
2017-01-31 13:07:29 +00:00
/*{"current", {wrap<current_user>, nullptr}},*/
2016-10-20 01:59:59 +00:00
{ " all " , { wrap < all_users > , nullptr } } ,
} ;
2018-07-25 12:35:06 +00:00
static void logout ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void session_for_on_disk_path ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2016-10-20 20:07:45 +00:00
MethodMap < T > const methods = {
2018-03-09 14:51:45 +00:00
{ " _logout " , wrap < logout > } ,
2017-02-01 13:18:59 +00:00
{ " _sessionForOnDiskPath " , wrap < session_for_on_disk_path > }
2016-10-20 20:07:45 +00:00
} ;
2016-10-20 00:55:46 +00:00
} ;
template < typename T >
void UserClass < T > : : get_server ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
2016-10-20 03:25:10 +00:00
std : : string server = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > server_url ( ) ;
2016-10-20 00:55:46 +00:00
return_value . set ( server ) ;
}
template < typename T >
void UserClass < T > : : get_identity ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
std : : string identity = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > identity ( ) ;
return_value . set ( identity ) ;
}
template < typename T >
void UserClass < T > : : get_token ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
std : : string token = get_internal < T , UserClass < T > > ( object ) - > get ( ) - > refresh_token ( ) ;
return_value . set ( token ) ;
}
template < typename T >
void UserClass < T > : : is_admin ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
return_value . set ( get_internal < T , UserClass < T > > ( object ) - > get ( ) - > is_admin ( ) ) ;
}
2018-01-11 13:47:54 +00:00
template < typename T >
void UserClass < T > : : is_admin_token ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
return_value . set ( get_internal < T , UserClass < T > > ( object ) - > get ( ) - > token_type ( ) = = SyncUser : : TokenType : : Admin ) ;
}
2016-10-20 00:55:46 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void UserClass < T > : : create_user ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 3 , 5 ) ;
2017-08-16 15:11:46 +00:00
SyncUserIdentifier userIdentifier {
2018-07-25 12:35:06 +00:00
Value : : validated_to_string ( ctx , arguments [ 1 ] , " identity " ) ,
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " )
2017-08-16 15:11:46 +00:00
} ;
2017-09-27 18:16:44 +00:00
SharedUser * user = new SharedUser ( syncManagerShared ( ) . get_user (
2017-08-16 15:11:46 +00:00
userIdentifier ,
2018-07-25 12:35:06 +00:00
Value : : validated_to_string ( ctx , arguments [ 2 ] , " refreshToken " )
2017-08-16 15:11:46 +00:00
) ) ;
2017-06-17 14:59:15 +00:00
2018-07-25 12:35:06 +00:00
if ( argc = = 5 ) {
( * user ) - > set_is_admin ( Value : : validated_to_boolean ( ctx , arguments [ 4 ] , " isAdmin " ) ) ;
2017-06-17 14:59:15 +00:00
}
2016-10-20 00:55:46 +00:00
return_value . set ( create_object < T , UserClass < T > > ( ctx , user ) ) ;
}
2017-08-31 19:38:10 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void UserClass < T > : : admin_user ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 2 , 2 ) ;
2017-09-27 18:16:44 +00:00
SharedUser * user = new SharedUser ( syncManagerShared ( ) . get_admin_token_user (
2018-07-25 12:35:06 +00:00
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " ) ,
Value : : validated_to_string ( ctx , arguments [ 1 ] , " refreshToken " )
2017-08-31 19:38:10 +00:00
) ) ;
return_value . set ( create_object < T , UserClass < T > > ( ctx , user ) ) ;
}
2018-01-11 15:00:31 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void UserClass < T > : : get_existing_user ( ContextType ctx , ObjectType , Arguments arguments , ReturnValue & return_value ) {
arguments . validate_count ( 2 ) ;
2018-01-11 15:00:31 +00:00
if ( auto user = syncManagerShared ( ) . get_existing_logged_in_user ( SyncUserIdentifier {
2018-07-25 12:35:06 +00:00
Value : : validated_to_string ( ctx , arguments [ 1 ] , " identity " ) ,
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " ) } ) ) {
2018-01-11 15:00:31 +00:00
return_value . set ( create_object < T , UserClass < T > > ( ctx , new SharedUser ( std : : move ( user ) ) ) ) ;
}
}
2016-10-20 01:59:59 +00:00
template < typename T >
void UserClass < T > : : all_users ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
2016-11-10 21:55:22 +00:00
auto users = Object : : create_empty ( ctx ) ;
2017-09-27 18:16:44 +00:00
for ( auto user : syncManagerShared ( ) . all_logged_in_users ( ) ) {
2017-06-27 18:32:34 +00:00
if ( user - > token_type ( ) = = SyncUser : : TokenType : : Normal ) {
2016-11-10 21:55:22 +00:00
Object : : set_property ( ctx , users , user - > identity ( ) , create_object < T , UserClass < T > > ( ctx , new SharedUser ( user ) ) , ReadOnly | DontDelete ) ;
2016-11-10 20:53:36 +00:00
}
2016-10-20 01:59:59 +00:00
}
2016-11-10 21:55:22 +00:00
return_value . set ( users ) ;
2016-10-20 01:59:59 +00:00
}
2016-10-20 20:07:45 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void UserClass < T > : : logout ( ContextType ctx , FunctionType , ObjectType this_object , size_t , const ValueType [ ] , ReturnValue & ) {
2017-02-07 10:01:26 +00:00
get_internal < T , UserClass < T > > ( this_object ) - > get ( ) - > log_out ( ) ;
2016-10-20 20:07:45 +00:00
}
2017-02-01 13:18:59 +00:00
template < typename T >
class SessionClass : public ClassDefinition < T , WeakSession > {
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using ReturnValue = js : : ReturnValue < T > ;
2017-10-11 21:31:43 +00:00
using Arguments = js : : Arguments < T > ;
2017-05-05 09:48:34 +00:00
2017-02-01 13:18:59 +00:00
public :
std : : string const name = " Session " ;
2017-09-01 15:12:06 +00:00
using ProgressHandler = void ( uint64_t transferred_bytes , uint64_t transferrable_bytes ) ;
2018-08-10 08:39:03 +00:00
using StateHandler = void ( SyncSession : : PublicState old_state , SyncSession : : PublicState new_state ) ;
2018-08-15 13:52:37 +00:00
using ConnectionHandler = void ( SyncSession : : ConnectionState new_state , SyncSession : : ConnectionState old_state ) ;
2017-02-01 13:18:59 +00:00
static FunctionType create_constructor ( ContextType ) ;
2017-05-05 09:48:34 +00:00
2017-02-01 13:18:59 +00:00
static void get_config ( ContextType , ObjectType , ReturnValue & ) ;
static void get_user ( ContextType , ObjectType , ReturnValue & ) ;
static void get_url ( ContextType , ObjectType , ReturnValue & ) ;
static void get_state ( ContextType , ObjectType , ReturnValue & ) ;
2018-08-10 08:39:03 +00:00
static void get_connection_state ( ContextType , ObjectType , ReturnValue & ) ;
2017-02-01 13:18:59 +00:00
2018-07-25 12:35:06 +00:00
static void simulate_error ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void refresh_access_token ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void add_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void remove_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
2018-08-10 08:39:03 +00:00
static void add_state_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void remove_state_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void add_connection_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void remove_connection_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
static void is_connected ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) ;
2017-02-01 13:18:59 +00:00
2018-07-25 12:35:06 +00:00
static void override_server ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & ) ;
2017-10-11 21:31:43 +00:00
2017-02-01 13:18:59 +00:00
PropertyMap < T > const properties = {
{ " config " , { wrap < get_config > , nullptr } } ,
{ " user " , { wrap < get_user > , nullptr } } ,
{ " url " , { wrap < get_url > , nullptr } } ,
2018-08-10 08:39:03 +00:00
{ " state " , { wrap < get_state > , nullptr } } ,
{ " connectionState " , { wrap < get_connection_state > , nullptr } } ,
2017-02-01 13:18:59 +00:00
} ;
MethodMap < T > const methods = {
{ " _simulateError " , wrap < simulate_error > } ,
2017-09-01 15:12:06 +00:00
{ " _refreshAccessToken " , wrap < refresh_access_token > } ,
2017-10-11 21:31:43 +00:00
{ " _overrideServer " , wrap < override_server > } ,
2017-09-01 15:12:06 +00:00
{ " addProgressNotification " , wrap < add_progress_notification > } ,
{ " removeProgressNotification " , wrap < remove_progress_notification > } ,
2018-08-10 08:39:03 +00:00
{ " addConnectionNotification " , wrap < add_connection_notification > } ,
{ " removeConnectionNotification " , wrap < remove_connection_notification > } ,
{ " isConnected " , wrap < is_connected > } ,
2017-02-01 13:18:59 +00:00
} ;
2018-08-10 08:39:03 +00:00
private :
static std : : string get_connection_state_value ( SyncSession : : ConnectionState state ) ;
2017-02-01 13:18:59 +00:00
} ;
template < typename T >
2017-02-07 10:01:26 +00:00
class SyncSessionErrorHandlerFunctor {
public :
SyncSessionErrorHandlerFunctor ( typename T : : Context ctx , typename T : : Function error_func )
: m_ctx ( Context < T > : : get_global_context ( ctx ) )
, m_func ( ctx , error_func )
{ }
typename T : : Function func ( ) const { return m_func ; }
void operator ( ) ( std : : shared_ptr < SyncSession > session , SyncError error ) {
HANDLESCOPE
2017-11-06 12:51:33 +00:00
std : : string name = " Error " ;
2017-02-07 10:01:26 +00:00
auto error_object = Object < T > : : create_empty ( m_ctx ) ;
2017-11-15 00:09:50 +00:00
2017-10-02 17:44:24 +00:00
if ( error . is_client_reset_requested ( ) ) {
auto config_object = Object < T > : : create_empty ( m_ctx ) ;
Object < T > : : set_property ( m_ctx , config_object , " path " , Value < T > : : from_string ( m_ctx , error . user_info [ SyncError : : c_recovery_file_path_key ] ) ) ;
Object < T > : : set_property ( m_ctx , config_object , " readOnly " , Value < T > : : from_boolean ( m_ctx , true ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " config " , config_object ) ;
2017-11-06 12:51:33 +00:00
name = " ClientReset " ;
2017-10-02 17:44:24 +00:00
}
2017-11-06 12:51:33 +00:00
Object < T > : : set_property ( m_ctx , error_object , " name " , Value < T > : : from_string ( m_ctx , name ) ) ;
2017-02-07 10:01:26 +00:00
Object < T > : : set_property ( m_ctx , error_object , " message " , Value < T > : : from_string ( m_ctx , error . message ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " isFatal " , Value < T > : : from_boolean ( m_ctx , error . is_fatal ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " category " , Value < T > : : from_string ( m_ctx , error . error_code . category ( ) . name ( ) ) ) ;
2017-11-06 12:51:33 +00:00
Object < T > : : set_property ( m_ctx , error_object , " code " , Value < T > : : from_number ( m_ctx , error . error_code . value ( ) ) ) ;
2017-02-07 10:01:26 +00:00
auto user_info = Object < T > : : create_empty ( m_ctx ) ;
for ( auto & kvp : error . user_info ) {
Object < T > : : set_property ( m_ctx , user_info , kvp . first , Value < T > : : from_string ( m_ctx , kvp . second ) ) ;
}
Object < T > : : set_property ( m_ctx , error_object , " userInfo " , user_info ) ;
typename T : : Value arguments [ 2 ] ;
arguments [ 0 ] = create_object < T , SessionClass < T > > ( m_ctx , new WeakSession ( session ) ) ;
arguments [ 1 ] = error_object ;
2017-06-19 11:19:43 +00:00
Function < T > : : callback ( m_ctx , m_func , typename T : : Object ( ) , 2 , arguments ) ;
2017-02-07 10:01:26 +00:00
}
private :
const Protected < typename T : : GlobalContext > m_ctx ;
const Protected < typename T : : Function > m_func ;
} ;
2017-09-22 08:29:37 +00:00
2017-09-22 09:17:59 +00:00
// An object of type SSLVerifyCallbackSyncThreadFunctor is registered with the sync client in order
// to verify SSL certificates. The SSLVerifyCallbackSyncThreadFunctor object's operator() is called
// on the sync client's event loop thread.
2017-09-22 08:29:37 +00:00
template < typename T >
class SSLVerifyCallbackSyncThreadFunctor {
public :
SSLVerifyCallbackSyncThreadFunctor ( typename T : : Context ctx , typename T : : Function ssl_verify_func )
: m_ctx ( Context < T > : : get_global_context ( ctx ) )
, m_func ( ctx , ssl_verify_func )
, m_event_loop_dispatcher { SSLVerifyCallbackSyncThreadFunctor < T > : : main_loop_handler }
, m_mutex { new std : : mutex }
, m_cond_var { new std : : condition_variable }
{
}
2017-09-22 09:17:59 +00:00
// This function is called on the sync client's event loop thread.
2017-09-22 08:29:37 +00:00
bool operator ( ) ( const std : : string & server_address , sync : : Session : : port_type server_port , const char * pem_data , size_t pem_size , int preverify_ok , int depth )
{
const std : : string pem_certificate { pem_data , pem_size } ;
{
std : : lock_guard < std : : mutex > lock { * m_mutex } ;
m_ssl_certificate_callback_done = false ;
}
2017-09-22 09:17:59 +00:00
// Dispatch the call to the main_loop_handler on the node.js thread.
2017-09-22 08:29:37 +00:00
m_event_loop_dispatcher ( this , server_address , server_port , pem_certificate , preverify_ok , depth ) ;
bool ssl_certificate_accepted = false ;
{
2017-09-22 09:17:59 +00:00
// Wait for the return value of the callback function on the node.js main thread.
// The sync client blocks during this wait.
2017-09-22 08:29:37 +00:00
std : : unique_lock < std : : mutex > lock ( * m_mutex ) ;
m_cond_var - > wait ( lock , [ this ] { return this - > m_ssl_certificate_callback_done ; } ) ;
ssl_certificate_accepted = m_ssl_certificate_accepted ;
}
return ssl_certificate_accepted ;
}
2017-09-22 09:17:59 +00:00
// main_loop_handler is called on the node.js main thread.
// main_loop_handler calls the user callback (m_func) and sends the return value
// back to the sync client's event loop thread through a condition variable.
2017-09-22 08:29:37 +00:00
static void main_loop_handler ( SSLVerifyCallbackSyncThreadFunctor < T > * this_object ,
const std : : string & server_address ,
sync : : Session : : port_type server_port ,
const std : : string & pem_certificate ,
int preverify_ok ,
int depth )
{
HANDLESCOPE
const Protected < typename T : : GlobalContext > & ctx = this_object - > m_ctx ;
typename T : : Object ssl_certificate_object = Object < T > : : create_empty ( ctx ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " serverAddress " , Value < T > : : from_string ( ctx , server_address ) ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " serverPort " , Value < T > : : from_number ( ctx , double ( server_port ) ) ) ;
Object < T > : : set_property ( ctx , ssl_certificate_object , " pemCertificate " , Value < T > : : from_string ( ctx , pem_certificate ) ) ;
2017-09-25 09:05:16 +00:00
Object < T > : : set_property ( ctx , ssl_certificate_object , " acceptedByOpenSSL " , Value < T > : : from_boolean ( ctx , preverify_ok ! = 0 ) ) ;
2017-09-22 08:29:37 +00:00
Object < T > : : set_property ( ctx , ssl_certificate_object , " depth " , Value < T > : : from_number ( ctx , double ( depth ) ) ) ;
const int argc = 1 ;
typename T : : Value arguments [ argc ] = { ssl_certificate_object } ;
typename T : : Value ret_val = Function < T > : : callback ( ctx , this_object - > m_func , typename T : : Object ( ) , 1 , arguments ) ;
bool ret_val_bool = Value < T > : : to_boolean ( ctx , ret_val ) ;
{
std : : lock_guard < std : : mutex > lock { * this_object - > m_mutex } ;
this_object - > m_ssl_certificate_callback_done = true ;
this_object - > m_ssl_certificate_accepted = ret_val_bool ;
}
this_object - > m_cond_var - > notify_one ( ) ;
} ;
private :
const Protected < typename T : : GlobalContext > m_ctx ;
const Protected < typename T : : Function > m_func ;
EventLoopDispatcher < void ( SSLVerifyCallbackSyncThreadFunctor < T > * this_object ,
const std : : string & server_address ,
sync : : Session : : port_type server_port ,
const std : : string & pem_certificate ,
int preverify_ok ,
int depth ) > m_event_loop_dispatcher ;
bool m_ssl_certificate_callback_done = false ;
bool m_ssl_certificate_accepted = false ;
std : : shared_ptr < std : : mutex > m_mutex ;
std : : shared_ptr < std : : condition_variable > m_cond_var ;
} ;
2017-02-07 10:01:26 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void UserClass < T > : : session_for_on_disk_path ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
2017-02-07 10:01:26 +00:00
auto user = * get_internal < T , UserClass < T > > ( this_object ) ;
2018-07-25 12:35:06 +00:00
if ( auto session = user - > session_for_on_disk_path ( Value : : validated_to_string ( ctx , arguments [ 0 ] ) ) ) {
2017-02-01 13:18:59 +00:00
return_value . set ( create_object < T , SessionClass < T > > ( ctx , new WeakSession ( session ) ) ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_config ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
ObjectType config = Object : : create_empty ( ctx ) ;
Object : : set_property ( ctx , config , " user " , create_object < T , UserClass < T > > ( ctx , new SharedUser ( session - > config ( ) . user ) ) ) ;
2017-09-28 22:31:22 +00:00
Object : : set_property ( ctx , config , " url " , Value : : from_string ( ctx , session - > config ( ) . realm_url ( ) ) ) ;
2018-01-17 11:35:50 +00:00
if ( auto dispatcher = session - > config ( ) . error_handler . template target < EventLoopDispatcher < SyncSessionErrorHandler > > ( ) ) {
if ( auto handler = dispatcher - > func ( ) . template target < SyncSessionErrorHandlerFunctor < T > > ( ) ) {
Object : : set_property ( ctx , config , " error " , handler - > func ( ) ) ;
}
2017-02-07 10:01:26 +00:00
}
2018-06-28 08:34:02 +00:00
if ( ! session - > config ( ) . custom_http_headers . empty ( ) ) {
ObjectType custom_http_headers_object = Object : : create_empty ( ctx ) ;
for ( auto it = session - > config ( ) . custom_http_headers . begin ( ) ; it ! = session - > config ( ) . custom_http_headers . end ( ) ; + + it ) {
Object : : set_property ( ctx , custom_http_headers_object , it - > first , Value : : from_string ( ctx , it - > second ) ) ;
}
Object : : set_property ( ctx , config , " custom_http_headers " , custom_http_headers_object ) ;
}
2017-02-01 13:18:59 +00:00
return_value . set ( config ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_user ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
return_value . set ( create_object < T , UserClass < T > > ( ctx , new SharedUser ( session - > config ( ) . user ) ) ) ;
} else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
void SessionClass < T > : : get_url ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
if ( util : : Optional < std : : string > url = session - > full_realm_url ( ) ) {
return_value . set ( * url ) ;
return ;
}
}
return_value . set_undefined ( ) ;
}
template < typename T >
void SessionClass < T > : : get_state ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
static const std : : string invalid ( " invalid " ) ;
static const std : : string inactive ( " inactive " ) ;
2017-05-05 09:48:34 +00:00
static const std : : string active ( " active " ) ;
2017-02-01 13:18:59 +00:00
return_value . set ( invalid ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
if ( session - > state ( ) = = SyncSession : : PublicState : : Inactive ) {
return_value . set ( inactive ) ;
2018-08-10 08:39:03 +00:00
} else {
2017-02-01 13:18:59 +00:00
return_value . set ( active ) ;
}
}
}
2018-08-10 08:39:03 +00:00
template < typename T >
std : : string SessionClass < T > : : get_connection_state_value ( SyncSession : : ConnectionState state ) {
switch ( state ) {
case SyncSession : : ConnectionState : : Disconnected : return " disconnected " ;
case SyncSession : : ConnectionState : : Connecting : return " connecting " ;
case SyncSession : : ConnectionState : : Connected : return " connected " ;
}
}
template < typename T >
void SessionClass < T > : : get_connection_state ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
return_value . set ( get_connection_state_value ( SyncSession : : ConnectionState : : Disconnected ) ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( object ) - > lock ( ) ) {
return_value . set ( get_connection_state_value ( session - > connection_state ( ) ) ) ;
}
}
2017-02-01 13:18:59 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SessionClass < T > : : simulate_error ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
validate_argument_count ( argc , 2 ) ;
2017-02-01 13:18:59 +00:00
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2018-07-25 12:35:06 +00:00
std : : error_code error_code ( Value : : validated_to_number ( ctx , arguments [ 0 ] ) , realm : : sync : : protocol_error_category ( ) ) ;
std : : string message = Value : : validated_to_string ( ctx , arguments [ 1 ] ) ;
2017-11-17 18:43:24 +00:00
SyncSession : : OnlyForTesting : : handle_error ( * session , SyncError ( error_code , message , false ) ) ;
2017-02-01 13:18:59 +00:00
}
}
template < typename T >
2018-07-25 12:35:06 +00:00
void SessionClass < T > : : refresh_access_token ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
validate_argument_count ( argc , 3 ) ;
2017-02-01 13:18:59 +00:00
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2018-07-25 12:35:06 +00:00
std : : string sync_label = Value : : validated_to_string ( ctx , arguments [ 2 ] , " syncLabel " ) ;
2017-11-15 00:09:50 +00:00
session - > set_multiplex_identifier ( std : : move ( sync_label ) ) ;
2018-07-25 12:35:06 +00:00
std : : string access_token = Value : : validated_to_string ( ctx , arguments [ 0 ] , " accessToken " ) ;
std : : string realm_url = Value : : validated_to_string ( ctx , arguments [ 1 ] , " realmUrl " ) ;
2017-02-01 13:18:59 +00:00
session - > refresh_access_token ( std : : move ( access_token ) , std : : move ( realm_url ) ) ;
}
}
2016-10-04 22:02:51 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SessionClass < T > : : add_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 3 ) ;
2017-09-01 15:12:06 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-09-22 09:17:59 +00:00
2018-07-25 12:35:06 +00:00
std : : string direction = Value : : validated_to_string ( ctx , arguments [ 0 ] , " direction " ) ;
std : : string mode = Value : : validated_to_string ( ctx , arguments [ 1 ] , " mode " ) ;
2017-09-01 15:12:06 +00:00
SyncSession : : NotifierType notifierType ;
if ( direction = = " download " ) {
notifierType = SyncSession : : NotifierType : : download ;
}
else if ( direction = = " upload " ) {
notifierType = SyncSession : : NotifierType : : upload ;
}
else {
throw std : : invalid_argument ( " Invalid argument 'direction'. Only 'download' and 'upload' progress notification directions are supported " ) ;
}
bool is_streaming = false ;
if ( mode = = " reportIndefinitely " ) {
is_streaming = true ;
}
else if ( mode = = " forCurrentlyOutstandingWork " ) {
is_streaming = false ;
}
else {
throw std : : invalid_argument ( " Invalid argument 'mode'. Only 'reportIndefinitely' and 'forCurrentlyOutstandingWork' progress notification modes are supported " ) ;
}
2018-07-25 12:35:06 +00:00
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 2 ] , " callback " ) ;
2017-09-01 15:12:06 +00:00
Protected < FunctionType > protected_callback ( ctx , callback_function ) ;
Protected < ObjectType > protected_this ( ctx , this_object ) ;
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
2017-09-22 09:17:59 +00:00
std : : function < ProgressHandler > progressFunc ;
2017-09-01 15:12:06 +00:00
EventLoopDispatcher < ProgressHandler > progress_handler ( [ = ] ( uint64_t transferred_bytes , uint64_t transferrable_bytes ) {
HANDLESCOPE
ValueType callback_arguments [ 2 ] ;
callback_arguments [ 0 ] = Value : : from_number ( protected_ctx , transferred_bytes ) ;
callback_arguments [ 1 ] = Value : : from_number ( protected_ctx , transferrable_bytes ) ;
2017-09-22 09:17:59 +00:00
2017-09-11 23:47:07 +00:00
Function < T > : : callback ( protected_ctx , protected_callback , typename T : : Object ( ) , 2 , callback_arguments ) ;
2017-09-01 15:12:06 +00:00
} ) ;
progressFunc = std : : move ( progress_handler ) ;
2017-09-20 18:59:07 +00:00
auto registrationToken = session - > register_progress_notifier ( std : : move ( progressFunc ) , notifierType , is_streaming ) ;
2017-09-01 15:12:06 +00:00
auto syncSession = create_object < T , SessionClass < T > > ( ctx , new WeakSession ( session ) ) ;
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete ;
2017-09-11 12:28:05 +00:00
Object : : set_property ( ctx , callback_function , " _syncSession " , syncSession , attributes ) ;
Object : : set_property ( ctx , callback_function , " _registrationToken " , Value : : from_number ( protected_ctx , registrationToken ) , attributes ) ;
2017-09-01 15:12:06 +00:00
}
}
template < typename T >
2018-07-25 12:35:06 +00:00
void SessionClass < T > : : remove_progress_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 0 ] , " callback " ) ;
2017-09-11 12:28:05 +00:00
auto syncSessionProp = Object : : get_property ( ctx , callback_function , " _syncSession " ) ;
2017-09-08 23:00:46 +00:00
if ( Value : : is_undefined ( ctx , syncSessionProp ) | | Value : : is_null ( ctx , syncSessionProp ) ) {
return ;
}
2017-09-01 15:12:06 +00:00
auto syncSession = Value : : validated_to_object ( ctx , syncSessionProp ) ;
2017-09-11 12:28:05 +00:00
auto registrationToken = Object : : get_property ( ctx , callback_function , " _registrationToken " ) ;
2017-09-01 15:12:06 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( syncSession ) - > lock ( ) ) {
auto reg = Value : : validated_to_number ( ctx , registrationToken ) ;
session - > unregister_progress_notifier ( reg ) ;
}
}
2018-08-10 08:39:03 +00:00
template < typename T >
void SessionClass < T > : : add_connection_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 0 ] , " callback " ) ;
Protected < FunctionType > protected_callback ( ctx , callback_function ) ;
Protected < ObjectType > protected_this ( ctx , this_object ) ;
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
std : : function < ConnectionHandler > connectionFunc ;
EventLoopDispatcher < ConnectionHandler > connection_handler ( [ = ] ( SyncSession : : ConnectionState old_state , SyncSession : : ConnectionState new_state ) {
HANDLESCOPE
ValueType callback_arguments [ 2 ] ;
2018-08-15 13:52:37 +00:00
callback_arguments [ 0 ] = Value : : from_string ( protected_ctx , get_connection_state_value ( new_state ) ) ;
callback_arguments [ 1 ] = Value : : from_string ( protected_ctx , get_connection_state_value ( old_state ) ) ;
2018-08-10 08:39:03 +00:00
Function < T > : : callback ( protected_ctx , protected_callback , typename T : : Object ( ) , 2 , callback_arguments ) ;
} ) ;
connectionFunc = std : : move ( connection_handler ) ;
auto notificationToken = session - > register_connection_change_callback ( std : : move ( connectionFunc ) ) ;
auto syncSession = create_object < T , SessionClass < T > > ( ctx , new WeakSession ( session ) ) ;
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete ;
Object : : set_property ( ctx , callback_function , " _syncSession " , syncSession , attributes ) ;
Object : : set_property ( ctx , callback_function , " _connectionNotificationToken " , Value : : from_number ( protected_ctx , notificationToken ) , attributes ) ;
}
}
template < typename T >
void SessionClass < T > : : remove_connection_notification ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 0 ] , " callback " ) ;
auto syncSessionProp = Object : : get_property ( ctx , callback_function , " _syncSession " ) ;
if ( Value : : is_undefined ( ctx , syncSessionProp ) | | Value : : is_null ( ctx , syncSessionProp ) ) {
return ;
}
auto syncSession = Value : : validated_to_object ( ctx , syncSessionProp ) ;
auto registrationToken = Object : : get_property ( ctx , callback_function , " _connectionNotificationToken " ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( syncSession ) - > lock ( ) ) {
auto reg = Value : : validated_to_number ( ctx , registrationToken ) ;
session - > unregister_connection_change_callback ( reg ) ;
}
}
template < typename T >
void SessionClass < T > : : is_connected ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 0 ) ;
return_value . set ( false ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
auto state = session - > state ( ) ;
auto connection_state = session - > connection_state ( ) ;
if ( connection_state = = SyncSession : : ConnectionState : : Connected
& & ( state = = SyncSession : : PublicState : : Active | | state = = SyncSession : : PublicState : : Dying ) ) {
return_value . set ( true ) ;
}
}
}
2017-10-11 21:31:43 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SessionClass < T > : : override_server ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & ) {
2017-10-11 21:31:43 +00:00
args . validate_count ( 2 ) ;
std : : string address = Value : : validated_to_string ( ctx , args [ 0 ] , " address " ) ;
double port = Value : : validated_to_number ( ctx , args [ 1 ] , " port " ) ;
if ( port < 1 | | port > 65535 | | uint16_t ( port ) ! = port ) {
std : : ostringstream message ;
message < < " Invalid port number. Expected an integer in the range 1-65,535, got ' " < < port < < " ' " ;
throw std : : invalid_argument ( message . str ( ) ) ;
}
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
session - > override_server ( std : : move ( address ) , uint16_t ( port ) ) ;
}
}
2018-02-20 12:40:54 +00:00
template < typename T >
class Subscription : public partial_sync : : Subscription {
public :
Subscription ( partial_sync : : Subscription s ) : partial_sync : : Subscription ( std : : move ( s ) ) { }
Subscription ( Subscription & & ) = default ;
std : : vector < std : : pair < Protected < typename T : : Function > , partial_sync : : SubscriptionNotificationToken > > m_notification_tokens ;
} ;
template < typename T >
class SubscriptionClass : public ClassDefinition < T , Subscription < T > > {
using GlobalContextType = typename T : : GlobalContext ;
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using Function = js : : Function < T > ;
using ReturnValue = js : : ReturnValue < T > ;
using Arguments = js : : Arguments < T > ;
public :
std : : string const name = " Subscription " ;
static FunctionType create_constructor ( ContextType ) ;
static ObjectType create_instance ( ContextType , partial_sync : : Subscription ) ;
static void get_state ( ContextType , ObjectType , ReturnValue & ) ;
static void get_error ( ContextType , ObjectType , ReturnValue & ) ;
2018-07-25 12:35:06 +00:00
static void unsubscribe ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
static void add_listener ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
static void remove_listener ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
static void remove_all_listeners ( ContextType , ObjectType , Arguments , ReturnValue & ) ;
2018-02-20 12:40:54 +00:00
PropertyMap < T > const properties = {
{ " state " , { wrap < get_state > , nullptr } } ,
{ " error " , { wrap < get_error > , nullptr } }
} ;
MethodMap < T > const methods = {
{ " unsubscribe " , wrap < unsubscribe > } ,
{ " addListener " , wrap < add_listener > } ,
{ " removeListener " , wrap < remove_listener > } ,
2018-03-21 17:18:37 +00:00
{ " removeAllListeners " , wrap < remove_all_listeners > } ,
2018-02-20 12:40:54 +00:00
} ;
} ;
template < typename T >
typename T : : Object SubscriptionClass < T > : : create_instance ( ContextType ctx , partial_sync : : Subscription subscription ) {
return create_object < T , SubscriptionClass < T > > ( ctx , new Subscription < T > ( std : : move ( subscription ) ) ) ;
}
template < typename T >
void SubscriptionClass < T > : : get_state ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
auto subscription = get_internal < T , SubscriptionClass < T > > ( object ) ;
return_value . set ( static_cast < int8_t > ( subscription - > state ( ) ) ) ;
}
template < typename T >
void SubscriptionClass < T > : : get_error ( ContextType ctx , ObjectType object , ReturnValue & return_value ) {
auto subscription = get_internal < T , SubscriptionClass < T > > ( object ) ;
if ( auto error = subscription - > error ( ) ) {
try {
std : : rethrow_exception ( error ) ;
}
catch ( const std : : exception & e ) {
return_value . set ( e . what ( ) ) ;
}
}
else {
return_value . set_undefined ( ) ;
}
}
template < typename T >
2018-07-25 12:35:06 +00:00
void SubscriptionClass < T > : : unsubscribe ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & return_value ) {
2018-02-20 12:40:54 +00:00
args . validate_maximum ( 0 ) ;
auto subscription = get_internal < T , SubscriptionClass < T > > ( this_object ) ;
partial_sync : : unsubscribe ( * subscription ) ;
return_value . set_undefined ( ) ;
}
template < typename T >
2018-07-25 12:35:06 +00:00
void SubscriptionClass < T > : : add_listener ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & return_value ) {
2018-02-20 12:40:54 +00:00
args . validate_maximum ( 1 ) ;
auto subscription = get_internal < T , SubscriptionClass < T > > ( this_object ) ;
auto callback = Value : : validated_to_function ( ctx , args [ 0 ] ) ;
Protected < FunctionType > protected_callback ( ctx , callback ) ;
Protected < ObjectType > protected_this ( ctx , this_object ) ;
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
auto token = subscription - > add_notification_callback ( [ = ] ( ) {
HANDLESCOPE
ValueType arguments [ 2 ] ;
arguments [ 0 ] = static_cast < ObjectType > ( protected_this ) ,
arguments [ 1 ] = Value : : from_number ( ctx , static_cast < double > ( subscription - > state ( ) ) ) ;
Function : : callback ( protected_ctx , protected_callback , protected_this , 2 , arguments ) ;
} ) ;
subscription - > m_notification_tokens . emplace_back ( protected_callback , std : : move ( token ) ) ;
}
template < typename T >
2018-07-25 12:35:06 +00:00
void SubscriptionClass < T > : : remove_listener ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & return_value ) {
2018-02-20 12:40:54 +00:00
args . validate_maximum ( 1 ) ;
auto subscription = get_internal < T , SubscriptionClass < T > > ( this_object ) ;
auto callback = Value : : validated_to_function ( ctx , args [ 0 ] ) ;
auto protected_function = Protected < FunctionType > ( ctx , callback ) ;
auto & tokens = subscription - > m_notification_tokens ;
auto compare = [ & ] ( auto & & token ) {
return typename Protected < FunctionType > : : Comparator ( ) ( token . first , protected_function ) ;
} ;
tokens . erase ( std : : remove_if ( tokens . begin ( ) , tokens . end ( ) , compare ) , tokens . end ( ) ) ;
}
2018-03-21 17:18:37 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SubscriptionClass < T > : : remove_all_listeners ( ContextType ctx , ObjectType this_object , Arguments args , ReturnValue & return_value ) {
2018-03-21 17:18:37 +00:00
args . validate_maximum ( 0 ) ;
auto subscription = get_internal < T , SubscriptionClass < T > > ( this_object ) ;
subscription - > m_notification_tokens . clear ( ) ;
}
2018-02-20 12:40:54 +00:00
2017-09-01 15:12:06 +00:00
template < typename T >
class SyncClass : public ClassDefinition < T , void * > {
2016-10-04 22:02:51 +00:00
using GlobalContextType = typename T : : GlobalContext ;
using ContextType = typename T : : Context ;
using FunctionType = typename T : : Function ;
using ObjectType = typename T : : Object ;
using ValueType = typename T : : Value ;
using String = js : : String < T > ;
using Object = js : : Object < T > ;
using Value = js : : Value < T > ;
using Function = js : : Function < T > ;
using ReturnValue = js : : ReturnValue < T > ;
public :
std : : string const name = " Sync " ;
static FunctionType create_constructor ( ContextType ) ;
2018-07-25 12:35:06 +00:00
static void set_sync_log_level ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
static void initiate_client_reset ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2016-10-04 22:02:51 +00:00
// private
2017-09-11 10:21:44 +00:00
static std : : function < SyncBindSessionHandler > session_bind_callback ( ContextType ctx , ObjectType sync_constructor ) ;
2016-11-11 23:05:08 +00:00
static void populate_sync_config ( ContextType , ObjectType realm_constructor , ObjectType config_object , Realm : : Config & ) ;
2018-05-14 12:16:52 +00:00
static void populate_sync_config_for_ssl ( ContextType , ObjectType config_object , SyncConfig & ) ;
2016-10-04 22:02:51 +00:00
// static properties
static void get_is_developer_edition ( ContextType , ObjectType , ReturnValue & ) ;
MethodMap < T > const static_methods = {
{ " setLogLevel " , wrap < set_sync_log_level > } ,
2017-10-02 17:44:24 +00:00
{ " initiateClientReset " , wrap < initiate_client_reset > } ,
2016-10-04 22:02:51 +00:00
} ;
} ;
template < typename T >
inline typename T : : Function SyncClass < T > : : create_constructor ( ContextType ctx ) {
FunctionType sync_constructor = ObjectWrap < T , SyncClass < T > > : : create_constructor ( ctx ) ;
2016-10-20 00:55:46 +00:00
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete ;
Object : : set_property ( ctx , sync_constructor , " User " , ObjectWrap < T , UserClass < T > > : : create_constructor ( ctx ) , attributes ) ;
2017-02-01 13:18:59 +00:00
Object : : set_property ( ctx , sync_constructor , " Session " , ObjectWrap < T , SessionClass < T > > : : create_constructor ( ctx ) , attributes ) ;
2017-05-05 09:48:34 +00:00
2016-10-04 22:02:51 +00:00
return sync_constructor ;
}
2017-10-02 17:44:24 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SyncClass < T > : : initiate_client_reset ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
std : : string path = Value : : validated_to_string ( ctx , arguments [ 0 ] ) ;
2017-10-02 17:44:24 +00:00
if ( ! SyncManager : : shared ( ) . immediately_run_file_actions ( std : : string ( path ) ) ) {
throw std : : runtime_error ( util : : format ( " Realm was not configured correctly. Client Reset could not be run for Realm at: %1 " , path ) ) ;
}
}
2016-10-04 22:02:51 +00:00
template < typename T >
2018-07-25 12:35:06 +00:00
void SyncClass < T > : : set_sync_log_level ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
validate_argument_count ( argc , 1 ) ;
std : : string log_level = Value : : validated_to_string ( ctx , arguments [ 0 ] ) ;
2016-10-04 22:02:51 +00:00
std : : istringstream in ( log_level ) ; // Throws
in . imbue ( std : : locale : : classic ( ) ) ; // Throws
in . unsetf ( std : : ios_base : : skipws ) ;
util : : Logger : : Level log_level_2 = util : : Logger : : Level ( ) ;
in > > log_level_2 ; // Throws
if ( ! in | | ! in . eof ( ) )
throw std : : runtime_error ( " Bad log level " ) ;
2017-09-27 18:16:44 +00:00
syncManagerShared ( ) . set_log_level ( log_level_2 ) ;
2016-10-04 22:02:51 +00:00
}
2017-09-11 10:21:44 +00:00
template < typename T >
std : : function < SyncBindSessionHandler > SyncClass < T > : : session_bind_callback ( ContextType ctx , ObjectType sync_constructor )
{
Protected < typename T : : GlobalContext > protected_ctx ( Context < T > : : get_global_context ( ctx ) ) ;
Protected < ObjectType > protected_sync_constructor ( ctx , sync_constructor ) ;
return EventLoopDispatcher < SyncBindSessionHandler > ( [ protected_ctx , protected_sync_constructor ] ( const std : : string & path , const realm : : SyncConfig & config , std : : shared_ptr < SyncSession > ) {
HANDLESCOPE
ObjectType user_constructor = Object : : validated_get_object ( protected_ctx , protected_sync_constructor , " User " ) ;
FunctionType refreshAccessToken = Object : : validated_get_function ( protected_ctx , user_constructor , " _refreshAccessToken " ) ;
ValueType arguments [ 3 ] ;
arguments [ 0 ] = create_object < T , UserClass < T > > ( protected_ctx , new SharedUser ( config . user ) ) ;
arguments [ 1 ] = Value : : from_string ( protected_ctx , path ) ;
2017-09-28 22:31:22 +00:00
arguments [ 2 ] = Value : : from_string ( protected_ctx , config . realm_url ( ) ) ;
2017-09-11 10:21:44 +00:00
Function : : call ( protected_ctx , refreshAccessToken , 3 , arguments ) ;
} ) ;
}
2016-10-04 22:02:51 +00:00
template < typename T >
2016-12-09 11:49:59 +00:00
void SyncClass < T > : : populate_sync_config ( ContextType ctx , ObjectType realm_constructor , ObjectType config_object , Realm : : Config & config )
{
2016-10-04 22:02:51 +00:00
ValueType sync_config_value = Object : : get_property ( ctx , config_object , " sync " ) ;
2017-04-18 10:41:07 +00:00
if ( Value : : is_boolean ( ctx , sync_config_value ) ) {
config . force_sync_history = Value : : to_boolean ( ctx , sync_config_value ) ;
2018-06-25 12:00:15 +00:00
if ( config . force_sync_history ) {
config . schema_mode = SchemaMode : : Additive ;
}
2017-04-18 10:41:07 +00:00
} else if ( ! Value : : is_undefined ( ctx , sync_config_value ) ) {
2016-10-04 22:02:51 +00:00
auto sync_config_object = Value : : validated_to_object ( ctx , sync_config_value ) ;
2017-09-11 10:21:44 +00:00
ObjectType sync_constructor = Object : : validated_get_object ( ctx , realm_constructor , " Sync " ) ;
auto bind = session_bind_callback ( ctx , sync_constructor ) ;
2016-10-20 00:55:46 +00:00
2017-02-01 13:18:59 +00:00
std : : function < SyncSessionErrorHandler > error_handler ;
ValueType error_func = Object : : get_property ( ctx , sync_config_object , " error " ) ;
if ( ! Value : : is_undefined ( ctx , error_func ) ) {
2017-02-07 10:01:26 +00:00
error_handler = EventLoopDispatcher < SyncSessionErrorHandler > ( SyncSessionErrorHandlerFunctor < T > ( ctx , Value : : validated_to_function ( ctx , error_func ) ) ) ;
2017-02-01 13:18:59 +00:00
}
2017-05-05 09:48:34 +00:00
2016-10-04 22:02:51 +00:00
ObjectType user = Object : : validated_get_object ( ctx , sync_config_object , " user " ) ;
2016-10-20 00:55:46 +00:00
SharedUser shared_user = * get_internal < T , UserClass < T > > ( user ) ;
2016-11-11 08:14:52 +00:00
if ( shared_user - > state ( ) ! = SyncUser : : State : : Active ) {
throw std : : runtime_error ( " User is no longer valid. " ) ;
}
2016-10-20 00:55:46 +00:00
std : : string raw_realm_url = Object : : validated_get_string ( ctx , sync_config_object , " url " ) ;
2017-07-10 13:04:55 +00:00
if ( shared_user - > token_type ( ) = = SyncUser : : TokenType : : Admin ) {
2017-10-11 12:54:52 +00:00
size_t pos = raw_realm_url . find ( " /~/ " ) ;
if ( pos ! = std : : string : : npos ) {
raw_realm_url . replace ( pos + 1 , 1 , " __auth " ) ;
}
2017-07-10 13:04:55 +00:00
}
2017-09-22 09:17:59 +00:00
2017-05-05 11:50:45 +00:00
bool client_validate_ssl = true ;
ValueType validate_ssl_temp = Object : : get_property ( ctx , sync_config_object , " validate_ssl " ) ;
if ( ! Value : : is_undefined ( ctx , validate_ssl_temp ) ) {
client_validate_ssl = Value : : validated_to_boolean ( ctx , validate_ssl_temp , " validate_ssl " ) ;
}
util : : Optional < std : : string > ssl_trust_certificate_path ;
ValueType trust_certificate_path_temp = Object : : get_property ( ctx , sync_config_object , " ssl_trust_certificate_path " ) ;
if ( ! Value : : is_undefined ( ctx , trust_certificate_path_temp ) ) {
ssl_trust_certificate_path = std : : string ( Value : : validated_to_string ( ctx , trust_certificate_path_temp , " ssl_trust_certificate_path " ) ) ;
}
else {
ssl_trust_certificate_path = util : : none ;
}
2017-09-22 08:29:37 +00:00
std : : function < sync : : Session : : SSLVerifyCallback > ssl_verify_callback ;
2017-09-25 09:05:16 +00:00
ValueType ssl_verify_func = Object : : get_property ( ctx , sync_config_object , " open_ssl_verify_callback " ) ;
2017-09-22 08:29:37 +00:00
if ( ! Value : : is_undefined ( ctx , ssl_verify_func ) ) {
SSLVerifyCallbackSyncThreadFunctor < T > ssl_verify_functor { ctx , Value : : validated_to_function ( ctx , ssl_verify_func ) } ;
ssl_verify_callback = std : : move ( ssl_verify_functor ) ;
}
2018-05-14 12:16:52 +00:00
2018-05-30 10:54:51 +00:00
bool is_partial = false ; // Change to `true` when `partial` is removed
ValueType full_synchronization_value = Object : : get_property ( ctx , sync_config_object , " fullSynchronization " ) ;
2017-10-02 18:29:36 +00:00
ValueType partial_value = Object : : get_property ( ctx , sync_config_object , " partial " ) ;
2018-05-30 10:54:51 +00:00
// Disallow setting `partial` and `fullSynchronization` at the same time
if ( ! Value : : is_undefined ( ctx , full_synchronization_value ) & & ! Value : : is_undefined ( ctx , partial_value ) ) {
throw std : : invalid_argument ( " 'partial' and 'fullSynchronization' were both set. 'partial' has been deprecated, use only 'fullSynchronization' " ) ;
}
2017-10-02 18:29:36 +00:00
if ( ! Value : : is_undefined ( ctx , partial_value ) ) {
is_partial = Value : : validated_to_boolean ( ctx , partial_value ) ;
2018-05-30 10:54:51 +00:00
} else if ( ! Value : : is_undefined ( ctx , full_synchronization_value ) ) {
is_partial = ! Value : : validated_to_boolean ( ctx , full_synchronization_value ) ;
2017-10-02 18:29:36 +00:00
}
2018-05-30 10:54:51 +00:00
bool disable_query_based_sync_url_checks = false ;
ValueType disable_query_based_sync_url_checks_value = Object : : get_property ( ctx , sync_config_object , " _disableQueryBasedSyncUrlChecks " ) ;
if ( ! Value : : is_undefined ( ctx , disable_query_based_sync_url_checks_value ) ) {
disable_query_based_sync_url_checks = Value : : validated_to_boolean ( ctx , disable_query_based_sync_url_checks_value ) ;
2018-03-13 07:27:47 +00:00
}
2018-05-30 10:54:51 +00:00
if ( disable_query_based_sync_url_checks ) {
2018-03-13 07:27:47 +00:00
config . sync_config = std : : make_shared < SyncConfig > ( shared_user , std : : move ( " " ) ) ;
config . sync_config - > reference_realm_url = std : : move ( raw_realm_url ) ;
}
else {
config . sync_config = std : : make_shared < SyncConfig > ( shared_user , std : : move ( raw_realm_url ) ) ;
}
2017-11-09 22:04:49 +00:00
config . sync_config - > bind_session_handler = std : : move ( bind ) ;
config . sync_config - > error_handler = std : : move ( error_handler ) ;
2018-05-14 12:16:52 +00:00
config . sync_config - > is_partial = is_partial ;
2018-06-28 08:34:02 +00:00
// Custom HTTP headers
ValueType sync_custom_http_headers_value = Object : : get_property ( ctx , sync_config_object , " custom_http_headers " ) ;
if ( ! Value : : is_undefined ( ctx , sync_custom_http_headers_value ) ) {
auto sync_custom_http_headers = Value : : validated_to_object ( ctx , sync_custom_http_headers_value ) ;
auto property_names = Object : : get_property_names ( ctx , sync_custom_http_headers ) ;
std : : map < std : : string , std : : string > http_headers ;
for ( auto it = property_names . begin ( ) ; it ! = property_names . end ( ) ; + + it ) {
auto name = * it ;
ValueType prop_value = Object : : get_property ( ctx , sync_custom_http_headers , name ) ;
auto value = Value : : validated_to_string ( ctx , prop_value ) ;
http_headers [ name ] = value ;
}
config . sync_config - > custom_http_headers = std : : move ( http_headers ) ;
}
2018-05-14 12:16:52 +00:00
// TODO: remove
2017-11-09 22:04:49 +00:00
config . sync_config - > client_validate_ssl = client_validate_ssl ;
config . sync_config - > ssl_trust_certificate_path = ssl_trust_certificate_path ;
config . sync_config - > ssl_verify_callback = std : : move ( ssl_verify_callback ) ;
2018-05-14 12:16:52 +00:00
ValueType ssl_config_value = Object : : get_property ( ctx , sync_config_object , " ssl " ) ;
if ( Value : : is_object ( ctx , ssl_config_value ) ) {
auto ssl_config_object = Value : : to_object ( ctx , ssl_config_value ) ;
populate_sync_config_for_ssl ( ctx , ssl_config_object , * config . sync_config ) ;
}
2017-09-22 08:29:37 +00:00
2016-10-04 22:02:51 +00:00
config . schema_mode = SchemaMode : : Additive ;
2017-10-04 09:28:50 +00:00
config . path = syncManagerShared ( ) . path_for_realm ( * shared_user , config . sync_config - > realm_url ( ) ) ;
2017-03-28 12:34:27 +00:00
if ( ! config . encryption_key . empty ( ) ) {
config . sync_config - > realm_encryption_key = std : : array < char , 64 > ( ) ;
std : : copy_n ( config . encryption_key . begin ( ) , config . sync_config - > realm_encryption_key - > size ( ) , config . sync_config - > realm_encryption_key - > begin ( ) ) ;
}
2018-05-14 12:16:52 +00:00
# if REALM_ANDROID
// For React Native Android, if the user didn't define the ssl_verify_callback, we provide a default
// implementation for him, otherwise all SSL validation will fail, since the Sync client doesn't have
// access to the Android Keystore.
// This default implementation will perform a JNI call to invoke a Java method defined at the `SSLHelper`
// to perform the certificate verification.
if ( ! config . sync_config - > ssl_verify_callback ) {
auto ssl_verify_functor =
[ ] ( const std : : string server_address , realm : : sync : : Session : : port_type server_port ,
const char * pem_data , size_t pem_size , int preverify_ok , int depth ) {
JNIEnv * env = realm : : jni_util : : JniUtils : : get_env ( true ) ;
static jmethodID java_certificate_verifier = env - > GetStaticMethodID ( ssl_helper_class , " certificateVerifier " , " (Ljava/lang/String;Ljava/lang/String;I)Z " ) ;
jstring jserver_address = env - > NewStringUTF ( server_address . c_str ( ) ) ;
// deep copy the pem_data into a string so DeleteLocalRef delete the local reference not the original const char
std : : string pem ( pem_data , pem_size ) ;
jstring jpem = env - > NewStringUTF ( pem . c_str ( ) ) ;
bool isValid = env - > CallStaticBooleanMethod ( ssl_helper_class , java_certificate_verifier ,
jserver_address ,
jpem , depth ) = = JNI_TRUE ;
env - > DeleteLocalRef ( jserver_address ) ;
env - > DeleteLocalRef ( jpem ) ;
return isValid ;
} ;
config . sync_config - > ssl_verify_callback = std : : move ( ssl_verify_functor ) ;
}
# endif
2016-10-04 22:02:51 +00:00
}
}
2018-05-14 12:16:52 +00:00
template < typename T >
void SyncClass < T > : : populate_sync_config_for_ssl ( ContextType ctx , ObjectType config_object , SyncConfig & config )
{
ValueType validate_ssl = Object : : get_property ( ctx , config_object , " validate " ) ;
2018-05-14 12:37:34 +00:00
if ( Value : : is_boolean ( ctx , validate_ssl ) ) {
2018-05-14 12:16:52 +00:00
config . client_validate_ssl = Value : : to_boolean ( ctx , validate_ssl ) ;
}
ValueType certificate_path = Object : : get_property ( ctx , config_object , " certificatePath " ) ;
if ( Value : : is_string ( ctx , certificate_path ) ) {
config . ssl_trust_certificate_path = std : : string ( Value : : to_string ( ctx , certificate_path ) ) ;
}
ValueType validate_callback = Object : : get_property ( ctx , config_object , " validateCallback " ) ;
if ( Value : : is_function ( ctx , validate_callback ) ) {
config . ssl_verify_callback = SSLVerifyCallbackSyncThreadFunctor < T > { ctx , Value : : to_function ( ctx , validate_callback ) } ;
}
}
2016-10-04 22:02:51 +00:00
} // js
} // realm