QtDataSync
4.2.0
A simple offline-first synchronisation framework, to synchronize data of Qt applications between devices
|
The class to setup and create datasync instances. More...
#include <setup.h>
Public Types | |
enum | SyncPolicy { PreferChanged, PreferDeleted } |
Defines the possible policies on how to treat merge conflicts between change and delete. More... | |
enum | SignatureScheme { RSA_PSS_SHA3_512, ECDSA_ECP_SHA3_512, ECNR_ECP_SHA3_512 } |
The signature schemes supported for Setup::signatureScheme. More... | |
enum | EncryptionScheme { RSA_OAEP_SHA3_512, ECIES_ECP_SHA3_512 } |
The encryption schemes supported for Setup::encryptionScheme. More... | |
enum | CipherScheme { AES_EAX, AES_GCM, TWOFISH_EAX, TWOFISH_GCM, SERPENT_EAX, SERPENT_GCM, IDEA_EAX } |
The symmetric cipher schemes supported for Setup::cipherScheme. More... | |
enum | EllipticCurve { secp112r1, secp128r1, secp160r1, secp192r1, secp224r1, secp256r1, secp384r1, secp521r1, brainpoolP160r1, brainpoolP192r1, brainpoolP224r1, brainpoolP256r1, brainpoolP320r1, brainpoolP384r1, brainpoolP512r1, secp112r2, secp128r2, secp160r2, secp160k1, secp192k1, secp224k1, secp256k1 } |
Elliptic curves supported as key parameter for Setup::signatureKeyParam and Setup::encryptionKeyParam in case an ECC scheme is used. | |
enum | EventMode { EventMode::Unchanged, EventMode::Enabled, EventMode::Disabled } |
Possible values for the event logging mode change. More... | |
using | FatalErrorHandler = std::function< void(QString, QString, const QMessageLogContext &)> |
Typedef of an error handler function. See Setup::fatalErrorHandler. | |
Static Public Member Functions | |
static bool | exists (const QString &name=DefaultSetup) |
Checks if a setup for the given name does already exist. More... | |
static void | setCleanupTimeout (unsigned long timeout) |
Sets the maximum timeout for shutting down setups. More... | |
static void | removeSetup (const QString &name, bool waitForFinished=false) |
Stops the datasync instance and removes it. More... | |
static QStringList | keystoreProviders () |
Returns a list of all keystore providers defined via the plugins. More... | |
static QStringList | availableKeystores () |
Returns a list of all keystore providers that are actually available for use. More... | |
static bool | keystoreAvailable (const QString &provider) |
Checks if the given keystore provider is available. More... | |
static QString | defaultKeystoreProvider () |
Returns the default provider to be used based on the current platform and the available providers. More... | |
static KeyStore * | loadKeystore (QObject *parent=nullptr, const QString &setupName=DefaultSetup) |
Create and load akeystore instance from the default provider. More... | |
static KeyStore * | loadKeystore (const QString &provider, QObject *parent=nullptr, const QString &setupName=DefaultSetup) |
Create and load akeystore instance from the given provider. More... | |
Properties | |
QString | localDir |
The local storage directoy used by the instance. More... | |
QUrl | remoteObjectHost |
The url to be used to host the remote object sources, and to connect to to acquire the replicas. More... | |
QJsonSerializer | serializer |
The serializer to be used to serialize and deserialize data to and from the store. More... | |
QtDataSync::ConflictResolver | conflictResolver |
An optional conflict resolver to handle merge conflicts. More... | |
FatalErrorHandler | fatalErrorHandler |
An alternative handler for fatal errors. More... | |
int | cacheSize |
The size of the internal database cache, in bytes. More... | |
bool | persistDeletedVersion |
Specify whether deleted datasets should persist. More... | |
SyncPolicy | syncPolicy |
The policiy for how to handle conflicts. More... | |
QSslConfiguration | sslConfiguration |
The ssl configuration to be used to connect to the remote. More... | |
QtDataSync::RemoteConfig | remoteConfiguration |
The configuration to be used to connect to the remote. More... | |
QString | keyStoreProvider |
The name of the preferred keystore provider. More... | |
SignatureScheme | signatureScheme |
The algorithmic scheme to be used for new signature keys. More... | |
QVariant | signatureKeyParam |
The generation parameter for the signature key. More... | |
EncryptionScheme | encryptionScheme |
The algorithmic scheme to be used for new encryption keys. More... | |
QVariant | encryptionKeyParam |
The generation parameter for the encryption key. More... | |
CipherScheme | cipherScheme |
The algorithmic scheme to be used for new secret exchange keys (which are symmetric) More... | |
qint32 | cipherKeySize |
The size in bytes for the secret exchange key (which is symmetric) More... | |
EventMode | eventLoggingMode |
The logging mode for database change events. More... | |
The class to setup and create datasync instances.
The Setup class is what you need for configuration. Before you can use datasync, you always have to create at least one instance by using this setup. For most applications, it's sufficient to simply use the default constructed setup and set the remote configuration. However, if you want to use custom implementations or change the way specific components behave, the setup is where to do so. Once all the configuration part has been done, you can call create() (or createPassive()) to create the instance.
The symmetric cipher schemes supported for Setup::cipherScheme.
The encryption schemes supported for Setup::encryptionScheme.
Enumerator | |
---|---|
RSA_OAEP_SHA3_512 | RSA in OAEP mode with Sha3 hash of 512 bits. |
ECIES_ECP_SHA3_512 | ECIES on prime curves with Sha3 hash of 512 bits (Requires at least crypto++ 6.0) |
|
strong |
The signature schemes supported for Setup::signatureScheme.
|
static |
Returns a list of all keystore providers that are actually available for use.
This list contains only the providers that can currently access the keystore and thus are usable. For a list of all existing providers, use keystoreProviders()
QtDataSync::Setup::create | ( | const QString & | name = DefaultSetup | ) |
Creates a datasync instance from this setup with the given name.
name | The unique name of the setup to be created |
SetupExistsException | If a datasync instance with the same name already exists |
SetupLockedException | If the local directory is already locked by another instance |
This method creates and starts a new datasync instance from the configuration of the setup. It will automatically launch the new thread and initialize it. This is done asynchronously, but you don't need to wait for it. After this method returned, you can use the instance as you please, with i.e. DataStore, SyncManager, etc.
QtDataSync::Setup::createPassive | ( | const QString & | name = DefaultSetup , |
int | timeout = 30000 |
||
) |
Creates a passive setup with the given name that connects to the primary datasync instance.
name | The unique name of the setup to be created |
timeout | An optional timeout to wait for the setup to connect to the active instance |
true
if the setup could connect to the active setup, false
if not SetupExistsException | If a datasync instance with the same name already exists |
The passive setup differs from the active one greatly. It does not create a datasync instance, but instead tries to connect to an active one. This means, you typically have one process that uses a normal (active) setup, and one or more other processes, that connect to it by using a passive setup. If the setup fails to connect within the timeout, false
is returned.
The behaviour also depends on the timeout. Possible values:
After beeing created, the passive setup can be used just like a normal setup, as long as it did successfully connect to the active one. Classes like the SyncManager and AccountManager will simply connect to the active instance. The DataStore classes access data directly, but use the active instance for communicating changes. This means data changes will still be atomic, but the corresponding change signals take a while to be delivered (the must go through the active instance due to technical reasons)
false
you can still use the setup. This only means that no active instance was found, which makes the Manager classes do nothing, and prevents the store classes from emitting any change signals. Accessing the stores however is still possible, and no data is lost. However, since the change signals are not working, most applications will not work correctly without having an active setup available. So no crashes or fatal errors. It is recommended to show an error to the user and gracefully exit in such cases.
|
static |
Returns the default provider to be used based on the current platform and the available providers.
Internally, the method has a list of all providers for each platform. It will check for all of these they are available and return the first one that actually is. If no provider is found, the plain provider is returned, even if it is not available. The list is sorted as follows:
QTDATASYNC_KEYSTORE
environment variable
|
static |
Checks if a setup for the given name does already exist.
name | The name of the setup to check for |
|
static |
Checks if the given keystore provider is available.
provider | The name of the keystore type to be checked |
true
if the provider exists and is usable, false
if notA useable provider is one that can access it's keystore and thus be used. If order to find out whether a provider exists, use keystoreProviders().
Internally, the plugin gets temporarily loaded in order to check if the keystore is accessible. This may take a moment to complete, depending on the keystore beeing used.
|
static |
Returns a list of all keystore providers defined via the plugins.
The list contains all providers of all found plugins. In order to obtain a list of providers that are actually currently usable (available), use availableKeystores()
|
static |
Create and load akeystore instance from the default provider.
parent | The parent object to set as the keystores parent |
setupName | The name of the setup to create the keystore for |
SetupDoesNotExistException | If the setup specified by setupName does not exist (yet) |
QException | If something went wrong while loading the plugin |
This method simply creates a keystore instance. This can be useful if you want to use a keystore outside of datasync to store your own secrets there.
|
static |
Create and load akeystore instance from the given provider.
provider | The keystore provider to create a keystore for |
parent | The parent object to set as the keystores parent |
setupName | The name of the setup to create the keystore for |
SetupDoesNotExistException | If the setup specified by setupName does not exist (yet) |
QException | If something went wrong while loading the plugin |
This method simply creates a keystore instance. This can be useful if you want to use a keystore outside of datasync to store your own secrets there.
|
static |
Stops the datasync instance and removes it.
name | The name of the setup to be removed |
waitForFinished | If set to true , the method will wait until finished |
Removing a setup stops the instances and delets it. This happens asynchronously and gracefully in the background. However, you cannot register another setup with the same name until the previous one has been deleted.
If you wait for the method to finish, the cleanup timeout will be used to terminate if the instance does not finish in time. This is not the case if you don't wait. Please note that waiting is blocking, and thus not recommended to use for GUI apps.
QtDataSync::Setup::setAccount | ( | const QJsonObject & | importData, |
bool | keepData = false , |
||
bool | allowFailure = false |
||
) |
Sets an account to be imported on creation of the instance.
importData | The account data to be imported |
keepData | Specify whether the stored data should be preserved |
allowFailure | Specify how a failure to import the account should be treated |
This method is basically a shortcut to import an account on creation - which is faster and much more efficient than first creating a new account and then delete it again to import an existing one. Internally, the same things will happen as with AccountManager::importAccount (or AccountManager::importAccountTrusted).
Whats special is that since there is no account manager to handle a failure, there are two way to handle that. If allowFailure
is false (the default), failing to import the data will trigger a fatal error. If it is set to true, the error is only printed and a new account is created instead, so that the engine can continue running.
Setup& QtDataSync::Setup::setAccount | ( | const QByteArray & | importData, |
bool | keepData = false , |
||
bool | allowFailure = false |
||
) |
Sets an account to be imported on creation of the instance.
importData | The account data to be imported |
keepData | Specify whether the stored data should be preserved |
allowFailure | Specify how a failure to import the account should be treated |
This method is basically a shortcut to import an account on creation - which is faster and much more efficient than first creating a new account and then delete it again to import an existing one. Internally, the same things will happen as with AccountManager::importAccount (or AccountManager::importAccountTrusted).
Whats special is that since there is no account manager to handle a failure, there are two way to handle that. If allowFailure
is false (the default), failing to import the data will trigger a fatal error. If it is set to true, the error is only printed and a new account is created instead, so that the engine can continue running.
QtDataSync::Setup::setAccountTrusted | ( | const QJsonObject & | importData, |
const QString & | password, | ||
bool | keepData = false , |
||
bool | allowFailure = false |
||
) |
Sets an account to be imported on creation of the instance.
password | The password used to decrypt the imported data with. Must be the same as used for the export |
importData | The account data to be imported |
keepData | Specify whether the stored data should be preserved |
allowFailure | Specify how a failure to import the account should be treated |
This method is basically a shortcut to import an account on creation - which is faster and much more efficient than first creating a new account and then delete it again to import an existing one. Internally, the same things will happen as with AccountManager::importAccount (or AccountManager::importAccountTrusted).
Whats special is that since there is no account manager to handle a failure, there are two way to handle that. If allowFailure
is false (the default), failing to import the data will trigger a fatal error. If it is set to true, the error is only printed and a new account is created instead, so that the engine can continue running.
Setup& QtDataSync::Setup::setAccountTrusted | ( | const QByteArray & | importData, |
const QString & | password, | ||
bool | keepData = false , |
||
bool | allowFailure = false |
||
) |
Sets an account to be imported on creation of the instance.
password | The password used to decrypt the imported data with. Must be the same as used for the export |
importData | The account data to be imported |
keepData | Specify whether the stored data should be preserved |
allowFailure | Specify how a failure to import the account should be treated |
This method is basically a shortcut to import an account on creation - which is faster and much more efficient than first creating a new account and then delete it again to import an existing one. Internally, the same things will happen as with AccountManager::importAccount (or AccountManager::importAccountTrusted).
Whats special is that since there is no account manager to handle a failure, there are two way to handle that. If allowFailure
is false (the default), failing to import the data will trigger a fatal error. If it is set to true, the error is only printed and a new account is created instead, so that the engine can continue running.
|
static |
Sets the maximum timeout for shutting down setups.
timeout | The new timeout to be used. |
When shutting down the application, the datasync instances, which are running on different threads, have to shut down as well. Upon destruction, all instances are told to stop, and then the main thread will block until they are done or a timout is reached. the cleanup timout is the maximum time to wait for each thread to finish. The default value is -1, which means there is no timeout. The app will wait infinitely.
|
readwrite |
The size of the internal database cache, in bytes.
Default: 100_mb
All loaded json data is internally cached to speed up frequent read operations on the same items. This property limits the size in bytes that cache can hold at most. If you set it to 0, the caching gets completly deactivated.
Accessors | |
---|---|
READ | cacheSize() |
WRITE | setCacheSize() |
RESET | resetCacheSize() |
|
readwrite |
The size in bytes for the secret exchange key (which is symmetric)
Default: 0
The size of the key depends on the Setup::cipherScheme. If this property is 0, the maximum key size for each algorithm is used. The following table shows what size limits apply per algorithm. Please note that the key size is independend of the mode of operation, i.e. the keysize for AES applies for both, Setup::AES_EAX and Setup::AES_GCM:
Algorithm | Allowed key sizes in bytes, default marked bold |
---|---|
AEX | 16, 24, 32 |
Twofish | 16, 24, 32 |
Serpent | 16, 24, 32 |
IDEA | 16 |
Accessors | |
---|---|
READ | cipherKeySize() |
WRITE | setCipherKeySize() |
RESET | resetCipherKeySize() |
|
readwrite |
The algorithmic scheme to be used for new secret exchange keys (which are symmetric)
Default: Setup::AES_EAX
Accessors | |
---|---|
READ | cipherScheme() |
WRITE | setCipherScheme() |
RESET | resetCipherScheme() |
|
readwrite |
An optional conflict resolver to handle merge conflicts.
Default: nullptr
If you want to use a custom conflict resolver to handle merge conflicts, you can set one for the engine here.
Accessors | |
---|---|
READ | conflictResolver() |
WRITE | setConflictResolver() |
RESET | resetConflictResolver() |
|
readwrite |
The generation parameter for the encryption key.
Default: invalid
The type and interpretation of this parameter depend on the Setup::encryptionScheme. If this property is an invalid QVariant, Defaults::defaultParam is used to determine the default key parameter for each type. The following table shows what the key param must be per scheme:
Scheme | Type | Default value | Description |
---|---|---|---|
Setup::RSA_OAEP_SHA3_512 | int | 4096 | The size of the RSA key |
Setup::ECIES_ECP_SHA3_512 | Setup::EllipticCurve | Setup::brainpoolP384r1 | The elliptic curve and the key size |
Accessors | |
---|---|
READ | encryptionKeyParam() |
WRITE | setEncryptionKeyParam() |
RESET | resetEncryptionKeyParam() |
|
readwrite |
The algorithmic scheme to be used for new encryption keys.
Default: Setup::RSA_OAEP_SHA3_512
Accessors | |
---|---|
READ | encryptionScheme() |
WRITE | setEncryptionScheme() |
RESET | resetEncryptionScheme() |
|
readwrite |
The logging mode for database change events.
Default: EventMode::Unchanged
Event logging is an optional component of datasync that can be used for reliable, deterministic and ordered handling of change events from any source. The main class to access these events is the EventCursor. This property is used to control the activation of this component.
If disabled, no events are logged at all. This means EventCursor becomes unusable. If enabled, all changes to data (create, change and delete) trigger an event that is permanently stored and can be accessed vua the EventCursor. The Unchanged mode simply means "keep it in whatever state it already is" - this is useful if you don't want to break anything
Accessors | |
---|---|
READ | eventLoggingMode() |
WRITE | setEventLoggingMode() |
RESET | resetEventLoggingMode() |
REVISION | 2 |
|
readwrite |
An alternative handler for fatal errors.
Default: invalid
When a fatal error occurs it is reported to the engine via logFatal (which uses Logger::reportFatalError). Such a fatal error must be handelt immediatly, and this method sets the method to be called in such cases.
It is called on the thread where the error happends, and should be used to react to the error in whatever way. A fatal error will render the datasync instance invalid, leading to the unavoidable abortion of the application. You can set a custom handler in order to custom log the error or perform other operations, but at the end the method should not return. This can be archieved by calling for example std::abort in the end.
The default handler, which is used in case no custom handler is set, calls qFatal to present the error and abort the application.
Semantics of the fatalErrorHandler
:
Accessors | |
---|---|
READ | fatalErrorHandler() |
WRITE | setFatalErrorHandler() |
RESET | resetFatalErrorHandler() |
|
readwrite |
The name of the preferred keystore provider.
Default: empty
The keystore is used to securely store private keys on the users device. You can use Setup::availableKeystores to find out which keystores are currently available
The following keystores are support via a plugin that is part of the Library:
$QT_INSTALL_PLUGINS/keystores
folder. If you have the plugins elsewhere, use the PLUGIN_KEYSTORES_PATH
environment variable to specify additional paths, seperated by the system path seperator (:
on unix, ;
on windows)More keystores can be added by creating a custom KeyStorePlugin
Accessors | |
---|---|
READ | keyStoreProvider() |
WRITE | setKeyStoreProvider() |
RESET | resetKeyStoreProvider() |
|
readwrite |
The local storage directoy used by the instance.
Default: "./qtdatasync/default"
The local directory is the heart of the datasync instance. It's where the actual data, configurations etc. are stored. A datasync instance is identified by it's local directory across multiple processes. There can only be one active (but many passive) instances per storage folder. If you want to make use of multiple setups within an application, they all need their own storage directory
Accessors | |
---|---|
READ | localDir() |
WRITE | setLocalDir() |
RESET | resetLocalDir() |
|
readwrite |
Specify whether deleted datasets should persist.
Default: false
Persisting deleted datasets means that even after a dataset has been deleted and all changes synchronized, the information that is was deleted is kept around. This can prevent problems with the synchronisation when keys are reused, as the engine is now able to detect which operation (change or delete) was newer. If not persisting, there is no way to tell if a dataset was deleted or never existed in the first place. In exchange, The database gets filled with deleted entries for every deleted dataset.
Rule of thumb: If you frequently reuse keys, set it to true
. If you create your keys randomly or chances of a key beeing reused are low, do not persist.
Accessors | |
---|---|
READ | persistDeletedVersion() |
WRITE | setPersistDeletedVersion() |
RESET | resetPersistDeletedVersion() |
|
readwrite |
The configuration to be used to connect to the remote.
Default: empty
This part must be set in order to connect to a remote. It is however possible to import the remote configuration instead of using the one provided via this property.
Accessors | |
---|---|
READ | remoteConfiguration() |
WRITE | setRemoteConfiguration() |
RESET | resetRemoteConfiguration() |
|
readwrite |
The url to be used to host the remote object sources, and to connect to to acquire the replicas.
Default: invalid
"threaded://qtdatasync/<setupName>/enginenode"
This url is used to create the host node in the engine that provides internal remote object sources, and to create the QRemoteObjectNode instances to connect to this host node and acquire the replicas. This is all happening internally, so you don't not to worry about the how to.
The default url uses the threaded connection, which is only valid within the same process. For more details, and the alternatives, see Threaded remote objects
Accessors | |
---|---|
READ | remoteObjectHost() |
WRITE | setRemoteObjectHost() |
RESET | resetRemoteObjectHost() |
|
readwrite |
The serializer to be used to serialize and deserialize data to and from the store.
Default: A pointer to a valid serializer
You can use this property to customize how the serializer should serialize data and to register converters for custom types.
Accessors | |
---|---|
READ | serializer() |
WRITE | setSerializer() |
RESET | resetSerializer() |
|
readwrite |
The generation parameter for the signature key.
Default: invalid
The type and interpretation of this parameter depend on the Setup::signatureScheme. If this property is an invalid QVariant, Defaults::defaultParam is used to determine the default key parameter for each type. The following table shows what the key param must be per scheme:
Scheme | Type | Default value | Description |
---|---|---|---|
Setup::RSA_PSS_SHA3_512 | int | 4096 | The size of the RSA key |
Setup::ECDSA_ECP_SHA3_512 | Setup::EllipticCurve | Setup::brainpoolP384r1 | The elliptic curve and the key size |
Setup::ECNR_ECP_SHA3_512 | Setup::EllipticCurve | Setup::brainpoolP384r1 | The elliptic curve and the key size |
Accessors | |
---|---|
READ | signatureKeyParam() |
WRITE | setSignatureKeyParam() |
RESET | resetSignatureKeyParam() |
|
readwrite |
The algorithmic scheme to be used for new signature keys.
Default: Setup::RSA_PSS_SHA3_512
Accessors | |
---|---|
READ | signatureScheme() |
WRITE | setSignatureScheme() |
RESET | resetSignatureScheme() |
|
readwrite |
The ssl configuration to be used to connect to the remote.
Default: QSslConfiguration::defaultConfiguration()
Is used by the internal websocket server. If you need a configuration different from the one for the application, you can set one via this property
Accessors | |
---|---|
READ | sslConfiguration() |
WRITE | setSslConfiguration() |
RESET | resetSslConfiguration() |
|
readwrite |
The policiy for how to handle conflicts.
Default: Setup::PreferChanged
In case a conflict is detected, and one of the changes is a data change, and one is a deletion, this policy decides which of the two should be kept, and which one gets discarded.
Accessors | |
---|---|
READ | syncPolicy() |
WRITE | setSyncPolicy() |
RESET | resetSyncPolicy() |