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
# include <list>
# include <map>
# include <set>
2017-07-10 13:04:55 +00:00
# include <regex>
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"
2016-10-04 22:02:51 +00:00
# include "realm/util/logger.hpp"
# include "realm/util/uri.hpp"
namespace realm {
namespace js {
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 > ;
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 & ) ;
PropertyMap < T > const properties = {
{ " server " , { wrap < get_server > , nullptr } } ,
{ " identity " , { wrap < get_identity > , nullptr } } ,
{ " token " , { wrap < get_token > , nullptr } } ,
{ " isAdmin " , { wrap < is_admin > , nullptr } } ,
} ;
2017-02-07 10:01:26 +00:00
static void create_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2017-08-31 19:38:10 +00:00
static void admin_user ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , 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 > } ,
{ " _adminUser " , wrap < admin_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);*/
2016-10-20 01:59:59 +00:00
static void all_users ( ContextType ctx , ObjectType object , ReturnValue & return_value ) ;
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 } } ,
} ;
2017-02-07 10:01:26 +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 = {
2017-02-01 13:18:59 +00:00
{ " logout " , wrap < logout > } ,
{ " _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 ( ) ) ;
}
template < typename T >
2017-02-07 10:01:26 +00:00
void UserClass < T > : : create_user ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
2017-06-17 14:59:15 +00:00
validate_argument_count ( argc , 3 , 5 ) ;
2017-08-16 15:11:46 +00:00
SyncUserIdentifier userIdentifier {
2017-08-17 09:37:16 +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
} ;
2016-10-20 20:07:45 +00:00
SharedUser * user = new SharedUser ( SyncManager : : shared ( ) . get_user (
2017-08-16 15:11:46 +00:00
userIdentifier ,
2017-08-17 09:37:16 +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
if ( argc = = 5 ) {
( * user ) - > set_is_admin ( Value : : validated_to_boolean ( ctx , arguments [ 4 ] , " isAdmin " ) ) ;
}
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 >
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 ) ;
SharedUser * user = new SharedUser ( SyncManager : : shared ( ) . get_admin_token_user (
Value : : validated_to_string ( ctx , arguments [ 0 ] , " authServerUrl " ) ,
Value : : validated_to_string ( ctx , arguments [ 1 ] , " refreshToken " )
) ) ;
return_value . set ( create_object < T , UserClass < T > > ( ctx , 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 ) ;
2016-12-02 20:55:27 +00:00
for ( auto user : SyncManager : : shared ( ) . 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 >
2017-02-07 10:01:26 +00:00
void UserClass < T > : : logout ( ContextType ctx , FunctionType , ObjectType this_object , size_t , const ValueType [ ] , ReturnValue & ) {
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-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 ) ;
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 & ) ;
2017-02-07 10:01:26 +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 & ) ;
2017-09-01 15:12:06 +00:00
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 & ) ;
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 } } ,
{ " state " , { wrap < get_state > , nullptr } }
} ;
MethodMap < T > const methods = {
{ " _simulateError " , wrap < simulate_error > } ,
2017-09-01 15:12:06 +00:00
{ " _refreshAccessToken " , wrap < refresh_access_token > } ,
{ " addProgressNotification " , wrap < add_progress_notification > } ,
{ " removeProgressNotification " , wrap < remove_progress_notification > } ,
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
auto error_object = Object < T > : : create_empty ( m_ctx ) ;
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 ( ) ) ) ;
Object < T > : : set_property ( m_ctx , error_object , " code " , Value < T > : : from_number ( m_ctx , error . error_code . value ( ) ) ) ;
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 >
void UserClass < T > : : session_for_on_disk_path ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
auto user = * get_internal < T , UserClass < T > > ( this_object ) ;
2017-02-01 13:18:59 +00:00
if ( auto session = user - > session_for_on_disk_path ( Value : : validated_to_string ( ctx , arguments [ 0 ] ) ) ) {
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 ) ) ) ;
Object : : set_property ( ctx , config , " url " , Value : : from_string ( ctx , session - > config ( ) . realm_url ) ) ;
2017-02-07 10:01:26 +00:00
if ( auto * dispatcher = session - > config ( ) . error_handler . template target < EventLoopDispatcher < SyncSessionErrorHandler > > ( ) ) {
auto & handler = * dispatcher - > func ( ) . template target < SyncSessionErrorHandlerFunctor < T > > ( ) ;
Object : : set_property ( ctx , config , " error " , handler . func ( ) ) ;
}
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 ) ;
} else if ( session - > state ( ) ! = SyncSession : : PublicState : : Error ) {
return_value . set ( active ) ;
}
}
}
template < typename T >
2017-02-07 10:01:26 +00:00
void SessionClass < T > : : simulate_error ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
2017-02-01 13:18:59 +00:00
validate_argument_count ( argc , 2 ) ;
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-02-01 13:18:59 +00:00
SyncError error ;
error . error_code = std : : error_code ( Value : : validated_to_number ( ctx , arguments [ 0 ] ) , realm : : sync : : protocol_error_category ( ) ) ;
error . message = Value : : validated_to_string ( ctx , arguments [ 1 ] ) ;
SyncSession : : OnlyForTesting : : handle_error ( * session , std : : move ( error ) ) ;
}
}
template < typename T >
2017-02-07 10:01:26 +00:00
void SessionClass < T > : : refresh_access_token ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & ) {
2017-02-01 13:18:59 +00:00
validate_argument_count ( argc , 2 ) ;
2017-02-07 10:01:26 +00:00
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-02-01 13:18:59 +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 " ) ;
session - > refresh_access_token ( std : : move ( access_token ) , std : : move ( realm_url ) ) ;
}
}
2016-10-04 22:02:51 +00:00
template < typename T >
2017-09-01 15:12: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 ) ;
if ( auto session = get_internal < T , SessionClass < T > > ( this_object ) - > lock ( ) ) {
2017-09-22 09:17:59 +00:00
2017-09-01 15:12: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 " ) ;
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 " ) ;
}
auto callback_function = Value : : validated_to_function ( ctx , arguments [ 2 ] , " 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 ) ) ;
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-22 09:17:59 +00:00
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 >
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 ) ;
}
}
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 ) ;
2017-02-07 10:01:26 +00:00
static void set_sync_log_level ( ContextType , FunctionType , ObjectType , size_t , const ValueType [ ] , ReturnValue & ) ;
2017-09-27 13:59:21 +00:00
static void initialize ( 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 & ) ;
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-09-27 13:59:21 +00:00
{ " initialize " , wrap < initialize > } ,
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 ;
}
template < typename T >
2017-02-07 10:01:26 +00:00
void SyncClass < T > : : set_sync_log_level ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
2016-10-04 22:02:51 +00:00
validate_argument_count ( argc , 1 ) ;
std : : string log_level = Value : : validated_to_string ( ctx , arguments [ 0 ] ) ;
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 " ) ;
realm : : SyncManager : : shared ( ) . set_log_level ( log_level_2 ) ;
}
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 ) ;
arguments [ 2 ] = Value : : from_string ( protected_ctx , config . realm_url ) ;
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 ) ;
} 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 ) {
static std : : regex tilde ( " /~/ " ) ;
raw_realm_url = std : : regex_replace ( raw_realm_url , tilde , " /__auth/ " ) ;
}
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 ) ;
}
2016-10-20 00:55:46 +00:00
// FIXME - use make_shared
2017-01-04 14:07:25 +00:00
config . sync_config = std : : shared_ptr < SyncConfig > ( new SyncConfig { shared_user , raw_realm_url ,
SyncSessionStopPolicy : : AfterChangesUploaded ,
2017-05-05 09:48:34 +00:00
std : : move ( bind ) , std : : move ( error_handler ) ,
nullptr , util : : none ,
2017-09-22 08:29:37 +00:00
client_validate_ssl , ssl_trust_certificate_path ,
std : : move ( ssl_verify_callback ) } ) ;
2016-10-04 22:02:51 +00:00
config . schema_mode = SchemaMode : : Additive ;
2017-08-16 15:11:46 +00:00
config . path = realm : : SyncManager : : shared ( ) . path_for_realm ( * shared_user , raw_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 ( ) ) ;
}
2016-10-04 22:02:51 +00:00
}
}
2017-09-27 13:59:21 +00:00
template < typename T >
void SyncClass < T > : : initialize ( ContextType ctx , FunctionType , ObjectType this_object , size_t argc , const ValueType arguments [ ] , ReturnValue & return_value ) {
// setup synced realmFile paths
ensure_directory_exists_for_file ( default_realm_file_directory ( ) ) ;
SyncManager : : shared ( ) . configure_file_system ( default_realm_file_directory ( ) , SyncManager : : MetadataMode : : NoEncryption ) ;
2017-09-27 15:53:09 +00:00
return_value . set_undefined ( ) ;
2017-09-27 13:59:21 +00:00
}
2016-10-04 22:02:51 +00:00
} // js
} // realm