diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f6e8274b7..f85da6c01 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,7 +34,7 @@ jobs: - name: Grant building script execute permission run : chmod +x ./infomaniak-build-tools/linux/build-ci-amd64.sh - name: Build kDrive desktop - run : ./infomaniak-build-tools/linux/build-ci-amd64.sh + run : ./infomaniak-build-tools/linux/build-ci-amd64.sh -u - name: Grant tests script execute permission run : chmod +x ./infomaniak-build-tools/run-tests.sh diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 989df0e13..eff96b2c5 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -68,7 +68,7 @@ jobs: - name: Grant building script execute permission run : chmod +x ./infomaniak-build-tools/linux/build-ci-amd64.sh - name: Build kDrive desktop - run : ./infomaniak-build-tools/linux/build-ci-amd64.sh release + run : ./infomaniak-build-tools/linux/build-ci-amd64.sh -t release - name: Grant packaging script execute permission run : chmod +x ./infomaniak-build-tools/linux/package-ci-amd64.sh diff --git a/extensions/windows/cfapi/Common/utilities.cpp b/extensions/windows/cfapi/Common/utilities.cpp index 964f338e7..dc4e2a7a1 100644 --- a/extensions/windows/cfapi/Common/utilities.cpp +++ b/extensions/windows/cfapi/Common/utilities.cpp @@ -143,6 +143,7 @@ void Utilities::initPipeName(const wchar_t *appName) { wchar_t userName[DEFAULT_BUFLEN]; GetUserName(userName, &len); s_pipeName = std::wstring(L"\\\\.\\pipe\\") + std::wstring(appName) + L"-" + std::wstring(userName, len); + TRACE_DEBUG(L"Init pipe: name = %ls", s_pipeName.c_str()); } bool Utilities::connectToPipeServer() { @@ -157,6 +158,7 @@ bool Utilities::connectToPipeServer() { return false; } + TRACE_DEBUG(L"Open pipe: name = %ls", s_pipeName.c_str()); while (true) { s_pipe = CreateFile(s_pipeName.data(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (s_pipe != INVALID_HANDLE_VALUE) { diff --git a/extensions/windows/cfapi/Vfs/cloudprovider.cpp b/extensions/windows/cfapi/Vfs/cloudprovider.cpp index d0510a1d3..ff9016d6d 100644 --- a/extensions/windows/cfapi/Vfs/cloudprovider.cpp +++ b/extensions/windows/cfapi/Vfs/cloudprovider.cpp @@ -90,6 +90,7 @@ bool CloudProvider::start(wchar_t *namespaceCLSID, DWORD *namespaceCLSIDSize) { TRACE_ERROR(L"Error in CloudProviderRegistrar::registerWithShell!"); return false; } + TRACE_DEBUG(L"CloudProviderRegistrar::registerWithShell done: syncRootID = %ls", _synRootID.c_str()); // Hook up callback methods for transferring files between client and server TRACE_DEBUG(L"Calling connectSyncRootTransferCallbacks"); diff --git a/extensions/windows/cfapi/Vfs/cloudproviderregistrar.cpp b/extensions/windows/cfapi/Vfs/cloudproviderregistrar.cpp index e5b756e2b..ac340f95b 100644 --- a/extensions/windows/cfapi/Vfs/cloudproviderregistrar.cpp +++ b/extensions/windows/cfapi/Vfs/cloudproviderregistrar.cpp @@ -87,7 +87,16 @@ std::wstring CloudProviderRegistrar::registerWithShell(ProviderInfo *providerInf winrt::StorageProviderSyncRootInfo info; info.Id(syncRootID); +#ifndef NDEBUG + // Silent WINRT_ASSERT(!is_sta()) + int reportMode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); +#endif auto folder = winrt::StorageFolder::GetFolderFromPathAsync(providerInfo->folderPath()).get(); +#ifndef NDEBUG + // Restore old report mode + _CrtSetReportMode(_CRT_ASSERT, reportMode); +#endif + info.Path(folder); info.DisplayNameResource(providerInfo->folderName()); @@ -178,6 +187,7 @@ std::wstring CloudProviderRegistrar::registerWithShell(ProviderInfo *providerInf bool CloudProviderRegistrar::unregister(std::wstring syncRootID) { try { + TRACE_DEBUG(L"StorageProviderSyncRootManager::Unregister: syncRootID = %ls", syncRootID.c_str()); winrt::StorageProviderSyncRootManager::Unregister(syncRootID); } catch (winrt::hresult_error const &ex) { TRACE_ERROR(L"WinRT error caught : hr %08x - %s!", static_cast(winrt::to_hresult()), ex.message().c_str()); diff --git a/extensions/windows/cfapi/Vfs/dllmain.cpp b/extensions/windows/cfapi/Vfs/dllmain.cpp index c00f01cf9..34175baab 100644 --- a/extensions/windows/cfapi/Vfs/dllmain.cpp +++ b/extensions/windows/cfapi/Vfs/dllmain.cpp @@ -81,6 +81,7 @@ DLL_EXP int __cdecl vfsStart(const wchar_t *driveId, const wchar_t *userId, cons if (s_cloudProviders.size() == 0) { if (!Utilities::connectToPipeServer()) { TRACE_ERROR(L"Error in connectToPipeServer!"); + delete cloudProvider; return E_ABORT; } } diff --git a/extensions/windows/cfapi/Vfs/placeholders.cpp b/extensions/windows/cfapi/Vfs/placeholders.cpp index 97504c2df..1fd08dc8c 100644 --- a/extensions/windows/cfapi/Vfs/placeholders.cpp +++ b/extensions/windows/cfapi/Vfs/placeholders.cpp @@ -45,6 +45,7 @@ bool Placeholders::create(const PCWSTR fileId, const PCWSTR relativePath, const cloudEntry.FsMetadata.BasicInfo.ChangeTime = Utilities::fileTimeToLargeInteger(findData->ftLastWriteTime); if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + cloudEntry.Flags |= CF_PLACEHOLDER_CREATE_FLAG_DISABLE_ON_DEMAND_POPULATION; cloudEntry.FsMetadata.FileSize.QuadPart = 0; } else { cloudEntry.FsMetadata.FileSize.QuadPart = ((ULONGLONG) findData->nFileSizeHigh << 32) + findData->nFileSizeLow; diff --git a/infomaniak-build-tools/kDrive-template.html b/infomaniak-build-tools/kDrive-template.html index a32fd4f46..e10a6dac3 100644 --- a/infomaniak-build-tools/kDrive-template.html +++ b/infomaniak-build-tools/kDrive-template.html @@ -19,18 +19,27 @@ -

Release notes - kDrive 3.6.6

+

Release notes - kDrive 3.6.7

+

New features

+

Bug fixes

\ No newline at end of file diff --git a/infomaniak-build-tools/linux/build-ci-amd64.sh b/infomaniak-build-tools/linux/build-ci-amd64.sh index c4446c281..f430321fe 100644 --- a/infomaniak-build-tools/linux/build-ci-amd64.sh +++ b/infomaniak-build-tools/linux/build-ci-amd64.sh @@ -20,16 +20,68 @@ set -xe -BUILDTYPE="Debug" -if [[ $1 == 'release' ]]; then - BUILDTYPE="Release"; +program_name="$(basename "$0")" + +function display_help { + echo "$program_name [-h] [-t build-type] [-u unit-tests]" + echo " Build the desktop-kDrive application for Linux Amd64 with the specified build type ." + echo "where:" + echo "-h Show this help text." + echo "-t " + echo " Set the type of the build. Defaults to 'debug'. The valid values are: 'debug' or 'release'." + echo "-u " + echo " Activate the build of unit tests. Without this flag, unit tests will not be built." +} + + +unit_tests=0 +build_type="debug" + +while : +do + case "$1" in + -t | --build-type) + build_type="$2" + shift 2 + ;; + -u | --unit-tests) + unit_tests=1 + shift 1 + ;; + -h | --help) + display_help + exit 0 + ;; + --) # End of all options + shift + break + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) # No more options + break + ;; + esac +done + + +if [[ $build_type == "release" ]]; then + build_type="Release" +elif [[ $build_type == "debug" ]]; then + build_type="Debug" fi +echo "Build type: $build_type" +echo "Unit tests build flag: $unit_tests" + + export QT_BASE_DIR="~/Qt/6.2.3" export QTDIR="$QT_BASE_DIR/gcc_64" export BASEPATH=$PWD export CONTENTDIR="$BASEPATH/build-linux" -export BUILDDIR="$CONTENTDIR/build" +export build_dir="$CONTENTDIR/build" export APPDIR="$CONTENTDIR/app" extract_debug () { @@ -39,7 +91,7 @@ extract_debug () { } mkdir -p $APPDIR -mkdir -p $BUILDDIR +mkdir -p $build_dir export QMAKE=$QTDIR/bin/qmake export PATH=$QTDIR/bin:$QTDIR/libexec:$PATH @@ -50,14 +102,14 @@ export PKG_CONFIG_PATH=$QTDIR/lib/pkgconfig:$PKG_CONFIG_PATH export SUFFIX="" # Build client -cd $BUILDDIR -mkdir -p $BUILDDIR/client +cd $build_dir +mkdir -p $build_dir/client CMAKE_PARAMS=() export KDRIVE_DEBUG=0 -cmake -B$BUILDDIR -H$BASEPATH \ +cmake -B$build_dir -H$BASEPATH \ -DOPENSSL_ROOT_DIR=/usr/local \ -DOPENSSL_INCLUDE_DIR=/usr/local/include \ -DOPENSSL_CRYPTO_LIBRARY=/usr/local/lib64/libcrypto.so \ @@ -66,11 +118,11 @@ cmake -B$BUILDDIR -H$BASEPATH \ -DCMAKE_BUILDTYPE=$BUILDTYPE \ -DCMAKE_PREFIX_PATH=$BASEPATH \ -DCMAKE_INSTALL_PREFIX=/usr \ - -DBIN_INSTALL_DIR=$BUILDDIR/client \ + -DBIN_INSTALL_DIR=$build_dir/client \ -DKDRIVE_VERSION_SUFFIX=$SUFFIX \ -DKDRIVE_THEME_DIR="$BASEPATH/infomaniak" \ -DKDRIVE_VERSION_BUILD="$(date +%Y%m%d)" \ - -DBUILD_UNIT_TESTS=1 \ + -DBUILD_UNIT_TESTS=$unit_tests \ "${CMAKE_PARAMS[@]}" \ make -j$(nproc) @@ -80,4 +132,4 @@ extract_debug ./bin kDrive_client make DESTDIR=$APPDIR install -cp $BASEPATH/sync-exclude-linux.lst $BUILDDIR/bin/sync-exclude.lst +cp $BASEPATH/sync-exclude-linux.lst $build_dir/bin/sync-exclude.lst diff --git a/infomaniak-build-tools/macos/build-ci.sh b/infomaniak-build-tools/macos/build-ci.sh index 26247e6a3..a9e7f1187 100644 --- a/infomaniak-build-tools/macos/build-ci.sh +++ b/infomaniak-build-tools/macos/build-ci.sh @@ -40,15 +40,17 @@ export PATH=$QTDIR/bin:$PATH # Set Infomaniak Theme KDRIVE_DIR="$SRCDIR/infomaniak" -# Path to Sparkle installation -SPARKLE_DIR="$HOME/Library/Frameworks" +# Set build dir +BUILDDIR="$PWD/build-macos/client" + +# Set install dir +INSTALLDIR="$PWD/build-macos/client/install" -# Prepare directory +# Create install dir if needed mkdir -p build-macos/client -mkdir -p build-macos/install -INSTALLDIR="$PWD/build-macos/install" -BUILDDIR="$PWD/build-macos/client" +mkdir -p build-macos/client/install +# Backup the existing .app if there is one if [ -d "$INSTALLDIR/$APPNAME-old.app" ]; then rm -rf "$INSTALLDIR/$APPNAME-old.app" fi @@ -57,40 +59,31 @@ if [ -d "$INSTALLDIR/$APPNAME.app" ]; then cp -a "$INSTALLDIR/$APPNAME.app" "$INSTALLDIR/$APPNAME-old.app" fi -pushd "$BUILDDIR" - +# Prepare additional cmake arguments if [ -z "$KDRIVE_VERSION_BUILD" ]; then KDRIVE_VERSION_BUILD="$(date +%Y%m%d)" fi -# Prepare cmake arguments CMAKE_PARAMS=(-DKDRIVE_VERSION_BUILD="$KDRIVE_VERSION_BUILD") if [ -n "$TEAM_IDENTIFIER" -a -n "$SIGN_IDENTITY" ]; then CMAKE_PARAMS+=(-DSOCKETAPI_TEAM_IDENTIFIER_PREFIX="$TEAM_IDENTIFIER.") fi -if [ -n "$APPLICATION_SERVER_URL" ]; then - CMAKE_PARAMS+=(-DAPPLICATION_SERVER_URL="$APPLICATION_SERVER_URL") -fi +# Configure +pushd "$BUILDDIR" # Configure infomaniakdrive cmake \ -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" \ -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" \ -DCMAKE_BUILD_TYPE=Release \ - -DSPARKLE_LIBRARY="$SPARKLE_DIR/Sparkle.framework" \ - -DOPENSSL_ROOT_DIR="/usr/local/" \ - -DOPENSSL_INCLUDE_DIR="/usr/local/include/" \ - -DOPENSSL_CRYPTO_LIBRARY="/usr/local/lib/libcrypto.dylib" \ - -DOPENSSL_SSL_LIBRARY="/usr/local/lib/libssl.dylib" \ -DKDRIVE_THEME_DIR="$KDRIVE_DIR" \ - -DQTDIR="$QTDIR" \ -DBUILD_UNIT_TESTS=1 \ "${CMAKE_PARAMS[@]}" \ "$SRCDIR" # Build kDrive sources -make -j6 install +make -j6 all install popd diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-de.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-de.html new file mode 100644 index 000000000..26227bea2 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-de.html @@ -0,0 +1,44 @@ + + + + + +

Versionshinweise - kDrive 3.6.7

+

Neue Funktionen

+
    +
  • Änderung des Aktualisierungsmechanismus, um die schrittweise Bereitstellung einer neuen Version zu ermöglichen
  • +
+

Fehlerbehebungen

+
    +
  • Behebung eines Dateisynchronisationsproblems, das insbesondere die Office-Suite betrifft
  • +
  • Neue Datei wurde im LiteSync-Modus fälschlicherweise heruntergeladen
  • +
  • Fehlende Einträge für Freigabelinks im Kontextmenü behoben
  • +
  • Konsolidierung des Fehlermanagements während der Ausbreitungsphase
  • +
  • Start der Anwendung nicht blockieren, wenn der lokale Synchronisierungsordner fehlt
  • +
  • Falsch auf die schwarze Liste gesetzte Dateien korrigiert
  • +
  • Fehlerhafte Synchronisierung auf FAT-Systemen behoben
  • +
  • Startproblem im Falle eines unbekannten Proxy-Typs behoben
  • +
  • Behobene Migrationsprobleme
  • +
  • Status bei Abbruch während des Uploads korrigiert
  • +
  • Ausschließen von Dateien und Ordnern mit einer Namenslänge von mehr als 255 Zeichen
  • +
  • Fehler bei der CSV-Analyse behoben, der zu einem unvollständigen Remote-Snapshot führte
  • +
  • Verbesserte Behandlung von Fehlern bei verweigertem Zugriff
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-en.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-en.html new file mode 100644 index 000000000..120319f26 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-en.html @@ -0,0 +1,44 @@ + + + + + +

Release notes - kDrive 3.6.7

+

New features

+
    +
  • Change the update mechanism to allow progressive delivery of a new version
  • +
+

Bug fixes

+
    +
  • Resolution of a file synchronization problem affecting the Office suite in particular
  • +
  • Fixed new file wrongly downloaded in LiteSync mode
  • +
  • Fixed missing share links entries in contextual menu
  • +
  • Consolidation of error management during the propagation step
  • +
  • Do not block application startup if local sync folder is missing
  • +
  • Fixed wrongly blacklisted files
  • +
  • Fixed broken sync on FAT systems
  • +
  • Fixed startup issue in case of unknown proxy type
  • +
  • Fixed migration issues
  • +
  • Fixed status in case of abort during upload
  • +
  • Exclude files and folders with name length above 255 characters
  • +
  • Fixed CSV parsing error leading to incomplete remote snapshot
  • +
  • Improved handling of access denied errors
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-es.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-es.html new file mode 100644 index 000000000..e40e56611 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-es.html @@ -0,0 +1,44 @@ + + + + + +

Notas de la versión - kDrive 3.6.7

+

Novedades

+
    +
  • Cambiar el mecanismo de actualización para permitir la entrega progresiva de una nueva versión
  • +
+

Corrección de errores

+
    +
  • Resolución de un problema de sincronización de archivos que afectaba sobre todo al paquete Office.
  • +
  • Corregido el error de descarga de un nuevo archivo en el modo LiteSync
  • +
  • Se ha corregido la falta de entradas de enlaces compartidos en el menú contextual.
  • +
  • Consolidación de la gestión de errores durante la etapa de propagación
  • +
  • No bloquear el inicio de la aplicación si falta la carpeta de sincronización local
  • +
  • Corrección de archivos incluidos erróneamente en la lista negra
  • +
  • Corregida la sincronización rota en sistemas FAT
  • +
  • Se ha corregido un problema de inicio en caso de tipo de proxy desconocido.
  • +
  • Solucionados los problemas de migración
  • +
  • Corregido el estado en caso de aborto durante la carga
  • +
  • Excluir archivos y carpetas cuyo nombre tenga más de 255 caracteres
  • +
  • Se ha corregido un error de análisis CSV que provocaba una instantánea remota incompleta.
  • +
  • Mejora de la gestión de los errores de acceso denegado
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-fr.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-fr.html new file mode 100644 index 000000000..5a93f04dc --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-fr.html @@ -0,0 +1,44 @@ + + + + + +

Notes de version - kDrive 3.6.7

+

Nouvelles fonctionnalités

+
    +
  • Modifier le mécanisme de mise à jour pour permettre la livraison progressive d'une nouvelle version
  • +
+

Correction de bugs

+
    +
  • Résolution d'un problème de synchronisation de fichiers affectant notamment la suite Office
  • +
  • Correction d'un nouveau fichier mal téléchargé en mode LiteSync
  • +
  • Correction de l'absence de liens de partage dans le menu contextuel
  • +
  • Consolidation de la gestion des erreurs lors de l'étape de propagation
  • +
  • Ne pas bloquer le démarrage de l'application si le dossier de synchronisation local est manquant
  • +
  • Correction des fichiers mis sur liste noire à tort
  • +
  • Correction de la synchronisation interrompue sur les systèmes FAT
  • +
  • Correction du problème de démarrage en cas de type de proxy inconnu
  • +
  • Correction des problèmes de migration
  • +
  • Correction de l'état en cas d'interruption du téléchargement
  • +
  • Exclure les fichiers et les dossiers dont le nom comporte plus de 255 caractères.
  • +
  • Correction d'une erreur d'analyse CSV entraînant un instantané à distance incomplet
  • +
  • Amélioration de la gestion des erreurs de refus d'accès
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-it.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-it.html new file mode 100644 index 000000000..5ed9fa53e --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-linux-it.html @@ -0,0 +1,44 @@ + + + + + +

Note di rilascio - kDrive 3.6.7

+

Nuove caratteristiche

+
    +
  • Modificare il meccanismo di aggiornamento per consentire la consegna progressiva di una nuova versione.
  • +
+

Correzioni di bug

+
    +
  • Risoluzione di un problema di sincronizzazione dei file che riguardava in particolare la suite Office.
  • +
  • Corretto il nuovo file scaricato erroneamente in modalità LiteSync
  • +
  • Correzione delle voci mancanti dei link di condivisione nel menu contestuale
  • +
  • Consolidamento della gestione degli errori durante la fase di propagazione
  • +
  • Non bloccare l'avvio dell'applicazione se manca la cartella di sincronizzazione locale
  • +
  • Corretti i file erroneamente inseriti nella lista nera
  • +
  • Corretta la sincronizzazione interrotta sui sistemi FAT
  • +
  • Corretto il problema di avvio in caso di tipo di proxy sconosciuto
  • +
  • Problemi di migrazione corretti
  • +
  • Corretto lo stato in caso di interruzione del caricamento
  • +
  • Escludete i file e le cartelle con nomi di lunghezza superiore a 255 caratteri.
  • +
  • Corretto l'errore di parsing CSV che portava a un'istantanea remota incompleta.
  • +
  • Gestione migliorata degli errori di accesso negato
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-de.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-de.html new file mode 100644 index 000000000..26227bea2 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-de.html @@ -0,0 +1,44 @@ + + + + + +

Versionshinweise - kDrive 3.6.7

+

Neue Funktionen

+
    +
  • Änderung des Aktualisierungsmechanismus, um die schrittweise Bereitstellung einer neuen Version zu ermöglichen
  • +
+

Fehlerbehebungen

+
    +
  • Behebung eines Dateisynchronisationsproblems, das insbesondere die Office-Suite betrifft
  • +
  • Neue Datei wurde im LiteSync-Modus fälschlicherweise heruntergeladen
  • +
  • Fehlende Einträge für Freigabelinks im Kontextmenü behoben
  • +
  • Konsolidierung des Fehlermanagements während der Ausbreitungsphase
  • +
  • Start der Anwendung nicht blockieren, wenn der lokale Synchronisierungsordner fehlt
  • +
  • Falsch auf die schwarze Liste gesetzte Dateien korrigiert
  • +
  • Fehlerhafte Synchronisierung auf FAT-Systemen behoben
  • +
  • Startproblem im Falle eines unbekannten Proxy-Typs behoben
  • +
  • Behobene Migrationsprobleme
  • +
  • Status bei Abbruch während des Uploads korrigiert
  • +
  • Ausschließen von Dateien und Ordnern mit einer Namenslänge von mehr als 255 Zeichen
  • +
  • Fehler bei der CSV-Analyse behoben, der zu einem unvollständigen Remote-Snapshot führte
  • +
  • Verbesserte Behandlung von Fehlern bei verweigertem Zugriff
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-en.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-en.html new file mode 100644 index 000000000..120319f26 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-en.html @@ -0,0 +1,44 @@ + + + + + +

Release notes - kDrive 3.6.7

+

New features

+
    +
  • Change the update mechanism to allow progressive delivery of a new version
  • +
+

Bug fixes

+
    +
  • Resolution of a file synchronization problem affecting the Office suite in particular
  • +
  • Fixed new file wrongly downloaded in LiteSync mode
  • +
  • Fixed missing share links entries in contextual menu
  • +
  • Consolidation of error management during the propagation step
  • +
  • Do not block application startup if local sync folder is missing
  • +
  • Fixed wrongly blacklisted files
  • +
  • Fixed broken sync on FAT systems
  • +
  • Fixed startup issue in case of unknown proxy type
  • +
  • Fixed migration issues
  • +
  • Fixed status in case of abort during upload
  • +
  • Exclude files and folders with name length above 255 characters
  • +
  • Fixed CSV parsing error leading to incomplete remote snapshot
  • +
  • Improved handling of access denied errors
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-es.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-es.html new file mode 100644 index 000000000..e40e56611 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-es.html @@ -0,0 +1,44 @@ + + + + + +

Notas de la versión - kDrive 3.6.7

+

Novedades

+
    +
  • Cambiar el mecanismo de actualización para permitir la entrega progresiva de una nueva versión
  • +
+

Corrección de errores

+
    +
  • Resolución de un problema de sincronización de archivos que afectaba sobre todo al paquete Office.
  • +
  • Corregido el error de descarga de un nuevo archivo en el modo LiteSync
  • +
  • Se ha corregido la falta de entradas de enlaces compartidos en el menú contextual.
  • +
  • Consolidación de la gestión de errores durante la etapa de propagación
  • +
  • No bloquear el inicio de la aplicación si falta la carpeta de sincronización local
  • +
  • Corrección de archivos incluidos erróneamente en la lista negra
  • +
  • Corregida la sincronización rota en sistemas FAT
  • +
  • Se ha corregido un problema de inicio en caso de tipo de proxy desconocido.
  • +
  • Solucionados los problemas de migración
  • +
  • Corregido el estado en caso de aborto durante la carga
  • +
  • Excluir archivos y carpetas cuyo nombre tenga más de 255 caracteres
  • +
  • Se ha corregido un error de análisis CSV que provocaba una instantánea remota incompleta.
  • +
  • Mejora de la gestión de los errores de acceso denegado
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-fr.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-fr.html new file mode 100644 index 000000000..5a93f04dc --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-fr.html @@ -0,0 +1,44 @@ + + + + + +

Notes de version - kDrive 3.6.7

+

Nouvelles fonctionnalités

+
    +
  • Modifier le mécanisme de mise à jour pour permettre la livraison progressive d'une nouvelle version
  • +
+

Correction de bugs

+
    +
  • Résolution d'un problème de synchronisation de fichiers affectant notamment la suite Office
  • +
  • Correction d'un nouveau fichier mal téléchargé en mode LiteSync
  • +
  • Correction de l'absence de liens de partage dans le menu contextuel
  • +
  • Consolidation de la gestion des erreurs lors de l'étape de propagation
  • +
  • Ne pas bloquer le démarrage de l'application si le dossier de synchronisation local est manquant
  • +
  • Correction des fichiers mis sur liste noire à tort
  • +
  • Correction de la synchronisation interrompue sur les systèmes FAT
  • +
  • Correction du problème de démarrage en cas de type de proxy inconnu
  • +
  • Correction des problèmes de migration
  • +
  • Correction de l'état en cas d'interruption du téléchargement
  • +
  • Exclure les fichiers et les dossiers dont le nom comporte plus de 255 caractères.
  • +
  • Correction d'une erreur d'analyse CSV entraînant un instantané à distance incomplet
  • +
  • Amélioration de la gestion des erreurs de refus d'accès
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-it.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-it.html new file mode 100644 index 000000000..5ed9fa53e --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-macos-it.html @@ -0,0 +1,44 @@ + + + + + +

Note di rilascio - kDrive 3.6.7

+

Nuove caratteristiche

+
    +
  • Modificare il meccanismo di aggiornamento per consentire la consegna progressiva di una nuova versione.
  • +
+

Correzioni di bug

+
    +
  • Risoluzione di un problema di sincronizzazione dei file che riguardava in particolare la suite Office.
  • +
  • Corretto il nuovo file scaricato erroneamente in modalità LiteSync
  • +
  • Correzione delle voci mancanti dei link di condivisione nel menu contestuale
  • +
  • Consolidamento della gestione degli errori durante la fase di propagazione
  • +
  • Non bloccare l'avvio dell'applicazione se manca la cartella di sincronizzazione locale
  • +
  • Corretti i file erroneamente inseriti nella lista nera
  • +
  • Corretta la sincronizzazione interrotta sui sistemi FAT
  • +
  • Corretto il problema di avvio in caso di tipo di proxy sconosciuto
  • +
  • Problemi di migrazione corretti
  • +
  • Corretto lo stato in caso di interruzione del caricamento
  • +
  • Escludete i file e le cartelle con nomi di lunghezza superiore a 255 caratteri.
  • +
  • Corretto l'errore di parsing CSV che portava a un'istantanea remota incompleta.
  • +
  • Gestione migliorata degli errori di accesso negato
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-de.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-de.html new file mode 100644 index 000000000..b357c56ba --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-de.html @@ -0,0 +1,45 @@ + + + + + +

Versionshinweise - kDrive 3.6.7

+

Neue Funktionen

+
    +
  • Änderung des Aktualisierungsmechanismus, um die schrittweise Bereitstellung einer neuen Version zu ermöglichen
  • +
+

Fehlerbehebungen

+
    +
  • Behebung eines Dateisynchronisationsproblems, das insbesondere die Office-Suite betrifft
  • +
  • Neue Datei wurde im LiteSync-Modus fälschlicherweise heruntergeladen
  • +
  • Fehlende Einträge für Freigabelinks im Kontextmenü behoben
  • +
  • Konsolidierung des Fehlermanagements während der Ausbreitungsphase
  • +
  • Start der Anwendung nicht blockieren, wenn der lokale Synchronisierungsordner fehlt
  • +
  • Falsch auf die schwarze Liste gesetzte Dateien korrigiert
  • +
  • Fehlerhafte Synchronisierung auf FAT-Systemen behoben
  • +
  • Startproblem im Falle eines unbekannten Proxy-Typs behoben
  • +
  • Behobene Migrationsprobleme
  • +
  • Status bei Abbruch während des Uploads korrigiert
  • +
  • Ausschließen von Dateien und Ordnern mit einer Namenslänge von mehr als 255 Zeichen
  • +
  • Problem mit Symkinks und Abzweigungen behoben
  • +
  • Fehler bei der CSV-Analyse behoben, der zu einem unvollständigen Remote-Snapshot führte
  • +
  • Verbesserte Behandlung von Fehlern bei verweigertem Zugriff
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-en.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-en.html new file mode 100644 index 000000000..4bf4e60b6 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-en.html @@ -0,0 +1,45 @@ + + + + + +

Release notes - kDrive 3.6.7

+

New features

+
    +
  • Change the update mechanism to allow progressive delivery of a new version
  • +
+

Bug fixes

+
    +
  • Resolution of a file synchronization problem affecting the Office suite in particular
  • +
  • Fixed new file wrongly downloaded in LiteSync mode
  • +
  • Fixed missing share links entries in contextual menu
  • +
  • Consolidation of error management during the propagation step
  • +
  • Do not block application startup if local sync folder is missing
  • +
  • Fixed wrongly blacklisted files
  • +
  • Fixed broken sync on FAT systems
  • +
  • Fixed startup issue in case of unknown proxy type
  • +
  • Fixed migration issues
  • +
  • Fixed status in case of abort during upload
  • +
  • Exclude files and folders with name length above 255 characters
  • +
  • Fixed issue on symkinks and junctions
  • +
  • Fixed CSV parsing error leading to incomplete remote snapshot
  • +
  • Improved handling of access denied errors
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-es.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-es.html new file mode 100644 index 000000000..517ae4a5b --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-es.html @@ -0,0 +1,45 @@ + + + + + +

Notas de la versión - kDrive 3.6.7

+

Novedades

+
    +
  • Cambiar el mecanismo de actualización para permitir la entrega progresiva de una nueva versión
  • +
+

Corrección de errores

+
    +
  • Resolución de un problema de sincronización de archivos que afectaba sobre todo al paquete Office.
  • +
  • Corregido el error de descarga de un nuevo archivo en el modo LiteSync
  • +
  • Se ha corregido la falta de entradas de enlaces compartidos en el menú contextual.
  • +
  • Consolidación de la gestión de errores durante la etapa de propagación
  • +
  • No bloquear el inicio de la aplicación si falta la carpeta de sincronización local
  • +
  • Corrección de archivos incluidos erróneamente en la lista negra
  • +
  • Corregida la sincronización rota en sistemas FAT
  • +
  • Se ha corregido un problema de inicio en caso de tipo de proxy desconocido.
  • +
  • Solucionados los problemas de migración
  • +
  • Corregido el estado en caso de aborto durante la carga
  • +
  • Excluir archivos y carpetas cuyo nombre tenga más de 255 caracteres
  • +
  • Se ha solucionado un problema con los enlaces simbólicos y los cruces.
  • +
  • Se ha corregido un error de análisis CSV que provocaba una instantánea remota incompleta.
  • +
  • Mejora de la gestión de los errores de acceso denegado
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-fr.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-fr.html new file mode 100644 index 000000000..6885d5f55 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-fr.html @@ -0,0 +1,45 @@ + + + + + +

Notes de version - kDrive 3.6.7

+

Nouvelles fonctionnalités

+
    +
  • Modifier le mécanisme de mise à jour pour permettre la livraison progressive d'une nouvelle version
  • +
+

Correction de bugs

+
    +
  • Résolution d'un problème de synchronisation de fichiers affectant notamment la suite Office
  • +
  • Correction d'un nouveau fichier mal téléchargé en mode LiteSync
  • +
  • Correction de l'absence de liens de partage dans le menu contextuel
  • +
  • Consolidation de la gestion des erreurs lors de l'étape de propagation
  • +
  • Ne pas bloquer le démarrage de l'application si le dossier de synchronisation local est manquant
  • +
  • Correction des fichiers mis sur liste noire à tort
  • +
  • Correction de la synchronisation interrompue sur les systèmes FAT
  • +
  • Correction du problème de démarrage en cas de type de proxy inconnu
  • +
  • Correction des problèmes de migration
  • +
  • Correction de l'état en cas d'interruption du téléchargement
  • +
  • Exclure les fichiers et les dossiers dont le nom comporte plus de 255 caractères.
  • +
  • Correction d'un problème sur les liens et les jonctions de systèmes
  • +
  • Correction d'une erreur d'analyse CSV entraînant un instantané à distance incomplet
  • +
  • Amélioration de la gestion des erreurs de refus d'accès
  • +
+ + \ No newline at end of file diff --git a/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-it.html b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-it.html new file mode 100644 index 000000000..5e1b5ed34 --- /dev/null +++ b/release_notes/kDrive-3.6.7.20241120/kDrive-3.6.7.20241120-win-it.html @@ -0,0 +1,45 @@ + + + + + +

Note di rilascio - kDrive 3.6.7

+

Nuove caratteristiche

+
    +
  • Modificare il meccanismo di aggiornamento per consentire la consegna progressiva di una nuova versione.
  • +
+

Correzioni di bug

+
    +
  • Risoluzione di un problema di sincronizzazione dei file che riguardava in particolare la suite Office.
  • +
  • Corretto il nuovo file scaricato erroneamente in modalità LiteSync
  • +
  • Correzione delle voci mancanti dei link di condivisione nel menu contestuale
  • +
  • Consolidamento della gestione degli errori durante la fase di propagazione
  • +
  • Non bloccare l'avvio dell'applicazione se manca la cartella di sincronizzazione locale
  • +
  • Corretti i file erroneamente inseriti nella lista nera
  • +
  • Corretta la sincronizzazione interrotta sui sistemi FAT
  • +
  • Corretto il problema di avvio in caso di tipo di proxy sconosciuto
  • +
  • Problemi di migrazione corretti
  • +
  • Corretto lo stato in caso di interruzione del caricamento
  • +
  • Escludete i file e le cartelle con nomi di lunghezza superiore a 255 caratteri.
  • +
  • Corretto il problema dei collegamenti simbolici e delle giunzioni
  • +
  • Corretto l'errore di parsing CSV che portava a un'istantanea remota incompleta.
  • +
  • Gestione migliorata degli errori di accesso negato
  • +
+ + \ No newline at end of file diff --git a/src/3rdparty/QProgressIndicator/QProgressIndicator.cpp b/src/3rdparty/QProgressIndicator/QProgressIndicator.cpp index 58a1d2070..52e6efba2 100644 --- a/src/3rdparty/QProgressIndicator/QProgressIndicator.cpp +++ b/src/3rdparty/QProgressIndicator/QProgressIndicator.cpp @@ -26,95 +26,73 @@ #include -QProgressIndicator::QProgressIndicator(QWidget* parent) - : QWidget(parent), - m_angle(0), - m_timerId(-1), - m_delay(40), - m_displayedWhenStopped(false), - m_color(Qt::black) -{ +QProgressIndicator::QProgressIndicator(QWidget* parent) : + QWidget(parent), m_angle(0), m_timerId(-1), m_delay(40), m_displayedWhenStopped(false), m_color(Qt::black) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setFocusPolicy(Qt::NoFocus); } -bool QProgressIndicator::isAnimated () const -{ +bool QProgressIndicator::isAnimated() const { return (m_timerId != -1); } -void QProgressIndicator::setDisplayedWhenStopped(bool state) -{ +void QProgressIndicator::setDisplayedWhenStopped(bool state) { m_displayedWhenStopped = state; update(); } -bool QProgressIndicator::isDisplayedWhenStopped() const -{ +bool QProgressIndicator::isDisplayedWhenStopped() const { return m_displayedWhenStopped; } -void QProgressIndicator::startAnimation() -{ +void QProgressIndicator::startAnimation() { m_angle = 0; - if (m_timerId == -1) - m_timerId = startTimer(m_delay); + if (m_timerId == -1) m_timerId = startTimer(m_delay); } -void QProgressIndicator::stopAnimation() -{ - if (m_timerId != -1) - killTimer(m_timerId); +void QProgressIndicator::stopAnimation() { + if (m_timerId != -1) killTimer(m_timerId); m_timerId = -1; update(); } -void QProgressIndicator::setAnimationDelay(int delay) -{ - if (m_timerId != -1) - killTimer(m_timerId); +void QProgressIndicator::setAnimationDelay(int delay) { + if (m_timerId != -1) killTimer(m_timerId); m_delay = delay; - if (m_timerId != -1) - m_timerId = startTimer(m_delay); + if (m_timerId != -1) m_timerId = startTimer(m_delay); } -void QProgressIndicator::setColor(const QColor & color) -{ +void QProgressIndicator::setColor(const QColor& color) { m_color = color; update(); } -QSize QProgressIndicator::sizeHint() const -{ - return QSize(20,20); +QSize QProgressIndicator::sizeHint() const { + return QSize(20, 20); } -int QProgressIndicator::heightForWidth(int w) const -{ +int QProgressIndicator::heightForWidth(int w) const { return w; } -void QProgressIndicator::timerEvent(QTimerEvent * /*event*/) -{ - m_angle = (m_angle+30)%360; +void QProgressIndicator::timerEvent(QTimerEvent* /*event*/) { + m_angle = (m_angle + 30) % 360; update(); } -void QProgressIndicator::paintEvent(QPaintEvent * /*event*/) -{ - if (!m_displayedWhenStopped && !isAnimated()) - return; +void QProgressIndicator::paintEvent(QPaintEvent* /*event*/) { + if (!m_displayedWhenStopped && !isAnimated()) return; int width = qMin(this->width(), this->height()); - + QPainter p(this); p.setRenderHint(QPainter::Antialiasing); @@ -122,25 +100,20 @@ void QProgressIndicator::paintEvent(QPaintEvent * /*event*/) int innerRadius = static_cast(std::round((width - 1) * 0.5 * 0.38)); int capsuleHeight = outerRadius - innerRadius; - int capsuleWidth = (width > 32) ? static_cast(std::round(capsuleHeight * .23)) - : static_cast(std::round(capsuleHeight * .35)); - int capsuleRadius = capsuleWidth/2; + int capsuleWidth = + (width > 32) ? static_cast(std::round(capsuleHeight * .23)) : static_cast(std::round(capsuleHeight * .35)); + int capsuleRadius = capsuleWidth / 2; - for (int i=0; i<12; i++) - { + for (int i = 0; i < 12; i++) { QColor color = m_color; - color.setAlphaF(1.0f - (i/12.0f)); + color.setAlphaF(1.0f - (i / 12.0f)); p.setPen(Qt::NoPen); - p.setBrush(color); + p.setBrush(color); p.save(); p.translate(rect().center()); - p.rotate(m_angle - i*30.0f); - p.drawRoundedRect(static_cast(std::round(-capsuleWidth * 0.5)), - -(innerRadius + capsuleHeight), - capsuleWidth, - capsuleHeight, - capsuleRadius, - capsuleRadius); + p.rotate(m_angle - i * 30.0f); + p.drawRoundedRect(static_cast(std::round(-capsuleWidth * 0.5)), -(innerRadius + capsuleHeight), capsuleWidth, + capsuleHeight, capsuleRadius, capsuleRadius); p.restore(); } } diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 0885f4d2c..3b8566444 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index f730082ad..dd03ddc08 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -261,7 +261,6 @@ target_link_libraries(${APPLICATION_CLIENT_EXECUTABLE} Qt6::Network Qt6::Sql Qt6::SvgWidgets Qt6::WebEngineWidgets sentry::sentry Poco::Foundation Poco::Net - ${libcommon_NAME} ${libcommongui_NAME}) ## handle DBUS for Fdo notifications diff --git a/src/gui/abstractfileitemwidget.cpp b/src/gui/abstractfileitemwidget.cpp index 04c317bea..a2e5d24c3 100644 --- a/src/gui/abstractfileitemwidget.cpp +++ b/src/gui/abstractfileitemwidget.cpp @@ -19,6 +19,7 @@ #include "abstractfileitemwidget.h" #include "gui/custommessagebox.h" #include "guiutility.h" +#include "libcommon/utility/utility.h" #include #include diff --git a/src/gui/appclient.cpp b/src/gui/appclient.cpp index 28dd72f9f..b0532cbb9 100644 --- a/src/gui/appclient.cpp +++ b/src/gui/appclient.cpp @@ -48,8 +48,6 @@ #include #include -#define LITE_SYNC_EXT_BUNDLE_ID "com.infomaniak.drive.desktopclient.LiteSyncExt" - #define CONNECTION_TRIALS 3 #define CHECKCOMMSTATUS_TRIALS 5 diff --git a/src/gui/confirmsynchronizationdialog.cpp b/src/gui/confirmsynchronizationdialog.cpp index 0f18adf63..49f34024f 100644 --- a/src/gui/confirmsynchronizationdialog.cpp +++ b/src/gui/confirmsynchronizationdialog.cpp @@ -26,7 +26,6 @@ #include #include -#include #include namespace KDC { diff --git a/src/gui/foldertreeitemwidget.h b/src/gui/foldertreeitemwidget.h index 52369eb4a..e51662acd 100644 --- a/src/gui/foldertreeitemwidget.h +++ b/src/gui/foldertreeitemwidget.h @@ -26,7 +26,6 @@ #include #include #include -#include namespace KDC { diff --git a/src/gui/guiutility.cpp b/src/gui/guiutility.cpp index f54917aef..d957adff2 100644 --- a/src/gui/guiutility.cpp +++ b/src/gui/guiutility.cpp @@ -49,8 +49,6 @@ #include #endif -#define LITE_SYNC_EXT_BUNDLE_ID "com.infomaniak.drive.desktopclient.LiteSyncExt" - namespace KDC { static const QString styleSheetWhiteFile(":/client/resources/styles/stylesheetwhite.qss"); diff --git a/src/gui/synchronizeditemwidget.cpp b/src/gui/synchronizeditemwidget.cpp index 8de6bc3cd..bd94be606 100644 --- a/src/gui/synchronizeditemwidget.cpp +++ b/src/gui/synchronizeditemwidget.cpp @@ -23,6 +23,7 @@ #include "languagechangefilter.h" #include "utility/utility.h" #include "parameterscache.h" +#include "libcommon/utility/utility.h" #include #include diff --git a/src/gui/versionwidget.cpp b/src/gui/versionwidget.cpp index eb50ee14f..8d8049fb8 100644 --- a/src/gui/versionwidget.cpp +++ b/src/gui/versionwidget.cpp @@ -26,6 +26,7 @@ #include "preferencesblocwidget.h" #include "utility/utility.h" #include "utility/widgetsignalblocker.h" +#include "libcommon/utility/utility.h" #include #include diff --git a/src/libcommon/CMakeLists.txt b/src/libcommon/CMakeLists.txt index 7225d27b3..a83af932b 100644 --- a/src/libcommon/CMakeLists.txt +++ b/src/libcommon/CMakeLists.txt @@ -2,6 +2,7 @@ project(libcommon) find_package(Qt6 REQUIRED COMPONENTS Network Widgets Gui Sql) find_package(Poco 1.13.3 REQUIRED Foundation JSON Util) +find_package(log4cplus 2.1.0 REQUIRED) find_package(sentry REQUIRED) if (UNIX AND CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") @@ -39,12 +40,13 @@ set(libcommon_SRCS info/proxyconfiginfo.h info/proxyconfiginfo.cpp log/sentry/sentryhandler.h log/sentry/sentryhandler.cpp log/sentry/sentryuser.h - log/customlogwstream.h + log/customlogstreams.h theme/theme.h theme/theme.cpp ) if(APPLE) list(APPEND libcommon_SRCS utility/utility_mac.mm) + set_property(SOURCE utility/utility_mac.mm APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") endif() # Target @@ -82,6 +84,7 @@ endif() target_link_libraries(${libcommon_NAME} Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Poco::Foundation Poco::JSON Poco::Util + log4cplus::log4cplusU sentry::sentry keychain libzip::zip diff --git a/src/libcommon/info/errorinfo.h b/src/libcommon/info/errorinfo.h index 44163e65d..5aff326b0 100644 --- a/src/libcommon/info/errorinfo.h +++ b/src/libcommon/info/errorinfo.h @@ -28,10 +28,11 @@ namespace KDC { class ErrorInfo { public: - ErrorInfo(int64_t dbId, qint64 time, ErrorLevel level, const QString &functionName, int syncDbId, const QString &workerName, - ExitCode exitCode, ExitCause exitCause, const QString &localNodeId, const QString &remoteNodeId, - NodeType nodeType, const QString &path, ConflictType conflictType, InconsistencyType inconsistencyType, - CancelType cancelType = CancelType::None, const QString &destinationPath = ""); + ErrorInfo(int64_t dbId, qint64 time, ErrorLevel level, const QString &functionName, int syncDbId, + const QString &workerName, ExitCode exitCode, ExitCause exitCause, const QString &localNodeId, + const QString &remoteNodeId, NodeType nodeType, const QString &path, ConflictType conflictType, + InconsistencyType inconsistencyType, CancelType cancelType = CancelType::None, + const QString &destinationPath = ""); ErrorInfo(qint64 time, ErrorLevel level, const QString &functionName, int syncDbId, const QString &workerName, ExitCode exitCode, ExitCause exitCause, const QString &localNodeId, const QString &remoteNodeId, NodeType nodeType, const QString &path, ConflictType conflictType, InconsistencyType inconsistencyType, diff --git a/src/libcommon/keychainmanager/keychainmanager.cpp b/src/libcommon/keychainmanager/keychainmanager.cpp index b11adeb15..644b6087a 100644 --- a/src/libcommon/keychainmanager/keychainmanager.cpp +++ b/src/libcommon/keychainmanager/keychainmanager.cpp @@ -22,8 +22,6 @@ #include -#include - #define PACKAGE "com.infomaniak.drive" #define SERVICE "desktopclient" diff --git a/src/libcommon/log/customlogstreams.h b/src/libcommon/log/customlogstreams.h new file mode 100644 index 000000000..37b635f0f --- /dev/null +++ b/src/libcommon/log/customlogstreams.h @@ -0,0 +1,161 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#include + +#include "libcommon/utility/types.h" + +class CustomLogStream : private std::stringstream { + public: + CustomLogStream() = default; + inline CustomLogStream(const CustomLogStream &str) = delete; + CustomLogStream &operator=(const CustomLogStream &) = delete; + + std::string str() const { return std::basic_stringstream::str(); } + + template + friend inline CustomLogStream &operator<<(CustomLogStream &os, C e) { + return os << KDC::toStringWithCode(e); + } + + + // We need to cast to std::stringstream as operators<<(std::stringstream, const wchar_t *str) and const std::string + // &str) are defined outside of the class std::stringstream and therefore it is not applicable to the current object + // because of the private inheritance + CustomLogStream &operator<<(const char *str) { + static_cast(*this) << str; + return *this; + } + CustomLogStream &operator<<(const std::string &str) { + static_cast(*this) << str; + return *this; + } + + CustomLogStream &operator<<(bool b) { + std::stringstream::operator<<(std::boolalpha); + std::stringstream::operator<<(b); + return *this; + } + CustomLogStream &operator<<(int i) { + std::stringstream::operator<<(i); + return *this; + } + CustomLogStream &operator<<(long i64) { + std::stringstream::operator<<(i64); + return *this; + } + CustomLogStream &operator<<(unsigned int ui) { + std::stringstream::operator<<(ui); + return *this; + } + CustomLogStream &operator<<(long long i64) { + std::stringstream::operator<<(i64); + return *this; + } + CustomLogStream &operator<<(unsigned long ul) { + std::stringstream::operator<<(ul); + return *this; + } + CustomLogStream &operator<<(unsigned long long ui64) { + std::stringstream::operator<<(ui64); + return *this; + } + CustomLogStream &operator<<(double d) { + std::stringstream::operator<<(d); + return *this; + } + CustomLogStream &operator<<(const QIODevice *ptr) { + std::stringstream::operator<<(ptr); + return *this; + } + CustomLogStream &operator<<(const std::error_code &code) { + std::stringstream::operator<<(code.value()); + return *this; + } +}; + +class CustomLogWStream : private std::wstringstream { + public: + CustomLogWStream() = default; + inline CustomLogWStream(const CustomLogWStream &wstr) = delete; + CustomLogWStream &operator=(const CustomLogWStream &) = delete; + + std::wstring str() const { return std::basic_stringstream::str(); } + + template + friend inline CustomLogWStream &operator<<(CustomLogWStream &os, C e) { + return os << KDC::typesUtility::stringToWideString(toStringWithCode(e)); + } + + // We need to cast to std::wstringstream as operators<<(std::wstringstream, const wchar_t *str and const std::wstring + // &str) are defined outside of the class std::wstringstream and therefore it is not applicable to the current object + // because of the private inheritance + CustomLogWStream &operator<<(const wchar_t *str) { + static_cast(*this) << str; + return *this; + } + CustomLogWStream &operator<<(const std::wstring &str) { + static_cast(*this) << str; + return *this; + } + + CustomLogWStream &operator<<(bool b) { + std::wstringstream::operator<<(std::boolalpha); + std::wstringstream::operator<<(b); + return *this; + } + CustomLogWStream &operator<<(int i) { + std::wstringstream::operator<<(i); + return *this; + } + CustomLogWStream &operator<<(unsigned int ui) { + std::wstringstream::operator<<(ui); + return *this; + } + CustomLogWStream &operator<<(long i64) { + std::wstringstream::operator<<(i64); + return *this; + } + CustomLogWStream &operator<<(long long i64) { + std::wstringstream::operator<<(i64); + return *this; + } + CustomLogWStream &operator<<(unsigned long ul) { + std::wstringstream::operator<<(ul); + return *this; + } + CustomLogWStream &operator<<(unsigned long long ui64) { + std::wstringstream::operator<<(ui64); + return *this; + } + CustomLogWStream &operator<<(double d) { + std::wstringstream::operator<<(d); + return *this; + } + CustomLogWStream &operator<<(const QIODevice *ptr) { + std::wstringstream::operator<<(ptr); + return *this; + } + CustomLogWStream &operator<<(const std::error_code &code) { + std::wstringstream::operator<<(code.value()); + return *this; + } +}; diff --git a/src/libcommon/log/customlogwstream.h b/src/libcommon/log/customlogwstream.h deleted file mode 100644 index bcc4d045c..000000000 --- a/src/libcommon/log/customlogwstream.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Infomaniak kDrive - Desktop - * Copyright (C) 2023-2024 Infomaniak Network SA - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "libcommon/utility/types.h" - -class CustomLogWStream : private std::wstringstream { - public: - CustomLogWStream() = default; - inline CustomLogWStream(const CustomLogWStream &wstr) = delete; - CustomLogWStream &operator=(const CustomLogWStream &) = delete; - - std::wstring str() const { return std::basic_stringstream::str(); } - - // We need to cast to std::wstringstream as operators<<(std::wstringstream, const wchar_t *str /*and const std::wstring - // &str*/) are defined outside of the class std::wstringstream and therefore it is not applicable to the current object - // because of the private inheritance - CustomLogWStream &operator<<(const wchar_t *str) { - static_cast(*this) << str; - return *this; - } - CustomLogWStream &operator<<(const std::wstring &str) { - static_cast(*this) << str; - return *this; - } - - CustomLogWStream &operator<<(bool b) { - std::wstringstream::operator<<(b); - return *this; - } - CustomLogWStream &operator<<(int i) { - std::wstringstream::operator<<(i); - return *this; - } - CustomLogWStream &operator<<(long i64) { - std::wstringstream::operator<<(i64); - return *this; - } - CustomLogWStream &operator<<(long long i64) { - std::wstringstream::operator<<(i64); - return *this; - } - CustomLogWStream &operator<<(unsigned long ul) { - std::wstringstream::operator<<(ul); - return *this; - } - CustomLogWStream &operator<<(unsigned long long ui64) { - std::wstringstream::operator<<(ui64); - return *this; - } - CustomLogWStream &operator<<(double d) { - std::wstringstream::operator<<(d); - return *this; - } - CustomLogWStream &operator<<(const QIODevice *ptr) { - std::wstringstream::operator<<(ptr); - return *this; - } -}; diff --git a/src/libcommon/log/sentry/sentryhandler.h b/src/libcommon/log/sentry/sentryhandler.h index 912515c23..7f50dad3f 100644 --- a/src/libcommon/log/sentry/sentryhandler.h +++ b/src/libcommon/log/sentry/sentryhandler.h @@ -22,7 +22,7 @@ #include #include -#include "utility/types.h" +#include "libcommon/utility/types.h" #include "sentryuser.h" namespace KDC { diff --git a/src/libcommon/utility/types.cpp b/src/libcommon/utility/types.cpp index 233421b87..73c3697ea 100644 --- a/src/libcommon/utility/types.cpp +++ b/src/libcommon/utility/types.cpp @@ -70,6 +70,8 @@ std::string toString(const OperationType e) { return "Delete"; case OperationType::Rights: return "Rights"; + case OperationType::MoveOut: + return "MoveOut"; default: return noConversionStr; } @@ -201,6 +203,10 @@ std::string toString(const ExitCause e) { } } +std::string toString(const ExitInfo e) { + return static_cast(e); +} + std::string toString(const ConflictType e) { switch (e) { case ConflictType::None: diff --git a/src/libcommon/utility/types.h b/src/libcommon/utility/types.h index 5c4fc95c8..62de2a29f 100644 --- a/src/libcommon/utility/types.h +++ b/src/libcommon/utility/types.h @@ -28,7 +28,6 @@ #include #include #include -#include "libcommon/log/customlogwstream.h" namespace KDC { @@ -118,11 +117,14 @@ concept EnumClass = std::is_enum_v; template // Any enum class that can be converted to (and from) int concept IntegralEnum = EnumClass && std::is_convertible_v, int>; -template // Any enum class that can be printed (with enumClassToString) -concept PrintableEnum = EnumClass && requires(C e) { toString(e); }; +template // Any types that can be converted to an int +concept ConvertibleToInt = requires(C e) { static_cast(e); }; + +template // Any types that can be converted to string +concept LogableType = requires(C e) { toString(e); }; // Converters -template +template inline constexpr int toInt(C e) { return static_cast(e); } @@ -168,7 +170,7 @@ enum class NodeType { }; std::string toString(NodeType e); -enum class OperationType { None = 0x00, Create = 0x01, Move = 0x02, Edit = 0x04, Delete = 0x08, Rights = 0x10 }; +enum class OperationType { None = 0x00, Create = 0x01, Move = 0x02, Edit = 0x04, Delete = 0x08, Rights = 0x10, MoveOut = 0x20 }; std::string toString(OperationType e); enum class ExitCode { @@ -246,6 +248,7 @@ struct ExitInfo { const ExitCause &cause() const { return _cause; } operator ExitCode() const { return _code; } operator ExitCause() const { return _cause; } + explicit operator std::string() const { return "ExitInfo{" + toString(code()) + ", " + toString(cause()) + "}"; } constexpr operator bool() const { return _code == ExitCode::Ok; } constexpr explicit operator int() const { return toInt(_code) * 100 + toInt(_cause); } constexpr bool operator==(const ExitInfo &other) const { return _code == other._code && _cause == other._cause; } @@ -254,6 +257,7 @@ struct ExitInfo { ExitCode _code{ExitCode::Unknown}; ExitCause _cause{ExitCause::Unknown}; }; +std::string toString(ExitInfo e); // Conflict types ordered by priority enum class ConflictType { @@ -417,6 +421,7 @@ enum class IoError { MaxDepthExceeded, NoSuchFileOrDirectory, ResultOutOfRange, + CrossDeviceLink, Unknown }; std::string toString(IoError e); @@ -589,27 +594,22 @@ std::wstring stringToWideString(const std::string &str); // Convert string to ws // Stream Operator (toString) static const std::string noConversionStr("No conversion to string available"); -template +template std::string toStringWithCode(C e) { return toString(e) + "(" + std::to_string(toInt(e)) + ")"; // Example: "Ok(1)" } -template +template inline std::wostream &operator<<(std::wostream &wos, C e) { return wos << typesUtility::stringToWideString(toStringWithCode(e)); } -template +template inline std::ostream &operator<<(std::ostream &os, C e) { return os << toStringWithCode(e); } -template -inline CustomLogWStream &operator<<(CustomLogWStream &os, C e) { - return os << typesUtility::stringToWideString(toStringWithCode(e)); -} - -template +template inline QDebug &operator<<(QDebug &os, C e) { return os << toStringWithCode(e).c_str(); } diff --git a/src/libcommon/utility/utility.cpp b/src/libcommon/utility/utility.cpp index 4ef957f0e..d6d0fee3a 100644 --- a/src/libcommon/utility/utility.cpp +++ b/src/libcommon/utility/utility.cpp @@ -37,8 +37,6 @@ #include #endif -#include - #ifdef ZLIB_FOUND #include #endif @@ -68,7 +66,10 @@ #define MAX_PATH_LENGTH_MAC 1023 #define MAX_PATH_LENGTH_LINUX 4096 -#define LITE_SYNC_EXT_BUNDLE_ID "com.infomaniak.drive.desktopclient.LiteSyncExt" +#ifdef __APPLE__ +constexpr char liteSyncExtBundleIdStr[] = "com.infomaniak.drive.desktopclient.LiteSyncExt"; +constexpr char loginItemAgentIdStr[] = "864VDCS2QY.com.infomaniak.drive.desktopclient.LoginItemAgent"; +#endif namespace KDC { const int CommonUtility::logsPurgeRate = 7; // days @@ -120,19 +121,6 @@ std::string CommonUtility::generateRandomStringPKCE(const int length /*= 10*/) { return generateRandomString(charArray, distrib, length); } -std::string CommonUtility::generateAppId(const int length /*= 10*/) { - // The back only accept characters from `A` to `F` - static constexpr char charArray[] = - "0123456789" - "ABCDEF" - "abcdef"; - - static std::uniform_int_distribution distrib( - 0, sizeof(charArray) - 2); // -2 in order to avoid the null terminating character - - return generateRandomString(charArray, distrib, length); -} - void CommonUtility::crash() { volatile int *a = (int *) (NULL); *a = 1; @@ -797,6 +785,16 @@ bool CommonUtility::fileNameIsValid(const SyncName &name) { return true; } +#ifdef __APPLE__ +const std::string CommonUtility::loginItemAgentId() { + return loginItemAgentIdStr; +} + +const std::string CommonUtility::liteSyncExtBundleId() { + return liteSyncExtBundleIdStr; +} +#endif + std::string CommonUtility::envVarValue(const std::string &name) { bool isSet = false; return envVarValue(name, isSet); @@ -888,7 +886,7 @@ bool CommonUtility::isLiteSyncExtEnabled() { process->start( "bash", QStringList() << "-c" - << QString("systemextensionsctl list | grep %1 | grep enabled | wc -l").arg(LITE_SYNC_EXT_BUNDLE_ID)); + << QString("systemextensionsctl list | grep %1 | grep enabled | wc -l").arg(liteSyncExtBundleIdStr)); process->waitForStarted(); process->waitForFinished(); QByteArray result = process->readAll(); @@ -909,14 +907,14 @@ bool CommonUtility::isLiteSyncExtFullDiskAccessAuthOk(std::string &errorDescr) { " and client = \"%2\"" " and client_type = 0") .arg(serviceStr) - .arg(LITE_SYNC_EXT_BUNDLE_ID)); + .arg(liteSyncExtBundleIdStr)); } else { query.prepare(QString("SELECT auth_value FROM access" " WHERE service = \"%1\"" " and client = \"%2\"" " and client_type = 0") .arg(serviceStr) - .arg(LITE_SYNC_EXT_BUNDLE_ID)); + .arg(liteSyncExtBundleIdStr)); } query.exec(); @@ -945,5 +943,6 @@ bool CommonUtility::isLiteSyncExtFullDiskAccessAuthOk(std::string &errorDescr) { return false; } + #endif } // namespace KDC diff --git a/src/libcommon/utility/utility.h b/src/libcommon/utility/utility.h index 37960c89e..5e45f8c85 100644 --- a/src/libcommon/utility/utility.h +++ b/src/libcommon/utility/utility.h @@ -23,15 +23,19 @@ #include "libcommon/info/nodeinfo.h" #include +#include + +#ifdef _WIN32 +#include +#endif #include #include #include #include +#include -#ifdef _WIN32 -#include -#endif +#include namespace KDC { struct COMMON_EXPORT CommonUtility { @@ -50,7 +54,6 @@ struct COMMON_EXPORT CommonUtility { static std::string generateRandomStringAlphaNum(int length = 10); static std::string generateRandomStringPKCE(int length = 10); - static std::string generateAppId(int length = 10); static QString fileSystemName(const QString &dirPath); @@ -105,7 +108,9 @@ struct COMMON_EXPORT CommonUtility { static bool dirNameIsValid(const SyncName &name); static bool fileNameIsValid(const SyncName &name); -#ifdef Q_OS_MAC +#ifdef __APPLE__ + static const std::string loginItemAgentId(); + static const std::string liteSyncExtBundleId(); static bool isLiteSyncExtEnabled(); static bool isLiteSyncExtFullDiskAccessAuthOk(std::string &errorDescr); #endif @@ -121,6 +126,24 @@ struct COMMON_EXPORT CommonUtility { static void extractIntFromStrVersion(const std::string &version, std::vector &tabVersion); }; +struct COMMON_EXPORT StdLoggingThread : public std::thread { + template + explicit StdLoggingThread(void (*runFct)(Args...), Args &&...args) : + std::thread( + [=]() { + runFct(args...); + log4cplus::threadCleanup(); + }, + args...) {} +}; + +struct COMMON_EXPORT QtLoggingThread : public QThread { + void run() override { + QThread::run(); + log4cplus::threadCleanup(); + } +}; + struct ArgsReader { template explicit ArgsReader(Args... args) : stream(¶ms, QIODevice::WriteOnly) { diff --git a/src/libcommongui/CMakeLists.txt b/src/libcommongui/CMakeLists.txt index 3230583ed..ab51cdc9e 100644 --- a/src/libcommongui/CMakeLists.txt +++ b/src/libcommongui/CMakeLists.txt @@ -25,5 +25,6 @@ set_target_properties(${libcommongui_NAME} PROPERTIES ) target_link_libraries(${libcommongui_NAME} + ${libcommon_NAME} Qt6::Core ) diff --git a/src/libcommongui/commclient.cpp b/src/libcommongui/commclient.cpp index d221834e1..e3fc6b543 100644 --- a/src/libcommongui/commclient.cpp +++ b/src/libcommongui/commclient.cpp @@ -47,7 +47,7 @@ std::shared_ptr CommClient::instance(QObject *parent) { } CommClient::CommClient(QObject *parent) : - QObject(parent), _requestWorkerThread(new QThread()), _requestWorker(new Worker()), _tcpConnection(new QTcpSocket()), + QObject(parent), _requestWorkerThread(new QtLoggingThread()), _requestWorker(new Worker()), _tcpConnection(new QTcpSocket()), _buffer(QByteArray()) { // Start worker thread _requestWorker->moveToThread(_requestWorkerThread); diff --git a/src/libcommongui/commclient.h b/src/libcommongui/commclient.h index 98c0fbebe..b935f26e4 100644 --- a/src/libcommongui/commclient.h +++ b/src/libcommongui/commclient.h @@ -19,6 +19,7 @@ #pragma once #include "libcommon/comm.h" +#include "libcommon/utility/utility.h" #include @@ -29,7 +30,6 @@ #include #include #include -#include #include namespace KDC { @@ -61,7 +61,7 @@ class CommClient : public QObject { private: static std::shared_ptr _instance; - QThread *_requestWorkerThread; + QtLoggingThread *_requestWorkerThread; Worker *_requestWorker; QTcpSocket *_tcpConnection; QByteArray _buffer; diff --git a/src/libcommongui/utility/utility.cpp b/src/libcommongui/utility/utility.cpp index 17b3990b3..00ac15fd7 100644 --- a/src/libcommongui/utility/utility.cpp +++ b/src/libcommongui/utility/utility.cpp @@ -18,8 +18,6 @@ #include "utility.h" -#include - #if defined(Q_OS_WIN) #include "utility_win.cpp" #elif defined(Q_OS_MAC) @@ -28,6 +26,9 @@ #include "utility_linux.cpp" #endif +#include +#include + namespace KDC { struct Period { const char *name; diff --git a/src/libcommongui/utility/utility.h b/src/libcommongui/utility/utility.h index 139d4306c..c7a20986d 100644 --- a/src/libcommongui/utility/utility.h +++ b/src/libcommongui/utility/utility.h @@ -23,7 +23,6 @@ #include "libcommon/info/nodeinfo.h" #include -#include #include static const QString dirSeparator = "/"; diff --git a/src/libcommonserver/CMakeLists.txt b/src/libcommonserver/CMakeLists.txt index cf16ab3aa..2ffa385b4 100644 --- a/src/libcommonserver/CMakeLists.txt +++ b/src/libcommonserver/CMakeLists.txt @@ -3,7 +3,6 @@ project(libcommonserver) find_package(SQLite3 3.8.0 REQUIRED) find_package(Poco 1.13.3 REQUIRED Foundation Net JSON Util) find_package(OpenSSL 3.1.0 REQUIRED SSL Crypto) -find_package(log4cplus 2.1.0 REQUIRED) find_package(xxHash 0.8.2 REQUIRED) if (UNIX AND CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") @@ -39,7 +38,10 @@ set(libcommonserver_SRCS if(APPLE) list(APPEND libcommonserver_SRCS io/iohelper_mac.mm io/iohelper_mac.cpp) + set_property(SOURCE io/iohelper_mac.mm APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") + list(APPEND libcommonserver_SRCS utility/utility_mac.mm) + set_property(SOURCE utility/utility_mac.mm APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") elseif(WIN32) list(APPEND libcommonserver_SRCS io/iohelper_win.h io/iohelper_win.cpp) else() @@ -70,7 +72,6 @@ if (WIN32) target_include_directories(${libcommonserver_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - "C:/Program Files (x86)/log4cplus/include" "C:/Program Files (x86)/libzip/include" "C:/Program Files (x86)/xxHash/include" ) @@ -99,7 +100,6 @@ target_link_libraries(${libcommonserver_NAME} if (WIN32) target_link_libraries(${libcommonserver_NAME} - log4cplus::log4cplusU xxHash::xxhash Secur32) elseif(APPLE) @@ -109,7 +109,6 @@ elseif(APPLE) find_library(APPKIT_LIBRARY NAMES AppKit) target_link_libraries(${libcommonserver_NAME} - log4cplus::log4cplusU xxHash::xxhash ${FOUNDATION_LIBRARY} ${CORESERVICES_LIBRARY} @@ -117,7 +116,6 @@ elseif(APPLE) utf8proc) else() target_link_libraries(${libcommonserver_NAME} - "/usr/local/lib/liblog4cplusU.so" "/usr/local/lib/libxxhash.so" utf8proc) endif() diff --git a/src/libcommonserver/commserver.cpp b/src/libcommonserver/commserver.cpp index 6dbef9f91..c30a21dc7 100644 --- a/src/libcommonserver/commserver.cpp +++ b/src/libcommonserver/commserver.cpp @@ -20,7 +20,7 @@ #include "common/utility.h" #include "libcommon/comm.h" #include "libcommonserver/log/log.h" -#include "libcommon/utility/utility.h" +#include "libcommonserver/utility/utility.h" #include #include @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -52,7 +51,7 @@ std::shared_ptr CommServer::instance(QObject *parent) { } CommServer::CommServer(QObject *parent) : - QObject(parent), _requestWorkerThread(new QThread()), _requestWorker(new Worker()), _tcpSocket(nullptr), + QObject(parent), _requestWorkerThread(new QtLoggingThread()), _requestWorker(new Worker()), _tcpSocket(nullptr), _buffer(QByteArray()), _hasQuittedProperly(false) { // Start worker thread _requestWorker->moveToThread(_requestWorkerThread); diff --git a/src/libcommonserver/commserver.h b/src/libcommonserver/commserver.h index 75a6d84d1..fff7b3e23 100644 --- a/src/libcommonserver/commserver.h +++ b/src/libcommonserver/commserver.h @@ -19,6 +19,7 @@ #pragma once #include "libcommon/comm.h" +#include "libcommon/utility/utility.h" #include @@ -59,7 +60,7 @@ class CommServer : public QObject { private: static std::shared_ptr _instance; - QThread *_requestWorkerThread; + QtLoggingThread *_requestWorkerThread; Worker *_requestWorker; QTcpServer _tcpServer; QTcpSocket *_tcpSocket; diff --git a/src/libcommonserver/db/db.cpp b/src/libcommonserver/db/db.cpp index 7bb749eec..4152ae186 100644 --- a/src/libcommonserver/db/db.cpp +++ b/src/libcommonserver/db/db.cpp @@ -376,7 +376,7 @@ bool Db::init(const std::string &version) { queryFree(CREATE_VERSION_TABLE_ID); // Insert version - LOG_DEBUG(_logger, "Insert version " << version.c_str()); + LOG_DEBUG(_logger, "Insert version " << version); if (!prepareQuery(INSERT_VERSION_REQUEST_ID, INSERT_VERSION_REQUEST)) return false; if (!insertVersion(version)) { LOG_WARN(_logger, "Error in Db::insertVersion"); @@ -386,7 +386,7 @@ bool Db::init(const std::string &version) { queryFree(INSERT_VERSION_REQUEST_ID); // Create DB - LOG_INFO(_logger, "Create " << dbType().c_str() << " DB"); + LOG_INFO(_logger, "Create " << dbType() << " DB"); if (bool retry = false; !create(retry)) { if (retry) { LOG_WARN(_logger, "Error in Db::create - Retry"); @@ -413,7 +413,7 @@ bool Db::init(const std::string &version) { void Db::startTransaction() { if (!_transaction) { if (!_sqliteDb->startTransaction()) { - LOG_WARN(_logger, "ERROR starting transaction: " << _sqliteDb->error().c_str()); + LOG_WARN(_logger, "ERROR starting transaction: " << _sqliteDb->error()); return; } _transaction = true; diff --git a/src/libcommonserver/io/iohelper.cpp b/src/libcommonserver/io/iohelper.cpp index 64b4b9e6f..ea6a5a806 100644 --- a/src/libcommonserver/io/iohelper.cpp +++ b/src/libcommonserver/io/iohelper.cpp @@ -19,7 +19,6 @@ #include "libcommonserver/io/filestat.h" #include "libcommonserver/io/iohelper.h" #include "libcommonserver/utility/utility.h" // Path2WStr -#include "libcommon/utility/utility.h" #include "config.h" // APPLICATION @@ -29,7 +28,7 @@ #if defined(__APPLE__) || defined(__unix__) #include #endif - +#include #include // LOGW_WARN namespace KDC { @@ -39,6 +38,8 @@ std::function IoHelper::_isDire static_cast(&std::filesystem::is_directory); std::function IoHelper::_isSymlink = static_cast(&std::filesystem::is_symlink); +std::function IoHelper::_rename = + static_cast(std::filesystem::rename); std::function IoHelper::_readSymlink = static_cast(&std::filesystem::read_symlink); std::function IoHelper::_fileSize = @@ -72,7 +73,10 @@ IoError IoHelper::stdError2ioError(int error) noexcept { case static_cast(std::errc::no_space_on_device): return IoError::DiskFull; case static_cast(std::errc::permission_denied): + case static_cast(std::errc::operation_not_permitted): return IoError::AccessDenied; + case static_cast(std::errc::cross_device_link): + return IoError::CrossDeviceLink; default: return IoError::Unknown; } @@ -150,6 +154,58 @@ std::string IoHelper::ioError2StdString(IoError ioError) noexcept { } } +bool IoHelper::openFile(const SyncPath &path, std::ifstream &file, IoError &ioError, int timeOut /*in seconds*/) { + int count = 0; + if (file.is_open()) file.close(); + do { + file.open(path.native(), std::ifstream::binary); + if (!file.is_open()) { + bool exists = false; + if (!IoHelper::checkIfPathExists(path, exists, ioError)) { + LOGW_WARN(logger(), L"Error in IoHelper::checkIfPathExists: " << Utility::formatIoError(path, ioError)); + return isExpectedError(ioError); + } + if (ioError == IoError::AccessDenied) { + LOGW_DEBUG(logger(), L"Access denied to " << Utility::formatSyncPath(path)); + return isExpectedError(ioError); + } + if (!exists) { + LOGW_DEBUG(logger(), L"Item does not exist anymore - " << Utility::formatSyncPath(path)); + ioError = IoError::NoSuchFileOrDirectory; + return isExpectedError(ioError); + } + LOGW_DEBUG(logger(), L"File is locked, retrying in one second " << Utility::formatSyncPath(path)); + + if (count < timeOut) Utility::msleep(1000); + } + } while (++count < timeOut && !file.is_open()); + + if (!file.is_open()) { + LOGW_DEBUG(logger(), L"Failed to open file - " << Utility::formatSyncPath(path)); + ioError = IoError::AccessDenied; + return isExpectedError(ioError); + } + + ioError = IoError::Success; + return true; +} + +ExitInfo IoHelper::openFile(const SyncPath &path, std::ifstream &file, int timeOut /*in seconds*/) { + IoError ioError = IoError::Success; + openFile(path, file, ioError, timeOut); + switch (ioError) { + case IoError::Success: + return ExitCode::Ok; + case IoError::AccessDenied: + return ExitInfo{ExitCode::SystemError, ExitCause::FileAccessError}; + case IoError::NoSuchFileOrDirectory: + return ExitInfo{ExitCode::SystemError, ExitCause::NotFound}; + default: + LOGW_WARN(logger(), L"Unexpected read error for " << Utility::formatIoError(path, ioError)); + return ExitCode::SystemError; + } +} + bool IoHelper::isExpectedError(IoError ioError) noexcept { return (ioError == IoError::NoSuchFileOrDirectory) || (ioError == IoError::AccessDenied); } @@ -791,7 +847,7 @@ bool IoHelper::moveItem(const SyncPath &sourcePath, const SyncPath &destinationP bool IoHelper::renameItem(const SyncPath &sourcePath, const SyncPath &destinationPath, IoError &ioError) noexcept { std::error_code ec; - std::filesystem::rename(sourcePath, destinationPath, ec); + _rename(sourcePath, destinationPath, ec); ioError = stdError2ioError(ec); return ioError == IoError::Success; } diff --git a/src/libcommonserver/io/iohelper.h b/src/libcommonserver/io/iohelper.h index 5dabcad4f..3d818660d 100644 --- a/src/libcommonserver/io/iohelper.h +++ b/src/libcommonserver/io/iohelper.h @@ -333,6 +333,21 @@ struct IoHelper { */ static bool setXAttrValue(const SyncPath &path, const std::string &attrName, const std::string &value, IoError &ioError) noexcept; + //! Remove the extended attributes with specified names for the item indicated by path. + /*! + \param path is the file system path of the item. + \param attrNames is a vector of the names of the extended attributes to remove. + \param ioError holds the error returned when an underlying OS API call fails. + \return true if no unexpected error occurred, false otherwise. + */ + static bool removeXAttrs(const SyncPath &path, const std::vector &attrNames, IoError &ioError) noexcept; + //! Remove the LiteSync extended attributes for the item indicated by path. + /*! + \param path is the file system path of the item. + \param ioError holds the error returned when an underlying OS API call fails. + \return true if no unexpected error occurred, false otherwise. + */ + static bool removeLiteSyncXAttrs(const SyncPath &path, IoError &ioError) noexcept; #endif #ifdef _WIN32 @@ -393,6 +408,9 @@ struct IoHelper { // The most common and expected errors during IO operations static bool isExpectedError(IoError ioError) noexcept; + static bool openFile(const SyncPath &path, std::ifstream &file, IoError &ioError, int timeOut = 10 /*in seconds*/); + static ExitInfo openFile(const SyncPath &path, std::ifstream &file, int timeOut = 10 /*in seconds*/); + protected: friend class DirectoryIterator; friend class TestIo; @@ -401,10 +419,10 @@ struct IoHelper { // They can be modified in tests. static std::function _isDirectory; static std::function _isSymlink; + static std::function _rename; static std::function _readSymlink; static std::function _fileSize; static std::function _tempDirectoryPath; - #ifdef __APPLE__ // Can be modified in tests. static std::function _readAlias; diff --git a/src/libcommonserver/io/iohelper_mac.cpp b/src/libcommonserver/io/iohelper_mac.cpp index 84a37a9ce..b93c1bac7 100644 --- a/src/libcommonserver/io/iohelper_mac.cpp +++ b/src/libcommonserver/io/iohelper_mac.cpp @@ -30,6 +30,9 @@ namespace KDC { +static constexpr std::string_view EXT_ATTR_STATUS("com.infomaniak.drive.desktopclient.litesync.status"); +static constexpr std::string_view EXT_ATTR_PIN_STATE("com.infomaniak.drive.desktopclient.litesync.pinstate"); + namespace { inline bool _isXAttrValueExpectedError(IoError error) { return (error == IoError::NoSuchFileOrDirectory) || (error == IoError::AttrNotFound) || (error == IoError::AccessDenied); @@ -40,7 +43,7 @@ bool IoHelper::getXAttrValue(const SyncPath &path, const std::string &attrName, value = ""; ItemType itemType; if (!getItemType(path, itemType)) { - LOGW_WARN(logger(), L"Error in IoHelper::getItemType for " << Utility::formatIoError(path, itemType.ioError).c_str()); + LOGW_WARN(logger(), L"Error in IoHelper::getItemType for " << Utility::formatIoError(path, itemType.ioError)); ioError = itemType.ioError; return false; } @@ -82,7 +85,7 @@ bool IoHelper::setXAttrValue(const SyncPath &path, const std::string &attrName, IoError &ioError) noexcept { ItemType itemType; if (!getItemType(path, itemType)) { - LOGW_WARN(logger(), L"Error in IoHelper::getItemType for " << Utility::formatIoError(path, itemType.ioError).c_str()); + LOGW_WARN(logger(), L"Error in IoHelper::getItemType for " << Utility::formatIoError(path, itemType.ioError)); ioError = itemType.ioError; return false; } @@ -105,14 +108,30 @@ bool IoHelper::setXAttrValue(const SyncPath &path, const std::string &attrName, return true; } +bool IoHelper::removeXAttrs(const SyncPath &path, const std::vector &attrNames, IoError &ioError) noexcept { + for (const auto &attrName: attrNames) { + if (removexattr(path.native().c_str(), attrName.c_str(), XATTR_NOFOLLOW) == -1) { + ioError = posixError2ioError(errno); + return _isXAttrValueExpectedError(ioError); + } + } + + // XAttr has been removed + ioError = IoError::Success; + return true; +} + +bool IoHelper::removeLiteSyncXAttrs(const SyncPath &path, IoError &ioError) noexcept { + const std::vector liteSyncAttrName = {std::string(EXT_ATTR_STATUS), std::string(EXT_ATTR_PIN_STATE)}; + return removeXAttrs(path, liteSyncAttrName, ioError); +} + bool IoHelper::checkIfFileIsDehydrated(const SyncPath &itemPath, bool &isDehydrated, IoError &ioError) noexcept { isDehydrated = false; ioError = IoError::Success; - static const std::string EXT_ATTR_STATUS = "com.infomaniak.drive.desktopclient.litesync.status"; - std::string value; - const bool result = IoHelper::getXAttrValue(itemPath.native(), EXT_ATTR_STATUS, value, ioError); + const bool result = IoHelper::getXAttrValue(itemPath.native(), std::string(EXT_ATTR_STATUS), value, ioError); if (!result) { return false; } diff --git a/src/libcommonserver/io/iohelper_mac.mm b/src/libcommonserver/io/iohelper_mac.mm index c12aa7618..b6ed2dc44 100644 --- a/src/libcommonserver/io/iohelper_mac.mm +++ b/src/libcommonserver/io/iohelper_mac.mm @@ -104,7 +104,7 @@ IoError nsError2ioError(NSError *nsError) noexcept { CFRelease(aliasUrl); if (!ret) { if (error) { - ioError = nsError2ioError((NSError *)error); + ioError = nsError2ioError((__bridge NSError *) error); CFRelease(error); if (ioError != IoError::Unknown) { return true; @@ -136,7 +136,7 @@ IoError nsError2ioError(NSError *nsError) noexcept { CFRelease(aliasUrl); if (bookmarkRef == nil) { if (error) { - ioError = nsError2ioError((NSError *)error); + ioError = nsError2ioError((__bridge NSError *) error); CFRelease(error); if (ioError != IoError::Unknown) { return true; @@ -170,7 +170,7 @@ IoError nsError2ioError(NSError *nsError) noexcept { CFStringRef targetPathStr = CFURLCopyFileSystemPath(targetUrl, kCFURLPOSIXPathStyle); CFRelease(targetUrl); - targetPath = SyncPath(std::string([(NSString *)targetPathStr UTF8String])); + targetPath = SyncPath(std::string([(__bridge NSString *) targetPathStr UTF8String])); CFRelease(targetPathStr); return true; @@ -194,7 +194,7 @@ IoError nsError2ioError(NSError *nsError) noexcept { if (bookmarkRef == nil) { if (error) { - ioError = nsError2ioError((NSError *)error); + ioError = nsError2ioError((__bridge NSError *) error); CFRelease(error); } CFRelease(aliasUrl); @@ -212,7 +212,7 @@ IoError nsError2ioError(NSError *nsError) noexcept { if (!result) { if (error) { - ioError = nsError2ioError((NSError *)error); + ioError = nsError2ioError((__bridge NSError *) error); CFRelease(error); } LOGW_WARN(logger(), L"Error in CFURLWriteBookmarkDataToFile: " << Utility::formatSyncPath(aliasPath).c_str()); diff --git a/src/libcommonserver/io/iohelper_win.cpp b/src/libcommonserver/io/iohelper_win.cpp index a87d2c1f2..687b83e57 100644 --- a/src/libcommonserver/io/iohelper_win.cpp +++ b/src/libcommonserver/io/iohelper_win.cpp @@ -54,6 +54,8 @@ IoError dWordError2ioError(DWORD error, log4cplus::Logger logger) noexcept { case ERROR_SUCCESS: return IoError::Success; case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: return IoError::AccessDenied; case ERROR_DISK_FULL: return IoError::DiskFull; @@ -68,6 +70,8 @@ IoError dWordError2ioError(DWORD error, log4cplus::Logger logger) noexcept { case ERROR_PATH_NOT_FOUND: case ERROR_INVALID_NAME: return IoError::NoSuchFileOrDirectory; + case ERROR_NOT_SAME_DEVICE: + return IoError::CrossDeviceLink; default: if (Log::isSet()) { LOG_WARN(logger, "Unhandled DWORD error - error=" << error); diff --git a/src/libcommonserver/log/customrollingfileappender.cpp b/src/libcommonserver/log/customrollingfileappender.cpp index 573600b1c..73fcd2804 100644 --- a/src/libcommonserver/log/customrollingfileappender.cpp +++ b/src/libcommonserver/log/customrollingfileappender.cpp @@ -303,8 +303,8 @@ void CustomRollingFileAppender::checkForExpiredFiles() { // Delete expired files if (_expire > 0 && entry.path().string().find(APPLICATION_NAME) != std::string::npos) { const auto now = std::chrono::system_clock::now(); - auto lastModified = std::chrono::system_clock::from_time_t(fileStat.modtime); - auto expireDateTime = lastModified + std::chrono::seconds(_expire); + const auto lastModified = std::chrono::system_clock::from_time_t(fileStat.modtime); // Only 1s precision. + const auto expireDateTime = lastModified + std::chrono::seconds(_expire); if (expireDateTime < now) { log4cplus::file_remove(Utility::s2ws(entry.path().string())); continue; diff --git a/src/libcommonserver/log/log.h b/src/libcommonserver/log/log.h index f8502d7b6..cf85f5aee 100644 --- a/src/libcommonserver/log/log.h +++ b/src/libcommonserver/log/log.h @@ -24,132 +24,194 @@ #include #include -#include #include "libcommon/log/sentry/sentryhandler.h" -#include "libcommon/log/customlogwstream.h" +#include "libcommon/log/customlogstreams.h" +#include "libcommon/utility/types.h" namespace KDC { #ifdef NDEBUG -#define LOG_DEBUG(logger, logEvent) \ - { \ - std::ostringstream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, stream.str().c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("debug")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_DEBUG(logger, logEvent) - -#define LOGW_DEBUG(logger, logEvent) \ - { \ - CustomLogWStream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(stream.str()).c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("debug")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_DEBUG(logger, logEvent) - -#define LOG_INFO(logger, logEvent) \ - { \ - std::ostringstream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, stream.str().c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("info")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_INFO(logger, logEvent) - -#define LOGW_INFO(logger, logEvent) \ - { \ - CustomLogWStream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(stream.str()).c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("info")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_INFO(logger, logEvent) - -#define LOG_WARN(logger, logEvent) \ - { \ - std::ostringstream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, stream.str().c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("warning")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_WARN(logger, logEvent) - -#define LOGW_WARN(logger, logEvent) \ - { \ - CustomLogWStream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(stream.str()).c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("warning")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_WARN(logger, logEvent) - -#define LOG_ERROR(logger, logEvent) \ - { \ - std::ostringstream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, stream.str().c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_ERROR(logger, logEvent) - -#define LOGW_ERROR(logger, logEvent) \ - { \ - CustomLogWStream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(stream.str()).c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_ERROR(logger, logEvent) - -#define LOG_FATAL(logger, logEvent) \ - { \ - std::ostringstream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, stream.str().c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("fatal")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_FATAL(logger, logEvent) - -#define LOGW_FATAL(logger, logEvent) \ - { \ - CustomLogWStream stream; \ - stream << logEvent; \ - sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(stream.str()).c_str()); \ - sentry_value_set_by_key(crumb, "level", sentry_value_new_string("fatal")); \ - sentry_add_breadcrumb(crumb); \ - } \ - LOG4CPLUS_FATAL(logger, logEvent) +#define LOG_DEBUG(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + const auto &customLogStreamStr_ = customLogStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, customLogStreamStr_.c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("debug")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_DEBUG(logger, customLogStreamStr_.c_str()); \ + } + +#define LOGW_DEBUG(logger, logEvent) \ + { \ + CustomLogWStream customLogWStream_; \ + customLogWStream_ << logEvent; \ + const auto &customLogWStreamStr_ = customLogWStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(customLogWStreamStr_).c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("debug")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_DEBUG(logger, customLogWStreamStr_.c_str()); \ + } + +#define LOG_INFO(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + const auto customLogStreamStr_ = customLogStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, customLogStreamStr_.c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("info")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_INFO(logger, customLogStreamStr_.c_str()); \ + } + +#define LOGW_INFO(logger, logEvent) \ + { \ + CustomLogWStream customLogWStream_; \ + customLogWStream_ << logEvent; \ + const auto &customLogWStreamStr_ = customLogWStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(customLogWStreamStr_).c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("info")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_INFO(logger, customLogWStreamStr_.c_str()); \ + } + +#define LOG_WARN(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + const auto customLogStreamStr_ = customLogStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, customLogStreamStr_.c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("warning")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_WARN(logger, customLogStreamStr_.c_str()); \ + } + + +#define LOGW_WARN(logger, logEvent) \ + { \ + CustomLogWStream customLogWStream_; \ + customLogWStream_ << logEvent; \ + const auto &customLogWStreamStr_ = customLogWStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(customLogWStreamStr_).c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("warning")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_WARN(logger, customLogWStreamStr_.c_str()); \ + } + +#define LOG_ERROR(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + const auto customLogStreamStr_ = customLogStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, customLogStreamStr_.c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_ERROR(logger, customLogStreamStr_.c_str()); \ + } + +#define LOGW_ERROR(logger, logEvent) \ + { \ + CustomLogWStream customLogWStream_; \ + customLogWStream_ << logEvent; \ + const auto &customLogWStreamStr_ = customLogWStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(customLogWStreamStr_).c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_ERROR(logger, customLogWStreamStr_.c_str()); \ + } + +#define LOG_FATAL(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + const auto customLogStreamStr_ = customLogStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, customLogStreamStr_.c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("fatal")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_FATAL(logger, customLogStreamStr_.c_str()); \ + } + +#define LOGW_FATAL(logger, logEvent) \ + { \ + CustomLogWStream customLogWStream_; \ + customLogWStream_ << logEvent; \ + const auto &customLogWStreamStr_ = customLogWStream_.str(); \ + sentry_value_t crumb = sentry_value_new_breadcrumb(nullptr, Utility::ws2s(customLogWStreamStr_).c_str()); \ + sentry_value_set_by_key(crumb, "level", sentry_value_new_string("fatal")); \ + sentry_add_breadcrumb(crumb); \ + LOG4CPLUS_FATAL(logger, customLogWStreamStr_.c_str()); \ + } #else -#define LOG_DEBUG(logger, logEvent) LOG4CPLUS_DEBUG(logger, logEvent) - -#define LOGW_DEBUG(logger, logEvent) LOG4CPLUS_DEBUG(logger, logEvent) - -#define LOG_INFO(logger, logEvent) LOG4CPLUS_INFO(logger, logEvent) - -#define LOGW_INFO(logger, logEvent) LOG4CPLUS_INFO(logger, logEvent) - -#define LOG_WARN(logger, logEvent) LOG4CPLUS_WARN(logger, logEvent) - -#define LOGW_WARN(logger, logEvent) LOG4CPLUS_WARN(logger, logEvent) - -#define LOG_ERROR(logger, logEvent) LOG4CPLUS_ERROR(logger, logEvent) - -#define LOGW_ERROR(logger, logEvent) LOG4CPLUS_ERROR(logger, logEvent) - -#define LOG_FATAL(logger, logEvent) LOG4CPLUS_FATAL(logger, logEvent) +#define LOG_DEBUG(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + LOG4CPLUS_DEBUG(logger, customLogStream_.str().c_str()); \ + } + +#define LOGW_DEBUG(logger, logEvent) \ + { \ + CustomLogWStream customLogWstream_; \ + customLogWstream_ << logEvent; \ + LOG4CPLUS_DEBUG(logger, customLogWstream_.str().c_str()); \ + } + +#define LOG_INFO(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + LOG4CPLUS_INFO(logger, customLogStream_.str().c_str()); \ + } + +#define LOGW_INFO(logger, logEvent) \ + { \ + CustomLogWStream customLogWstream_; \ + customLogWstream_ << logEvent; \ + LOG4CPLUS_INFO(logger, customLogWstream_.str().c_str()); \ + } + +#define LOG_WARN(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + LOG4CPLUS_WARN(logger, customLogStream_.str().c_str()); \ + } + +#define LOGW_WARN(logger, logEvent) \ + { \ + CustomLogWStream customLogWstream_; \ + customLogWstream_ << logEvent; \ + LOG4CPLUS_WARN(logger, customLogWstream_.str().c_str()); \ + } + +#define LOG_ERROR(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + LOG4CPLUS_ERROR(logger, customLogStream_.str().c_str()); \ + } + +#define LOGW_ERROR(logger, logEvent) \ + { \ + CustomLogWStream customLogWstream_; \ + customLogWstream_ << logEvent; \ + LOG4CPLUS_ERROR(logger, customLogWstream_.str().c_str()); \ + } + +#define LOG_FATAL(logger, logEvent) \ + { \ + CustomLogStream customLogStream_; \ + customLogStream_ << logEvent; \ + LOG4CPLUS_FATAL(logger, customLogStream_.str().c_str()); \ + } + +#define LOGW_FATAL(logger, logEvent) \ + { \ + CustomLogWStream customLogWstream_; \ + customLogWstream_ << logEvent; \ + LOG4CPLUS_FATAL(logger, customLogWstream_.str().c_str()); \ + } -#define LOGW_FATAL(logger, logEvent) LOG4CPLUS_FATAL(logger, logEvent) #endif class COMMONSERVER_EXPORT Log { diff --git a/src/libcommonserver/utility/utility.cpp b/src/libcommonserver/utility/utility.cpp index b32857e75..688248eeb 100644 --- a/src/libcommonserver/utility/utility.cpp +++ b/src/libcommonserver/utility/utility.cpp @@ -48,8 +48,6 @@ #include #include -#include - #ifndef _WIN32 #include #endif @@ -402,12 +400,12 @@ bool Utility::isEqualInsensitive(const SyncName &a, const SyncName &b) { bool Utility::checkIfSameNormalization(const SyncName &a, const SyncName &b, bool &areSame) { SyncName aNormalized; if (!Utility::normalizedSyncName(a, aNormalized)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(a)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(a)); return false; } SyncName bNormalized; if (!Utility::normalizedSyncName(b, bNormalized)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(b)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(b)); return false; } areSame = (aNormalized == bNormalized); @@ -417,12 +415,12 @@ bool Utility::checkIfSameNormalization(const SyncName &a, const SyncName &b, boo bool Utility::checkIfSameNormalization(const SyncPath &a, const SyncPath &b, bool &areSame) { SyncPath aNormalized; if (!Utility::normalizedSyncPath(a, aNormalized)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncPath: " << Utility::formatSyncPath(a)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncPath: " << Utility::formatSyncPath(a)); return false; } SyncPath bNormalized; if (!Utility::normalizedSyncPath(b, bNormalized)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncPath: " << Utility::formatSyncPath(b)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncPath: " << Utility::formatSyncPath(b)); return false; } areSame = (aNormalized == bNormalized); @@ -699,7 +697,7 @@ bool Utility::normalizedSyncPath(const SyncPath &path, SyncPath &normalizedPath) if (segmentIt->lexically_normal() != SyncPath(Str("/")).lexically_normal()) { SyncName normalizedName; if (!Utility::normalizedSyncName(segment, normalizedName)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(segment)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(segment)); return false; } segment = normalizedName; @@ -711,7 +709,7 @@ bool Utility::normalizedSyncPath(const SyncPath &path, SyncPath &normalizedPath) if (segmentIt->lexically_normal() != SyncPath(Str("/")).lexically_normal()) { SyncName normalizedName; if (!Utility::normalizedSyncName(*segmentIt, normalizedName)) { - LOGW_WARN(_logger, L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(*segmentIt)); + LOGW_WARN(logger(), L"Error in Utility::normalizedSyncName: " << Utility::formatSyncName(*segmentIt)); return false; } normalizedPath /= normalizedName; @@ -911,4 +909,8 @@ SyncPath Utility::sharedFolderName() { return Str2SyncName(SHARED_FOLDER); } +std::string Utility::userName() { + return userName_private(); +} + } // namespace KDC diff --git a/src/libcommonserver/utility/utility.h b/src/libcommonserver/utility/utility.h index 6ea2c2a40..0c4735661 100644 --- a/src/libcommonserver/utility/utility.h +++ b/src/libcommonserver/utility/utility.h @@ -182,6 +182,7 @@ struct COMMONSERVER_EXPORT Utility { static SyncPath commonDocumentsFolderName(); static SyncPath sharedFolderName(); + static std::string userName(); private: static log4cplus::Logger _logger; diff --git a/src/libcommonserver/utility/utility_linux.cpp b/src/libcommonserver/utility/utility_linux.cpp index f8ea851f2..154cdc17c 100644 --- a/src/libcommonserver/utility/utility_linux.cpp +++ b/src/libcommonserver/utility/utility_linux.cpp @@ -17,6 +17,7 @@ */ #include "log/log.h" +#include "libcommon/utility/utility.h" #include #include @@ -261,4 +262,9 @@ static bool setFileDates_private(const KDC::SyncPath &filePath, std::optional #include @@ -174,4 +175,10 @@ static bool setFileDates_private(const KDC::SyncPath &filePath, std::optional modificationDate, bool symlink, bool &exists) { return KDC::setFileDates(filePath, creationDate, modificationDate, symlink, exists); } + +static std::string userName_private() { + bool isSet = false; + return CommonUtility::envVarValue("USER", isSet); +} + } // namespace KDC diff --git a/src/libcommonserver/utility/utility_win.cpp b/src/libcommonserver/utility/utility_win.cpp index e3d6483b6..036ab5e37 100644 --- a/src/libcommonserver/utility/utility_win.cpp +++ b/src/libcommonserver/utility/utility_win.cpp @@ -45,6 +45,8 @@ #include #include +constexpr int userNameBufLen = 4096; + namespace KDC { static bool moveItemToTrash_private(const SyncPath &itemPath) { @@ -283,4 +285,11 @@ static bool cpuUsageByProcess_private(double &percent) { return true; } +static std::string userName_private() { + DWORD len = userNameBufLen; + wchar_t userName[userNameBufLen]; + GetUserName(userName, &len); + return Utility::ws2s(std::wstring(userName)); +} + } // namespace KDC diff --git a/src/libcommonserver/vfs.h b/src/libcommonserver/vfs.h index ad7201f94..985f5ccbf 100644 --- a/src/libcommonserver/vfs.h +++ b/src/libcommonserver/vfs.h @@ -294,6 +294,8 @@ class VfsOff : public Vfs { protected: bool startImpl(bool &installationDone, bool &activationDone, bool &connectionDone) override; void stopImpl(bool /*unregister*/) override {} + + friend class TestWorkers; }; /// Check whether the plugin for the mode is available. diff --git a/src/libparms/db/parmsdb.h b/src/libparms/db/parmsdb.h index 2d2d0b125..d9935ad95 100644 --- a/src/libparms/db/parmsdb.h +++ b/src/libparms/db/parmsdb.h @@ -140,7 +140,7 @@ class PARMS_EXPORT ParmsDb : public Db { bool insertDefaultParameters(); bool insertDefaultAppState(); - bool insertAppState(AppStateKey key, const std::string &value, bool noEmptyValue = false); + bool insertAppState(AppStateKey key, const std::string &value, bool updateOnlyIfEmpty = false); bool updateExclusionTemplates(); bool createAppState(); diff --git a/src/libparms/db/parmsdbappstate.cpp b/src/libparms/db/parmsdbappstate.cpp index 1f3e8d619..84844a62e 100644 --- a/src/libparms/db/parmsdbappstate.cpp +++ b/src/libparms/db/parmsdbappstate.cpp @@ -103,7 +103,7 @@ bool ParmsDb::insertDefaultAppState() { return false; } - if (!insertAppState(AppStateKey::AppUid, CommonUtility::generateAppId(25), true)) { + if (!insertAppState(AppStateKey::AppUid, CommonUtility::generateRandomStringAlphaNum(25), true)) { LOG_WARN(_logger, "Error when inserting default value for LogUploadToken"); return false; } @@ -111,11 +111,8 @@ bool ParmsDb::insertDefaultAppState() { return true; } -bool ParmsDb::insertAppState(AppStateKey key, const std::string &value, bool noEmptyValue /*= false*/) { +bool ParmsDb::insertAppState(AppStateKey key, const std::string &value, const bool updateOnlyIfEmpty /*= false*/) { const std::scoped_lock lock(_mutex); - int errId = 0; - std::string error; - bool found = false; std::string valueStr = value; if (valueStr.empty()) { LOG_WARN(_logger, "Value is empty for AppStateKey: " << CommonUtility::appStateKeyToString(key).c_str()); @@ -127,31 +124,28 @@ bool ParmsDb::insertAppState(AppStateKey key, const std::string &value, bool noE ASSERT(queryResetAndClearBindings(SELECT_APP_STATE_REQUEST_ID)) ASSERT(queryBindValue(SELECT_APP_STATE_REQUEST_ID, 1, static_cast(key))) + bool found = false; if (!queryNext(SELECT_APP_STATE_REQUEST_ID, found)) { LOG_WARN(_logger, "Error getting query result: " << SELECT_APP_STATE_REQUEST_ID); return false; } std::string existingValue; - ASSERT(queryStringValue(SELECT_APP_STATE_REQUEST_ID, 0, existingValue)) + if (found) { + ASSERT(queryStringValue(SELECT_APP_STATE_REQUEST_ID, 0, existingValue)) + } ASSERT(queryResetAndClearBindings(SELECT_APP_STATE_REQUEST_ID)) - if (!found) { - ASSERT(queryResetAndClearBindings(INSERT_APP_STATE_REQUEST_ID)) - ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 1, static_cast(key))) - ASSERT(queryBindValue(INSERT_APP_STATE_REQUEST_ID, 2, valueStr)) - if (!queryExec(INSERT_APP_STATE_REQUEST_ID, errId, error)) { - LOG_WARN(_logger, "Error running query: " << INSERT_APP_STATE_REQUEST_ID); - return false; - } - } else if (noEmptyValue && existingValue.empty()) { - ASSERT(queryResetAndClearBindings(UPDATE_APP_STATE_REQUEST_ID)) - ASSERT(queryBindValue(UPDATE_APP_STATE_REQUEST_ID, 1, static_cast(key))) - ASSERT(queryBindValue(UPDATE_APP_STATE_REQUEST_ID, 2, valueStr)) - if (!queryExec(UPDATE_APP_STATE_REQUEST_ID, errId, error)) { - LOG_WARN(_logger, "Error running query: " << UPDATE_APP_STATE_REQUEST_ID); + if (!found || (updateOnlyIfEmpty && existingValue.empty())) { + const auto requestId = found ? UPDATE_APP_STATE_REQUEST_ID : INSERT_APP_STATE_REQUEST_ID; + ASSERT(queryBindValue(requestId, 1, static_cast(key))) + ASSERT(queryBindValue(requestId, 2, valueStr)) + int errId = 0; + std::string error; + if (!queryExec(requestId, errId, error)) { + LOG_WARN(_logger, "Error running query: " << requestId); return false; } - ASSERT(queryResetAndClearBindings(UPDATE_APP_STATE_REQUEST_ID)) + ASSERT(queryResetAndClearBindings(requestId)) } return true; } diff --git a/src/libsyncengine/jobs/jobmanager.cpp b/src/libsyncengine/jobs/jobmanager.cpp index 1340fe99e..6347a4c68 100644 --- a/src/libsyncengine/jobs/jobmanager.cpp +++ b/src/libsyncengine/jobs/jobmanager.cpp @@ -25,7 +25,6 @@ #include "performance_watcher/performancewatcher.h" #include "requests/parameterscache.h" -#include #include // std::max #include @@ -139,7 +138,7 @@ JobManager::JobManager() : _logger(Log::instance()->getLogger()) { _cpuUsageThreshold = ParametersCache::instance()->parameters().maxAllowedCpu() / 100.0; - _thread = std::make_unique(run); + _thread = std::make_unique(run); LOG_DEBUG(_logger, "Network Job Manager started with max " << _maxNbThread << " threads"); } diff --git a/src/libsyncengine/jobs/jobmanager.h b/src/libsyncengine/jobs/jobmanager.h index c3cd1f558..b7dc0d6f9 100644 --- a/src/libsyncengine/jobs/jobmanager.h +++ b/src/libsyncengine/jobs/jobmanager.h @@ -20,6 +20,7 @@ #pragma once #include "abstractjob.h" +#include "libcommon/utility/utility.h" #include @@ -96,7 +97,7 @@ class JobManager { static std::chrono::time_point _maxNbThreadChrono; log4cplus::Logger _logger; - std::unique_ptr _thread; + std::unique_ptr _thread; static std::unordered_map> _managedJobs; // queued + running + pending jobs static std::priority_queue, Poco::Thread::Priority>, diff --git a/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp b/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp index 432fc82fa..49d3e7b98 100644 --- a/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp +++ b/src/libsyncengine/jobs/network/API_v2/downloadjob.cpp @@ -43,8 +43,9 @@ namespace KDC { DownloadJob::DownloadJob(int driveDbId, const NodeId &remoteFileId, const SyncPath &localpath, int64_t expectedSize, SyncTime creationTime, SyncTime modtime, bool isCreate) : - AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0, false), _remoteFileId(remoteFileId), _localpath(localpath), - _expectedSize(expectedSize), _creationTime(creationTime), _modtimeIn(modtime), _isCreate(isCreate) { + AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0, false), + _remoteFileId(remoteFileId), _localpath(localpath), _expectedSize(expectedSize), _creationTime(creationTime), + _modtimeIn(modtime), _isCreate(isCreate) { _httpMethod = Poco::Net::HTTPRequest::HTTP_GET; _customTimeout = 60; _trials = TRIALS; @@ -62,32 +63,32 @@ DownloadJob::~DownloadJob() { if (_responseHandlingCanceled) { if (_vfsSetPinState) { if (!_vfsSetPinState(_localpath, PinState::OnlineOnly)) { - LOGW_WARN(_logger, L"Error in vfsSetPinState: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsSetPinState: " << Utility::formatSyncPath(_localpath)); } } // TODO: usefull ? if (_vfsForceStatus) { if (!_vfsForceStatus(_localpath, false, 0, false)) { - LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath)); } } if (_vfsCancelHydrate) { if (!_vfsCancelHydrate(_localpath)) { - LOGW_WARN(_logger, L"Error in vfsCancelHydrate: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsCancelHydrate: " << Utility::formatSyncPath(_localpath)); } } } else { if (_vfsSetPinState) { if (!_vfsSetPinState(_localpath, _exitCode == ExitCode::Ok ? PinState::AlwaysLocal : PinState::OnlineOnly)) { - LOGW_WARN(_logger, L"Error in vfsSetPinState: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsSetPinState: " << Utility::formatSyncPath(_localpath)); } } if (_vfsForceStatus) { if (!_vfsForceStatus(_localpath, false, 0, _exitCode == ExitCode::Ok)) { - LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath)); } } } @@ -110,15 +111,15 @@ bool DownloadJob::canRun() { bool exists; IoError ioError = IoError::Success; if (!IoHelper::checkIfPathExists(_localpath, exists, ioError)) { - LOGW_WARN(_logger, L"Error in IoHelper::checkIfPathExists: " << Utility::formatIoError(_localpath, ioError).c_str()); + LOGW_WARN(_logger, L"Error in IoHelper::checkIfPathExists: " << Utility::formatIoError(_localpath, ioError)); _exitCode = ExitCode::SystemError; _exitCause = ExitCause::FileAccessError; return false; } if (_isCreate && exists) { - LOGW_DEBUG(_logger, L"Item: " << Utility::formatSyncPath(_localpath).c_str() - << L" already exist. Aborting current sync and restart."); + LOGW_DEBUG(_logger, + L"Item with " << Utility::formatSyncPath(_localpath) << L" already exists. Aborting current sync and restarting."); _exitCode = ExitCode::NeedRestart; _exitCause = ExitCause::UnexpectedFileSystemEvent; return false; @@ -134,29 +135,29 @@ void DownloadJob::runJob() noexcept { FileStat filestat; IoError ioError = IoError::Success; if (!IoHelper::getFileStat(_localpath, &filestat, ioError)) { - LOGW_WARN(_logger, L"Error in IoHelper::getFileStat: " << Utility::formatIoError(_localpath, ioError).c_str()); + LOGW_WARN(_logger, L"Error in IoHelper::getFileStat: " << Utility::formatIoError(_localpath, ioError)); return; } if (ioError == IoError::NoSuchFileOrDirectory) { - LOGW_WARN(_logger, L"Item does not exist anymore: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Item does not exist anymore: " << Utility::formatSyncPath(_localpath)); return; } else if (ioError == IoError::AccessDenied) { - LOGW_WARN(_logger, L"Item misses search permission: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Item misses search permission: " << Utility::formatSyncPath(_localpath)); return; } std::string error; if (!_vfsUpdateMetadata(_localpath, filestat.creationTime, filestat.modtime, _expectedSize, std::to_string(filestat.inode), error)) { - LOGW_WARN(_logger, L"Update metadata failed: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Update metadata failed: " << Utility::formatSyncPath(_localpath)); return; } } if (_vfsForceStatus) { if (!_vfsForceStatus(_localpath, true, 0, false)) { - LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsForceStatus: " << Utility::formatSyncPath(_localpath)); return; } } @@ -209,7 +210,7 @@ bool DownloadJob::handleResponse(std::istream &is) { SyncPath tmpPath; IoError ioError = IoError::Success; if (!IoHelper::tempDirectoryPath(tmpPath, ioError)) { - LOGW_WARN(_logger, L"Failed to get temporary directory path: " << Utility::formatIoError(tmpPath, ioError).c_str()); + LOGW_WARN(_logger, L"Failed to get temporary directory path: " << Utility::formatIoError(tmpPath, ioError)); _exitCode = ExitCode::SystemError; _exitCause = ExitCause::Unknown; return false; @@ -219,7 +220,7 @@ bool DownloadJob::handleResponse(std::istream &is) { std::ofstream output(tmpPath.native().c_str(), std::ios::binary); if (!output) { - LOGW_WARN(_logger, L"Failed to create file: " << Utility::formatSyncPath(tmpPath).c_str()); + LOGW_WARN(_logger, L"Failed to create file: " << Utility::formatSyncPath(tmpPath)); _exitCode = ExitCode::SystemError; _exitCause = Utility::enoughSpace(tmpPath) ? ExitCause::FileAccessError : ExitCause::NotEnoughDiskSpace; return false; @@ -303,11 +304,11 @@ bool DownloadJob::handleResponse(std::istream &is) { if (elapsed_seconds.count() > NOTIFICATION_DELAY || done) { // Update fetch status if (!_vfsUpdateFetchStatus(tmpPath, _localpath, getProgress(), fetchCanceled, fetchFinished)) { - LOGW_WARN(_logger, L"Error in vfsUpdateFetchStatus: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsUpdateFetchStatus: " << Utility::formatSyncPath(_localpath)); fetchError = true; break; } else if (fetchCanceled) { - LOGW_WARN(_logger, L"Update fetch status canceled: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Update fetch status canceled: " << Utility::formatSyncPath(_localpath)); break; } fileProgressTimer = std::chrono::steady_clock::now(); @@ -330,12 +331,12 @@ bool DownloadJob::handleResponse(std::istream &is) { if (_vfsUpdateFetchStatus && !fetchFinished) { // Update fetch status if (!_vfsUpdateFetchStatus(tmpPath, _localpath, getProgress(), fetchCanceled, fetchFinished)) { - LOGW_WARN(_logger, L"Error in vfsUpdateFetchStatus: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in vfsUpdateFetchStatus: " << Utility::formatSyncPath(_localpath)); fetchError = true; } else if (fetchCanceled) { - LOGW_WARN(_logger, L"Update fetch status canceled: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Update fetch status canceled: " << Utility::formatSyncPath(_localpath)); } else if (!fetchFinished) { - LOGW_WARN(_logger, L"Update fetch status not terminated: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Update fetch status not terminated: " << Utility::formatSyncPath(_localpath)); } _responseHandlingCanceled = fetchCanceled || fetchError || (!fetchFinished); @@ -343,7 +344,7 @@ bool DownloadJob::handleResponse(std::istream &is) { // Replace file by tmp one bool replaceError = false; if (!moveTmpFile(tmpPath, restartSync)) { - LOGW_WARN(_logger, L"Failed to replace file by tmp one: " << Utility::formatSyncPath(tmpPath).c_str()); + LOGW_WARN(_logger, L"Failed to replace file by tmp one: " << Utility::formatSyncPath(tmpPath)); replaceError = true; } @@ -356,7 +357,7 @@ bool DownloadJob::handleResponse(std::istream &is) { // Remove tmp file if (!removeTmpFile(tmpPath)) { - LOGW_WARN(_logger, L"Failed to remove tmp file: " << Utility::formatSyncPath(tmpPath).c_str()); + LOGW_WARN(_logger, L"Failed to remove tmp file: " << Utility::formatSyncPath(tmpPath)); } if (isAborted() || fetchCanceled) { @@ -381,12 +382,12 @@ bool DownloadJob::handleResponse(std::istream &is) { bool exists = false; if (!Utility::setFileDates(_localpath, std::make_optional(_creationTime), std::make_optional(_modtimeIn), isLink, exists)) { - LOGW_WARN(_logger, L"Error in Utility::setFileDates: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Error in Utility::setFileDates: " << Utility::formatSyncPath(_localpath)); // Do nothing (remote file will be updated during the next sync) SentryHandler::instance()->captureMessage(SentryLevel::Warning, "DownloadJob::handleResponse", "Unable to set file dates"); } else if (!exists) { - LOGW_INFO(_logger, L"Item does not exist anymore. Restarting sync: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_INFO(_logger, L"Item does not exist anymore. Restarting sync: " << Utility::formatSyncPath(_localpath)); _exitCode = ExitCode::DataError; _exitCause = ExitCause::InvalidSnapshot; return false; @@ -397,19 +398,19 @@ bool DownloadJob::handleResponse(std::istream &is) { FileStat filestat; IoError ioError = IoError::Success; if (!IoHelper::getFileStat(_localpath, &filestat, ioError)) { - LOGW_WARN(_logger, L"Error in IoHelper::getFileStat: " << Utility::formatIoError(_localpath, ioError).c_str()); + LOGW_WARN(_logger, L"Error in IoHelper::getFileStat: " << Utility::formatIoError(_localpath, ioError)); _exitCode = ExitCode::SystemError; _exitCause = ExitCause::Unknown; return false; } if (ioError == IoError::NoSuchFileOrDirectory) { - LOGW_WARN(_logger, L"Item does not exist anymore: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Item does not exist anymore: " << Utility::formatSyncPath(_localpath)); _exitCode = ExitCode::DataError; _exitCause = ExitCause::InvalidSnapshot; return false; } else if (ioError == IoError::AccessDenied) { - LOGW_WARN(_logger, L"Item misses search permission: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_WARN(_logger, L"Item misses search permission: " << Utility::formatSyncPath(_localpath)); _exitCode = ExitCode::SystemError; _exitCause = ExitCause::FileAccessError; return false; @@ -430,56 +431,56 @@ bool DownloadJob::createLink(const std::string &mimeType, const std::string &dat // Create symlink const auto targetPath = Str2Path(data); if (targetPath == _localpath) { - LOGW_DEBUG(_logger, L"Cannot create symlink on itself: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Cannot create symlink on itself: " << Utility::formatSyncPath(_localpath)); return false; } - LOGW_DEBUG(_logger, L"Create symlink: " << Utility::formatSyncPath(targetPath).c_str() << L", " - << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, + L"Create symlink with target " << Utility::formatSyncPath(targetPath) << L", " << Utility::formatSyncPath(_localpath)); bool isFolder = mimeType == mimeTypeSymlinkFolder; IoError ioError = IoError::Success; if (!IoHelper::createSymlink(targetPath, _localpath, isFolder, ioError)) { - LOGW_WARN(_logger, L"Failed to create symlink: " << Utility::formatIoError(targetPath, ioError).c_str()); + LOGW_WARN(_logger, L"Failed to create symlink: " << Utility::formatIoError(targetPath, ioError)); return false; } } else if (mimeType == mimeTypeHardlink) { // Unreachable code const auto targetPath = Str2Path(data); if (targetPath == _localpath) { - LOGW_DEBUG(_logger, L"Cannot create hardlink on itself: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Cannot create hardlink on itself: " << Utility::formatSyncPath(_localpath)); return false; } - LOGW_DEBUG(_logger, L"Create hardlink: target " << Utility::formatSyncPath(targetPath).c_str() << L", " - << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Create hardlink: target " << Utility::formatSyncPath(targetPath) << L", " + << Utility::formatSyncPath(_localpath)); std::error_code ec; std::filesystem::create_hard_link(targetPath, _localpath, ec); if (ec) { - LOGW_WARN(_logger, L"Failed to create hardlink: target " << Utility::formatSyncPath(targetPath).c_str() << L", " - << Utility::formatSyncPath(_localpath).c_str() << L", " - << Utility::formatStdError(ec).c_str()); + LOGW_WARN(_logger, L"Failed to create hardlink: target " << Utility::formatSyncPath(targetPath) << L", " + << Utility::formatSyncPath(_localpath) << L", " + << Utility::formatStdError(ec)); return false; } } else if (mimeType == mimeTypeJunction) { #if defined(_WIN32) - LOGW_DEBUG(_logger, L"Create junction: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Create junction: " << Utility::formatSyncPath(_localpath)); IoError ioError = IoError::Success; if (!IoHelper::createJunction(data, _localpath, ioError)) { - LOGW_WARN(_logger, L"Failed to create junction: " << Utility::formatIoError(_localpath, ioError).c_str()); + LOGW_WARN(_logger, L"Failed to create junction: " << Utility::formatIoError(_localpath, ioError)); return false; } #endif } else if (mimeType == mimeTypeFinderAlias) { #if defined(__APPLE__) - LOGW_DEBUG(_logger, L"Create alias: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Create alias: " << Utility::formatSyncPath(_localpath)); IoError ioError = IoError::Success; if (!IoHelper::createAlias(data, _localpath, ioError)) { const std::wstring message = Utility::s2ws(IoHelper::ioError2StdString(ioError)); - LOGW_WARN(_logger, L"Failed to create alias: " << Utility::formatIoError(_localpath, ioError).c_str()); + LOGW_WARN(_logger, L"Failed to create alias: " << Utility::formatIoError(_localpath, ioError)); return false; } @@ -493,21 +494,15 @@ bool DownloadJob::createLink(const std::string &mimeType, const std::string &dat } bool DownloadJob::removeTmpFile(const SyncPath &path) { - std::error_code ec; - if (!std::filesystem::remove_all(path, ec)) { - if (ec) { - LOGW_WARN(_logger, L"Failed to remove all: " << Utility::formatStdError(path, ec).c_str()); - return false; - } - - LOGW_WARN(_logger, L"Failed to remove all: " << Utility::formatSyncPath(path).c_str()); + if (std::error_code ec; !std::filesystem::remove_all(path, ec)) { + LOGW_WARN(_logger, L"Failed to remove all: " << Utility::formatStdError(path, ec)); return false; } return true; } -bool DownloadJob::moveTmpFile(const SyncPath &path, bool &restartSync) { +bool DownloadJob::moveTmpFile(const SyncPath &tmpPath, bool &restartSync) { restartSync = false; // Move downloaded file from tmp directory to sync directory @@ -518,20 +513,29 @@ bool DownloadJob::moveTmpFile(const SyncPath &path, bool &restartSync) { while (retry) { retry = false; #endif - std::filesystem::rename(path, _localpath, ec); -#ifdef _WIN32 - const bool crossDeviceLink = ec.value() == ERROR_NOT_SAME_DEVICE; -#else - const bool crossDeviceLink = ec.value() == (int) std::errc::cross_device_link; -#endif + + IoError ioError = IoError::Success; + IoHelper::moveItem(tmpPath, _localpath, ioError); + const bool crossDeviceLink = ioError == IoError::CrossDeviceLink; + if (ioError != IoError::Success && !crossDeviceLink) { + LOGW_WARN(_logger, L"Failed to move: " << Utility::formatIoError(tmpPath, ioError) << L" to " + << Utility::formatSyncPath(_localpath)); + return false; + } + if (crossDeviceLink) { // The sync might be on a different file system than tmp folder. // In that case, try to copy the file instead. ec.clear(); - std::filesystem::copy(path, _localpath, std::filesystem::copy_options::overwrite_existing, ec); + std::filesystem::copy(tmpPath, _localpath, std::filesystem::copy_options::overwrite_existing, ec); + bool removed = removeTmpFile(tmpPath); if (ec) { - LOGW_WARN(_logger, L"Failed to copy: " << Utility::formatSyncPath(_localpath).c_str() << L", " - << Utility::formatStdError(ec).c_str()); + LOGW_WARN(_logger, + L"Failed to copy to " << Utility::formatSyncPath(_localpath) << L", " << Utility::formatStdError(ec)); + return false; + } + if (!removed) { + LOGW_WARN(_logger, L"Failed to remove " << Utility::formatSyncPath(tmpPath)); return false; } } @@ -541,10 +545,10 @@ bool DownloadJob::moveTmpFile(const SyncPath &path, bool &restartSync) { // Retry retry = true; Utility::msleep(10); - LOGW_DEBUG(_logger, L"Retrying to move downloaded file: " << Utility::formatSyncPath(_localpath).c_str()); + LOGW_DEBUG(_logger, L"Retrying to move downloaded file: " << Utility::formatSyncPath(_localpath)); counter--; } else { - LOGW_WARN(_logger, L"Failed to rename: " << Utility::formatStdError(_localpath, ec).c_str()); + LOGW_WARN(_logger, L"Failed to rename: " << Utility::formatStdError(_localpath, ec)); return false; } } @@ -557,8 +561,8 @@ bool DownloadJob::moveTmpFile(const SyncPath &path, bool &restartSync) { bool exists = false; IoError ioError = IoError::Success; if (!IoHelper::checkIfPathExists(_localpath.parent_path(), exists, ioError)) { - LOGW_WARN(_logger, L"Error in IoHelper::checkIfPathExists: " - << Utility::formatIoError(_localpath.parent_path(), ioError).c_str()); + LOGW_WARN(_logger, + L"Error in IoHelper::checkIfPathExists: " << Utility::formatIoError(_localpath.parent_path(), ioError)); _exitCode = ExitCode::SystemError; _exitCause = ExitCause::Unknown; return false; @@ -571,12 +575,12 @@ bool DownloadJob::moveTmpFile(const SyncPath &path, bool &restartSync) { } if (!exists) { - LOGW_INFO(_logger, L"Parent of item does not exist anymore: " << Utility::formatStdError(_localpath, ec).c_str()); + LOGW_INFO(_logger, L"Parent of item does not exist anymore: " << Utility::formatStdError(_localpath, ec)); restartSync = true; return true; } - LOGW_WARN(_logger, L"Failed to rename: " << Utility::formatStdError(_localpath, ec).c_str()); + LOGW_WARN(_logger, L"Failed to rename: " << Utility::formatStdError(_localpath, ec)); return false; } #ifdef _WIN32 diff --git a/src/libsyncengine/jobs/network/API_v2/upload_session/abstractuploadsession.cpp b/src/libsyncengine/jobs/network/API_v2/upload_session/abstractuploadsession.cpp index 8b9ec69b3..f6444bbf4 100644 --- a/src/libsyncengine/jobs/network/API_v2/upload_session/abstractuploadsession.cpp +++ b/src/libsyncengine/jobs/network/API_v2/upload_session/abstractuploadsession.cpp @@ -179,7 +179,7 @@ bool AbstractUploadSession::canRun() { if (!exists) { LOGW_DEBUG(_logger, - L"Item does not exist anymore. Aborting current sync and restart. - path=" << Path2WStr(_filePath).c_str()); + L"Item does not exist anymore. Aborting current sync and restart " << Utility::formatSyncPath(_filePath)); _exitCode = ExitCode::NeedRestart; _exitCause = ExitCause::UnexpectedFileSystemEvent; return false; @@ -265,11 +265,15 @@ bool AbstractUploadSession::sendChunks() { bool checksumError = false; bool jobCreationError = false; bool sendChunksCanceled = false; - std::ifstream file(_filePath.native().c_str(), std::ifstream::binary); - if (!file.is_open()) { - LOGW_WARN(_logger, L"Failed to open file " << Path2WStr(_filePath).c_str()); - _exitCode = ExitCode::DataError; - return false; + + // Some applications generate locked temporary files during save operations. To avoid spurious "access denied" errors, + // we retry for 10 seconds, which is usually sufficient for the application to delete the tmp file. If the file is still + // locked after 10 seconds, a file access error is displayed to the user. Proper handling is also implemented for + // "file not found" errors. + std::ifstream file; + if (ExitInfo exitInfo = IoHelper::openFile(_filePath, file, 10); !exitInfo) { + LOGW_WARN(_logger, L"Failed to open file " << Utility::formatSyncPath(_filePath)); + return exitInfo; } // Create a hash state diff --git a/src/libsyncengine/jobs/network/API_v2/uploadjob.cpp b/src/libsyncengine/jobs/network/API_v2/uploadjob.cpp index 42a1fa041..fe3fb0fcc 100644 --- a/src/libsyncengine/jobs/network/API_v2/uploadjob.cpp +++ b/src/libsyncengine/jobs/network/API_v2/uploadjob.cpp @@ -79,7 +79,8 @@ bool UploadJob::canRun() { } if (!exists) { - LOGW_DEBUG(_logger, L"Item does not exist anymore. Aborting current sync and restart - path=" << Path2WStr(_filePath)); + LOGW_DEBUG(_logger, + L"Item does not exist anymore. Aborting current sync and restart " << Utility::formatSyncPath(_filePath)); _exitCode = ExitCode::NeedRestart; _exitCause = ExitCause::UnexpectedFileSystemEvent; return false; @@ -184,10 +185,14 @@ std::string UploadJob::getContentType(bool &canceled) { } ExitInfo UploadJob::readFile() { - std::ifstream file(_filePath, std::ios_base::in | std::ios_base::binary); - if (!file.is_open()) { - LOGW_WARN(_logger, L"Failed to open file - path=" << Path2WStr(_filePath)); - return {ExitCode::SystemError, ExitCause::FileAccessError}; + // Some applications generate locked temporary files during save operations. To avoid spurious "access denied" errors, + // we retry for 10 seconds, which is usually sufficient for the application to delete the tmp file. If the file is still + // locked after 10 seconds, a file access error is displayed to the user. Proper handling is also implemented for + // "file not found" errors. + std::ifstream file; + if (ExitInfo exitInfo = IoHelper::openFile(_filePath, file, 10); !exitInfo) { + LOGW_WARN(_logger, L"Failed to open file " << Utility::formatSyncPath(_filePath)); + return exitInfo; } std::ostringstream ostrm; diff --git a/src/libsyncengine/jobs/network/abstractnetworkjob.cpp b/src/libsyncengine/jobs/network/abstractnetworkjob.cpp index da1243d10..bfc5dfd92 100644 --- a/src/libsyncengine/jobs/network/abstractnetworkjob.cpp +++ b/src/libsyncengine/jobs/network/abstractnetworkjob.cpp @@ -76,7 +76,7 @@ AbstractNetworkJob::AbstractNetworkJob() { LOG_INFO(_logger, "Unknown error in Poco::Net::Context constructor: " << errorText(e).c_str() << ", retrying..."); } else { - LOG_ERROR(_logger, "Unknown error in Poco::Net::Context constructor: " << errorText(e).c_str()); + LOG_ERROR(_logger, "Unknown error in Poco::Net::Context constructor: " << errorText(e)); throw std::runtime_error(ABSTRACTNETWORKJOB_NEW_ERROR_MSG); } } diff --git a/src/libsyncengine/jobs/network/abstractnetworkjob.h b/src/libsyncengine/jobs/network/abstractnetworkjob.h index d7492e7f1..1a42476c6 100644 --- a/src/libsyncengine/jobs/network/abstractnetworkjob.h +++ b/src/libsyncengine/jobs/network/abstractnetworkjob.h @@ -18,7 +18,7 @@ #pragma once -#include "jobs/abstractjob.h" +#include "libsyncengine/jobs/abstractjob.h" #include #include diff --git a/src/libsyncengine/jobs/network/getappversionjob.cpp b/src/libsyncengine/jobs/network/getappversionjob.cpp index 171cb4fc9..b1d411fbe 100644 --- a/src/libsyncengine/jobs/network/getappversionjob.cpp +++ b/src/libsyncengine/jobs/network/getappversionjob.cpp @@ -24,7 +24,7 @@ namespace KDC { -static const std::string hasProdNextKey = "has_prod_next"; +static const std::string prodVersionKey = "prod_version"; static const std::string applicationKey = "application"; static const std::string publishedVersionsKey = "published_versions"; static const std::string versionTypeProdKey = "production"; @@ -41,12 +41,13 @@ static const std::string buildMinOsVersionKey = "build_min_os_version"; static const std::string downloadUrlKey = "download_link"; GetAppVersionJob::GetAppVersionJob(const Platform platform, const std::string &appID) : GetAppVersionJob(platform, appID, {}) {} + GetAppVersionJob::GetAppVersionJob(const Platform platform, const std::string &appID, const std::vector &userIdList) : _platform(platform), _appId(appID), _userIdList(userIdList) { _httpMethod = Poco::Net::HTTPRequest::HTTP_GET; } -std::string platformToStr(Platform platform) { +std::string toStr(const Platform platform) { switch (platform) { case Platform::MacOS: return platformMacOsKey; @@ -61,11 +62,20 @@ std::string platformToStr(Platform platform) { } } +DistributionChannel toDistributionChannel(const std::string &str) { + if (str == versionTypeProdKey) return DistributionChannel::Prod; + if (str == versionTypeNextKey) return DistributionChannel::Next; + if (str == versionTypeBetaKey) return DistributionChannel::Beta; + if (str == versionTypeInternalKey) return DistributionChannel::Internal; + return DistributionChannel::Unknown; +} + std::string GetAppVersionJob::getSpecificUrl() { std::stringstream ss; - ss << "/app-information/kstore-update/" << platformToStr(_platform) << "/com.infomaniak.drive/" << _appId; + ss << "/app-information/kstore-update/" << toStr(_platform) << "/com.infomaniak.drive/" << _appId; return ss.str(); } + std::string GetAppVersionJob::getContentType(bool &canceled) { canceled = false; return {}; @@ -92,16 +102,14 @@ DistributionChannel GetAppVersionJob::toDistributionChannel(const std::string &v } bool GetAppVersionJob::handleResponse(std::istream &is) { - if (!AbstractNetworkJob::handleJsonResponse(is)) { - return false; - } + if (!AbstractNetworkJob::handleJsonResponse(is)) return false; const Poco::JSON::Object::Ptr dataObj = JsonParserUtility::extractJsonObject(jsonRes(), dataKey); if (!dataObj) return false; - if (!JsonParserUtility::extractValue(dataObj, hasProdNextKey, _hasProdNext)) { - return false; - } + std::string tmpStr; + if (!JsonParserUtility::extractValue(dataObj, prodVersionKey, tmpStr)) return false; + _prodVersionChannel = toDistributionChannel(tmpStr); const Poco::JSON::Object::Ptr applicationObj = JsonParserUtility::extractJsonObject(dataObj, applicationKey); if (!applicationObj) return false; @@ -112,24 +120,16 @@ bool GetAppVersionJob::handleResponse(std::istream &is) { for (const auto &versionInfo: *publishedVersions) { const auto &obj = versionInfo.extract(); std::string versionType; - if (!JsonParserUtility::extractValue(obj, typeKey, versionType)) { - return false; - } + if (!JsonParserUtility::extractValue(obj, typeKey, versionType)) return false; const DistributionChannel channel = toDistributionChannel(versionType); _versionInfo[channel].channel = channel; - if (!JsonParserUtility::extractValue(obj, tagKey, _versionInfo[channel].tag)) { - return false; - } - if (!JsonParserUtility::extractValue(obj, buildVersionKey, _versionInfo[channel].buildVersion)) { - return false; - } - if (!JsonParserUtility::extractValue(obj, buildMinOsVersionKey, _versionInfo[channel].buildMinOsVersion, false)) { - return false; - } - if (!JsonParserUtility::extractValue(obj, downloadUrlKey, _versionInfo[channel].downloadUrl)) { + + if (!JsonParserUtility::extractValue(obj, tagKey, _versionInfo[channel].tag)) return false; + if (!JsonParserUtility::extractValue(obj, buildVersionKey, _versionInfo[channel].buildVersion)) return false; + if (!JsonParserUtility::extractValue(obj, buildMinOsVersionKey, _versionInfo[channel].buildMinOsVersion, false)) return false; - } + if (!JsonParserUtility::extractValue(obj, downloadUrlKey, _versionInfo[channel].downloadUrl)) return false; if (!_versionInfo[channel].isValid()) { LOG_WARN(_logger, "Missing mandatory value."); @@ -139,4 +139,5 @@ bool GetAppVersionJob::handleResponse(std::istream &is) { return true; } + } // namespace KDC diff --git a/src/libsyncengine/jobs/network/getappversionjob.h b/src/libsyncengine/jobs/network/getappversionjob.h index 0771cb607..5b86e0f84 100644 --- a/src/libsyncengine/jobs/network/getappversionjob.h +++ b/src/libsyncengine/jobs/network/getappversionjob.h @@ -30,12 +30,15 @@ class GetAppVersionJob : public AbstractNetworkJob { GetAppVersionJob(Platform platform, const std::string &appID, const std::vector &userIdList); ~GetAppVersionJob() override = default; - const VersionInfo &getVersionInfo(const DistributionChannel channel) { return _versionInfo[channel]; } + const VersionInfo &getVersionInfo(const DistributionChannel channel) { + return _versionInfo.contains(channel) ? _versionInfo[channel] : _defaultVersionInfo; + } + const VersionInfo &getProdVersionInfo() { + return _versionInfo.contains(_prodVersionChannel) ? _versionInfo[_prodVersionChannel] : _defaultVersionInfo; + } std::string getUrl() override { return INFOMANIAK_API_URL + getSpecificUrl(); } - [[nodiscard]] bool hasProdNext() const { return _hasProdNext; } - protected: bool handleResponse(std::istream &is) override; @@ -52,7 +55,8 @@ class GetAppVersionJob : public AbstractNetworkJob { const std::string _appId; const std::vector _userIdList; - bool _hasProdNext{false}; + const VersionInfo _defaultVersionInfo; + DistributionChannel _prodVersionChannel{DistributionChannel::Unknown}; std::unordered_map _versionInfo; }; diff --git a/src/libsyncengine/performance_watcher/performancewatcher.cpp b/src/libsyncengine/performance_watcher/performancewatcher.cpp index dffddb808..73134c7c8 100644 --- a/src/libsyncengine/performance_watcher/performancewatcher.cpp +++ b/src/libsyncengine/performance_watcher/performancewatcher.cpp @@ -59,7 +59,7 @@ void PerformanceWatcher::stop() { } PerformanceWatcher::PerformanceWatcher() { - _thread = std::make_unique(run); + _thread = std::make_unique(run); LOG_DEBUG(_logger, "Performance Watcher started"); } diff --git a/src/libsyncengine/performance_watcher/performancewatcher.h b/src/libsyncengine/performance_watcher/performancewatcher.h index 7e4d09cc0..ab4c94a78 100644 --- a/src/libsyncengine/performance_watcher/performancewatcher.h +++ b/src/libsyncengine/performance_watcher/performancewatcher.h @@ -18,6 +18,7 @@ #pragma once +#include "libcommon/utility/utility.h" #include "libcommonserver/utility/utility.h" #include "log/log.h" @@ -51,7 +52,7 @@ class PerformanceWatcher { static bool _stop; log4cplus::Logger _logger = Log::instance()->getLogger(); - std::unique_ptr _thread = nullptr; + std::unique_ptr _thread = nullptr; static void updateAllStats(); diff --git a/src/libsyncengine/progress/progressinfo.cpp b/src/libsyncengine/progress/progressinfo.cpp index 01a4384d1..effdb8594 100644 --- a/src/libsyncengine/progress/progressinfo.cpp +++ b/src/libsyncengine/progress/progressinfo.cpp @@ -119,8 +119,6 @@ bool ProgressInfo::setProgress(const SyncPath &path, const int64_t completed) { const auto it = _currentItems.find(normalizedPath); if (it == _currentItems.end() || it->second.empty()) { - LOGW_INFO(Log::instance()->getLogger(), - L"Item not found in ProgressInfo list (normal for ommited operation): " << Utility::formatSyncPath(path)); return true; } @@ -143,7 +141,7 @@ bool ProgressInfo::setProgressComplete(const SyncPath &path, const SyncFileStatu const auto it = _currentItems.find(normalizedPath); if (it == _currentItems.end() || it->second.empty()) { LOGW_INFO(Log::instance()->getLogger(), - L"Item not found in ProgressInfo list (normal for ommited operation): " << Utility::formatSyncPath(path)); + L"Item not found in ProgressInfo list (normal for ommited operation): " << Utility::formatSyncPath(path)); return true; } @@ -198,7 +196,8 @@ Estimates ProgressInfo::totalProgress() const { 1.0 - std::max(0.0, std::min((trans - transL * _maxBytesPerSecond) / ((transU - transL) * _maxBytesPerSecond), 1.0)); double beOptimistic = nearMaxFps * slowTransfer; - size.setEstimatedEta(static_cast(((1.0 - beOptimistic) * static_cast(size.estimatedEta()) + beOptimistic * static_cast(optimisticEta())))); + size.setEstimatedEta(static_cast(((1.0 - beOptimistic) * static_cast(size.estimatedEta()) + + beOptimistic * static_cast(optimisticEta())))); return size; } diff --git a/src/libsyncengine/propagation/executor/executorworker.cpp b/src/libsyncengine/propagation/executor/executorworker.cpp index 7e468e6f4..456039793 100644 --- a/src/libsyncengine/propagation/executor/executorworker.cpp +++ b/src/libsyncengine/propagation/executor/executorworker.cpp @@ -219,8 +219,8 @@ void ExecutorWorker::execute() { _syncPal->vfsCleanUpStatuses(); setExitCause(executorExitInfo.cause()); - setDone(executorExitInfo.code()); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(executorExitInfo.code()); } void ExecutorWorker::initProgressManager() { @@ -405,7 +405,7 @@ ExitInfo ExecutorWorker::handleCreateOp(SyncOpPtr syncOp, std::shared_ptraffectedNode()->type() == NodeType::Directory) { // Propagate the directory creation immediately in order to avoid blocking other dependant job creation - if (const ExitInfo exitInfoRunCreateDirJob = runCreateDirJob(syncOp, job); !exitInfoRunCreateDirJob ) { + if (const ExitInfo exitInfoRunCreateDirJob = runCreateDirJob(syncOp, job); !exitInfoRunCreateDirJob) { std::shared_ptr createDirJob = std::dynamic_pointer_cast(job); if (createDirJob && (createDirJob->getStatusCode() == Poco::Net::HTTPResponse::HTTP_BAD_REQUEST || createDirJob->getStatusCode() == Poco::Net::HTTPResponse::HTTP_FORBIDDEN)) { @@ -423,7 +423,7 @@ ExitInfo ExecutorWorker::handleCreateOp(SyncOpPtr syncOp, std::shared_ptraffectedNode()->name())); return exitInfo; } +#endif uint64_t filesize = 0; if (ExitInfo exitInfo = getFileSize(absoluteLocalFilePath, filesize); !exitInfo) { @@ -758,6 +761,11 @@ ExitInfo ExecutorWorker::createPlaceholder(const SyncPath &relativeLocalPath) { return ExitCode::SystemError; } + if (ioError == IoError::AccessDenied) { + LOGW_WARN(_logger, L"Access denied to " << Path2WStr(absoluteLocalPath).c_str()); + return {ExitCode::SystemError, ExitCause::FileAccessError}; + } + if (exists) { LOGW_WARN(_logger, L"Item already exists: " << Utility::formatSyncPath(absoluteLocalPath)); return {ExitCode::DataError, ExitCause::InvalidSnapshot}; @@ -838,7 +846,7 @@ ExitInfo ExecutorWorker::processCreateOrConvertToPlaceholderError(const SyncPath if (create && exists) { return {ExitCode::SystemError, ExitCause::FileAccessError}; } else if (!create && !exists) { - return {ExitCode::DataError, ExitCause::FileAlreadyExist}; + return {ExitCode::DataError, ExitCause::InvalidSnapshot}; } if (create) { @@ -1101,7 +1109,11 @@ ExitInfo ExecutorWorker::checkLiteSyncInfoForEdit(SyncOpPtr syncOp, const SyncPa syncOp->affectedNode()->lastmodified().has_value() ? *syncOp->affectedNode()->lastmodified() : 0, syncOp->affectedNode()->size(), syncOp->affectedNode()->id().has_value() ? *syncOp->affectedNode()->id() : std::string(), error); + // TODO: Vfs functions should return an ExitInfo struct syncOp->setOmit(true); // Do not propagate change in file system, only in DB + if (!error.empty()) { + return {ExitCode::SystemError, ExitCause::FileAccessError}; + } break; } case PinState::Unspecified: @@ -1428,7 +1440,7 @@ bool ExecutorWorker::isValidDestination(const SyncOpPtr syncOp) { return false; } - if (newCorrespondingParentNode->isCommonDocumentsFolder()) { + if (newCorrespondingParentNode->isCommonDocumentsFolder() && syncOp->nodeType() != NodeType::Directory) { return false; } @@ -1436,6 +1448,7 @@ bool ExecutorWorker::isValidDestination(const SyncOpPtr syncOp) { return false; } } + return true; } @@ -1503,6 +1516,7 @@ ExitInfo ExecutorWorker::waitForAllJobsToFinish() { ExitInfo ExecutorWorker::deleteFinishedAsyncJobs() { ExitInfo exitInfo = ExitCode::Ok; while (!_terminatedJobs.empty()) { + std::scoped_lock lock(_terminatedJobs); // Delete all terminated jobs if (exitInfo && _ongoingJobs.find(_terminatedJobs.front()) != _ongoingJobs.end()) { auto onGoingJobIt = _ongoingJobs.find(_terminatedJobs.front()); @@ -2497,6 +2511,9 @@ ExitInfo ExecutorWorker::handleExecutorError(SyncOpPtr syncOp, ExitInfo opsExitI case static_cast(ExitInfo(ExitCode::SystemError, ExitCause::MoveToTrashFailed)): { return handleOpsFileAccessError(syncOp, opsExitInfo); } + case static_cast(ExitInfo(ExitCode::SystemError, ExitCause::NotFound)): { + return handleOpsFileNotFound(syncOp, opsExitInfo); + } case static_cast(ExitInfo(ExitCode::BackError, ExitCause::FileAlreadyExist)): case static_cast(ExitInfo(ExitCode::DataError, ExitCause::FileAlreadyExist)): { return handleOpsAlreadyExistError(syncOp, opsExitInfo); @@ -2530,6 +2547,16 @@ ExitInfo ExecutorWorker::handleOpsFileAccessError(SyncOpPtr syncOp, ExitInfo ops return removeDependentOps(syncOp); } +ExitInfo ExecutorWorker::handleOpsFileNotFound(SyncOpPtr syncOp, ExitInfo opsExitInfo) { + if (syncOp->targetSide() != ReplicaSide::Remote) { + LOGW_SYNCPAL_WARN(_logger, L"Unhandled target side for " << opsExitInfo << L": " << syncOp->targetSide()); + return opsExitInfo; // Unable to handle this error + } + + _syncPal->setRestart(true); + return removeDependentOps(syncOp); +} + ExitInfo ExecutorWorker::handleOpsAlreadyExistError(SyncOpPtr syncOp, ExitInfo opsExitInfo) { // If the file/directory already exist either on local or remote side, we blacklist it localy and the remote // verson will be downloaded again. diff --git a/src/libsyncengine/propagation/executor/executorworker.h b/src/libsyncengine/propagation/executor/executorworker.h index ee740520a..6851ba511 100644 --- a/src/libsyncengine/propagation/executor/executorworker.h +++ b/src/libsyncengine/propagation/executor/executorworker.h @@ -38,22 +38,27 @@ class SyncDb; * In the context of `ExecutorWorker`, the terminated jobs queue is the only container that can be accessed from multiple threads, * namely, the job threads. Therefore, it is the only container that requires to be thread safe. */ -class TerminatedJobsQueue { +class TerminatedJobsQueue : public std::recursive_mutex { public: void push(const UniqueId id) { - const std::scoped_lock lock(_mutex); + const std::scoped_lock lock(*this); _terminatedJobs.push(id); } void pop() { - const std::scoped_lock lock(_mutex); + const std::scoped_lock lock(*this); _terminatedJobs.pop(); } - [[nodiscard]] UniqueId front() const { return _terminatedJobs.front(); } - [[nodiscard]] bool empty() const { return _terminatedJobs.empty(); } + [[nodiscard]] UniqueId front() { + const std::scoped_lock lock(*this); + return _terminatedJobs.front(); + } + [[nodiscard]] bool empty() { + const std::scoped_lock lock(*this); + return _terminatedJobs.empty(); + } private: std::queue _terminatedJobs; - std::mutex _mutex; }; class ExecutorWorker : public OperationProcessor { @@ -147,6 +152,7 @@ class ExecutorWorker : public OperationProcessor { // return opsExitInfo. ExitInfo handleExecutorError(SyncOpPtr syncOp, ExitInfo opsExitInfo); ExitInfo handleOpsFileAccessError(SyncOpPtr syncOp, ExitInfo opsExitInfo); + ExitInfo handleOpsFileNotFound(SyncOpPtr syncOp, ExitInfo opsExitInfo); ExitInfo handleOpsAlreadyExistError(SyncOpPtr syncOp, ExitInfo opsExitInfo); ExitInfo removeDependentOps(SyncOpPtr syncOp); diff --git a/src/libsyncengine/propagation/operation_sorter/operationsorterworker.cpp b/src/libsyncengine/propagation/operation_sorter/operationsorterworker.cpp index 227eb8e5d..919fcdc5e 100644 --- a/src/libsyncengine/propagation/operation_sorter/operationsorterworker.cpp +++ b/src/libsyncengine/propagation/operation_sorter/operationsorterworker.cpp @@ -41,8 +41,8 @@ void OperationSorterWorker::execute() { std::chrono::duration elapsed_seconds = std::chrono::steady_clock::now() - start; LOG_SYNCPAL_INFO(_logger, "Operation sorting finished in: " << elapsed_seconds.count() << "s"); - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } ExitCode OperationSorterWorker::sortOperations() { diff --git a/src/libsyncengine/reconciliation/conflict_finder/conflictfinderworker.cpp b/src/libsyncengine/reconciliation/conflict_finder/conflictfinderworker.cpp index 4b5a83b20..bb93ba6e9 100644 --- a/src/libsyncengine/reconciliation/conflict_finder/conflictfinderworker.cpp +++ b/src/libsyncengine/reconciliation/conflict_finder/conflictfinderworker.cpp @@ -33,9 +33,8 @@ void ConflictFinderWorker::execute() { findConflicts(); exitCode = ExitCode::Ok; - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); - return; + setDone(exitCode); } void ConflictFinderWorker::findConflicts() { diff --git a/src/libsyncengine/reconciliation/conflict_resolver/conflictresolverworker.cpp b/src/libsyncengine/reconciliation/conflict_resolver/conflictresolverworker.cpp index aba184557..f0f1158d2 100644 --- a/src/libsyncengine/reconciliation/conflict_resolver/conflictresolverworker.cpp +++ b/src/libsyncengine/reconciliation/conflict_resolver/conflictresolverworker.cpp @@ -50,8 +50,8 @@ void ConflictResolverWorker::execute() { // The sync must be restarted after the execution of the operations that resolve the conflict _syncPal->setRestart(true); - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } ExitCode ConflictResolverWorker::generateOperations(const Conflict &conflict, bool &continueSolving) { diff --git a/src/libsyncengine/reconciliation/operation_generator/operationgeneratorworker.cpp b/src/libsyncengine/reconciliation/operation_generator/operationgeneratorworker.cpp index b87c212c4..5f5371ee7 100644 --- a/src/libsyncengine/reconciliation/operation_generator/operationgeneratorworker.cpp +++ b/src/libsyncengine/reconciliation/operation_generator/operationgeneratorworker.cpp @@ -129,8 +129,8 @@ void OperationGeneratorWorker::execute() { } } - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } void OperationGeneratorWorker::generateCreateOperation(std::shared_ptr currentNode, diff --git a/src/libsyncengine/reconciliation/platform_inconsistency_checker/platforminconsistencycheckerworker.cpp b/src/libsyncengine/reconciliation/platform_inconsistency_checker/platforminconsistencycheckerworker.cpp index 1784a70c0..86f15371a 100644 --- a/src/libsyncengine/reconciliation/platform_inconsistency_checker/platforminconsistencycheckerworker.cpp +++ b/src/libsyncengine/reconciliation/platform_inconsistency_checker/platforminconsistencycheckerworker.cpp @@ -41,10 +41,10 @@ void PlatformInconsistencyCheckerWorker::execute() { for (const auto &idItem: _idsToBeRemoved) { if (!idItem.remoteId.empty() && !_syncPal->updateTree(ReplicaSide::Remote)->deleteNode(idItem.remoteId)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: node id=" << Utility::s2ws(idItem.remoteId.c_str())); + LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: node id=" << Utility::s2ws(idItem.remoteId)); } if (!idItem.localId.empty() && !_syncPal->updateTree(ReplicaSide::Local)->deleteNode(idItem.localId)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: node id=" << Utility::s2ws(idItem.localId.c_str())); + LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: node id=" << Utility::s2ws(idItem.localId)); } } @@ -53,8 +53,8 @@ void PlatformInconsistencyCheckerWorker::execute() { _syncPal->updateTree(ReplicaSide::Remote)->setInconsistencyCheckDone(); - setDone(ExitCode::Ok); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(ExitCode::Ok); } ExitCode PlatformInconsistencyCheckerWorker::checkTree(ReplicaSide side) { @@ -291,9 +291,9 @@ void PlatformInconsistencyCheckerWorker::removeLocalNodeFromDb(std::shared_ptrgetPath(); } +NodeType SyncOperation::nodeType() const noexcept { + return _affectedNode ? _affectedNode->type() : NodeType::Unknown; +} + bool SyncOperation::operator==(const SyncOperation &other) const { return _id == other.id(); } diff --git a/src/libsyncengine/reconciliation/syncoperation.h b/src/libsyncengine/reconciliation/syncoperation.h index 8782d7c8b..ad3992f5f 100644 --- a/src/libsyncengine/reconciliation/syncoperation.h +++ b/src/libsyncengine/reconciliation/syncoperation.h @@ -51,6 +51,7 @@ class SyncOperation { [[nodiscard]] SyncName nodeName(ReplicaSide side) const; [[nodiscard]] SyncPath nodePath(ReplicaSide side) const; + [[nodiscard]] NodeType nodeType() const noexcept; bool operator==(const SyncOperation &other) const; diff --git a/src/libsyncengine/requests/serverrequests.cpp b/src/libsyncengine/requests/serverrequests.cpp index 137365795..6ed0c7113 100644 --- a/src/libsyncengine/requests/serverrequests.cpp +++ b/src/libsyncengine/requests/serverrequests.cpp @@ -1115,7 +1115,7 @@ ExitCode ServerRequests::getPublicLinkUrl(int driveDbId, const QString &fileId, return ExitCode::Ok; } -ExitCode ServerRequests::getFolderSize(int userDbId, int driveId, const NodeId &nodeId, +ExitInfo ServerRequests::getFolderSize(int userDbId, int driveId, const NodeId &nodeId, std::function callback) { if (nodeId.empty()) { LOG_WARN(Log::instance()->getLogger(), "Node ID is empty"); diff --git a/src/libsyncengine/requests/serverrequests.h b/src/libsyncengine/requests/serverrequests.h index f6c03d105..a338eddfb 100644 --- a/src/libsyncengine/requests/serverrequests.h +++ b/src/libsyncengine/requests/serverrequests.h @@ -99,7 +99,7 @@ struct SYNCENGINE_EXPORT ServerRequests { static ExitCode getSubFolders(int driveDbId, const QString &nodeId, QList &list, bool withPath = false); static ExitCode createDir(int driveDbId, const QString &parentNodeId, const QString &dirName, QString &newNodeId); static ExitCode getPublicLinkUrl(int driveDbId, const QString &fileId, QString &linkUrl); - static ExitCode getFolderSize(int userDbId, int driveId, const NodeId &nodeId, + static ExitInfo getFolderSize(int userDbId, int driveId, const NodeId &nodeId, std::function callback); static ExitCode getNodeIdByPath(int userDbId, int driveId, const SyncPath &path, QString &nodeId); static ExitCode getPathByNodeId(int userDbId, int driveId, const QString &nodeId, QString &path); diff --git a/src/libsyncengine/syncpal/isyncworker.cpp b/src/libsyncengine/syncpal/isyncworker.cpp index 638bce75f..054888f55 100644 --- a/src/libsyncengine/syncpal/isyncworker.cpp +++ b/src/libsyncengine/syncpal/isyncworker.cpp @@ -28,15 +28,11 @@ ISyncWorker::ISyncWorker(std::shared_ptr syncPal, const std::string &na ISyncWorker::~ISyncWorker() { if (_isRunning) { - stop(); - } - - if (_thread && _thread->joinable()) { - _thread->join(); - _thread = nullptr; + ISyncWorker::stop(); } LOG_SYNCPAL_DEBUG(_logger, "Worker " << _name.c_str() << " destroyed"); + log4cplus::threadCleanup(); } void ISyncWorker::start() { @@ -47,12 +43,6 @@ void ISyncWorker::start() { LOG_SYNCPAL_DEBUG(_logger, "Worker " << _name.c_str() << " start"); - if (_thread && _thread->joinable()) { - _thread->join(); - _thread.release(); - _thread = nullptr; - } - _stopAsked = false; _isRunning = true; _exitCause = ExitCause::Unknown; @@ -114,14 +104,11 @@ void ISyncWorker::stop() { } void ISyncWorker::waitForExit() { - if (!_isRunning) { - LOG_SYNCPAL_DEBUG(_logger, "Worker " << _name.c_str() << " is not running"); - return; - } - - _thread->join(); + LOG_SYNCPAL_DEBUG(_logger, "Worker " << _name.c_str() << " wait for exit"); - _isRunning = false; + if (_thread && _thread->joinable()) { + _thread->join(); + } } void ISyncWorker::setPauseDone() { @@ -150,11 +137,12 @@ void ISyncWorker::setDone(ExitCode exitCode) { _isRunning = false; _stopAsked = false; _exitCode = exitCode; + log4cplus::threadCleanup(); } -void *ISyncWorker::executeFunc(void *thisWorker) { +void ISyncWorker::executeFunc(void *thisWorker) { ((ISyncWorker *) thisWorker)->execute(); - return nullptr; + log4cplus::threadCleanup(); } } // namespace KDC diff --git a/src/libsyncengine/syncpal/isyncworker.h b/src/libsyncengine/syncpal/isyncworker.h index b3830dcb4..70e703898 100644 --- a/src/libsyncengine/syncpal/isyncworker.h +++ b/src/libsyncengine/syncpal/isyncworker.h @@ -20,6 +20,7 @@ #include "syncpal.h" #include "libcommon/utility/types.h" +#include "libcommon/utility/utility.h" #include @@ -73,7 +74,7 @@ class ISyncWorker { inline int syncDbId() const { return _syncPal ? _syncPal->syncDbId() : -1; } private: - static void *executeFunc(void *thisWorker); + static void executeFunc(void *thisWorker); const std::string _name; const std::string _shortName; diff --git a/src/libsyncengine/syncpal/operationprocessor.cpp b/src/libsyncengine/syncpal/operationprocessor.cpp index f0644e24d..62224bbc0 100644 --- a/src/libsyncengine/syncpal/operationprocessor.cpp +++ b/src/libsyncengine/syncpal/operationprocessor.cpp @@ -31,8 +31,8 @@ bool OperationProcessor::isPseudoConflict(std::shared_ptr node, std::share return false; } - std::shared_ptr snapshot = _syncPal->snapshot(node->side(), true); - std::shared_ptr otherSnapshot = _syncPal->snapshot(correspondingNode->side(), true); + std::shared_ptr snapshot = _syncPal->snapshotCopy(node->side()); + std::shared_ptr otherSnapshot = _syncPal->snapshotCopy(correspondingNode->side()); // Create-Create pseudo-conflict if (node->hasChangeEvent(OperationType::Create) && correspondingNode->hasChangeEvent(OperationType::Create) && diff --git a/src/libsyncengine/syncpal/operationprocessor.h b/src/libsyncengine/syncpal/operationprocessor.h index bcd54b033..8724e03c5 100644 --- a/src/libsyncengine/syncpal/operationprocessor.h +++ b/src/libsyncengine/syncpal/operationprocessor.h @@ -36,7 +36,7 @@ class OperationProcessor : public ISyncWorker { * @param node a shared pointer to the node in current tree. * @return a shared pointer to the node in other tree. nullptr il not found. */ - std::shared_ptr correspondingNodeInOtherTree(std::shared_ptr node); + virtual std::shared_ptr correspondingNodeInOtherTree(std::shared_ptr node); /** * Find the corresponding node in other tree. * Looks in DB only. diff --git a/src/libsyncengine/syncpal/syncpal.cpp b/src/libsyncengine/syncpal/syncpal.cpp index 8c8ac86a2..75f47859a 100644 --- a/src/libsyncengine/syncpal/syncpal.cpp +++ b/src/libsyncengine/syncpal/syncpal.cpp @@ -48,6 +48,7 @@ #include "jobs/jobmanager.h" #include "libcommon/utility/utility.h" #include "libcommonserver/utility/utility.h" +#include "libcommonserver/io/iohelper.h" #include "tmpblacklistmanager.h" #define SYNCPAL_NEW_ERROR_MSG "Failed to create SyncPal instance!" @@ -62,8 +63,6 @@ SyncPal::SyncPal(const SyncPath &syncDbPath, const std::string &version, const b if (!createOrOpenDb(syncDbPath, version)) { throw std::runtime_error(SYNCPAL_NEW_ERROR_MSG); } - - createSharedObjects(); } SyncPal::SyncPal(const int syncDbId_, const std::string &version) : _logger(Log::instance()->getLogger()) { @@ -166,8 +165,6 @@ SyncPal::SyncPal(const int syncDbId_, const std::string &version) : _logger(Log: fixInconsistentFileNames(); fixNodeTableDeleteItemsWithNullParentNodeId(); - - createSharedObjects(); } SyncPal::~SyncPal() { @@ -512,7 +509,8 @@ void SyncPal::loadProgress(int64_t ¤tFile, int64_t &totalFiles, int64_t &c } void SyncPal::createSharedObjects() { - // Create shared objects + LOG_SYNCPAL_DEBUG(_logger, "Create shared objects"); + _interruptSync = std::make_shared(false); _localSnapshot = std::make_shared(ReplicaSide::Local, _syncDb->rootNode()); _remoteSnapshot = std::make_shared(ReplicaSide::Remote, _syncDb->rootNode()); _localSnapshotCopy = std::make_shared(ReplicaSide::Local, _syncDb->rootNode()); @@ -523,26 +521,66 @@ void SyncPal::createSharedObjects() { _remoteUpdateTree = std::make_shared(ReplicaSide::Remote, _syncDb->rootNode()); _conflictQueue = std::make_shared(_localUpdateTree, _remoteUpdateTree); _syncOps = std::make_shared(); + _progressInfo = std::make_shared(shared_from_this()); - // Init SyncNode table cache - SyncNodeCache::instance()->initCache(syncDbId(), _syncDb); + initSharedObjects(); +} + +void SyncPal::freeSharedObjects() { + LOG_SYNCPAL_DEBUG(_logger, "Free shared objects"); + _interruptSync.reset(); + _localSnapshot.reset(); + _remoteSnapshot.reset(); + _localSnapshotCopy.reset(); + _remoteSnapshotCopy.reset(); + _localOperationSet.reset(); + _remoteOperationSet.reset(); + _localUpdateTree.reset(); + _remoteUpdateTree.reset(); + _conflictQueue.reset(); + _syncOps.reset(); + _progressInfo.reset(); + + // Check that there is no memory leak + ASSERT(_interruptSync.use_count() == 0); + ASSERT(_localSnapshot.use_count() == 0); + ASSERT(_remoteSnapshot.use_count() == 0); + ASSERT(_localSnapshotCopy.use_count() == 0); + ASSERT(_remoteSnapshotCopy.use_count() == 0); + ASSERT(_localOperationSet.use_count() == 0); + ASSERT(_remoteOperationSet.use_count() == 0); + ASSERT(_localUpdateTree.use_count() == 0); + ASSERT(_remoteUpdateTree.use_count() == 0); + ASSERT(_conflictQueue.use_count() == 0); + ASSERT(_syncOps.use_count() == 0); + ASSERT(_progressInfo.use_count() == 0); +} + +void SyncPal::initSharedObjects() { + LOG_SYNCPAL_DEBUG(_logger, "Init shared objects"); + if (_localUpdateTree) _localUpdateTree->init(); + if (_remoteUpdateTree) _remoteUpdateTree->init(); + + setSyncHasFullyCompleted(false); } void SyncPal::resetSharedObjects() { LOG_SYNCPAL_DEBUG(_logger, "Reset shared objects"); - _localOperationSet->clear(); - _remoteOperationSet->clear(); - _localUpdateTree->init(); - _remoteUpdateTree->init(); - _conflictQueue->clear(); - _syncOps->clear(); - setSyncHasFullyCompleted(false); + if (_localOperationSet) _localOperationSet->clear(); + if (_remoteOperationSet) _remoteOperationSet->clear(); + if (_localUpdateTree) _localUpdateTree->clear(); + if (_remoteUpdateTree) _remoteUpdateTree->clear(); + if (_conflictQueue) _conflictQueue->clear(); + if (_syncOps) _syncOps->clear(); + + initSharedObjects(); LOG_SYNCPAL_DEBUG(_logger, "Reset shared objects done"); } void SyncPal::createWorkers() { + LOG_SYNCPAL_DEBUG(_logger, "Create workers"); #if defined(_WIN32) _localFSObserverWorker = std::shared_ptr( new LocalFileSystemObserverWorker_win(shared_from_this(), "Local File System Observer", "LFSO")); @@ -574,7 +612,8 @@ void SyncPal::createWorkers() { _tmpBlacklistManager = std::shared_ptr(new TmpBlacklistManager(shared_from_this())); } -void SyncPal::free() { +void SyncPal::freeWorkers() { + LOG_SYNCPAL_DEBUG(_logger, "Free workers"); _localFSObserverWorker.reset(); _remoteFSObserverWorker.reset(); _computeFSOperationsWorker.reset(); @@ -588,31 +627,6 @@ void SyncPal::free() { _executorWorker.reset(); _syncPalWorker.reset(); _tmpBlacklistManager.reset(); - - _interruptSync.reset(); - _localSnapshot.reset(); - _remoteSnapshot.reset(); - _localSnapshotCopy.reset(); - _remoteSnapshotCopy.reset(); - _localOperationSet.reset(); - _remoteOperationSet.reset(); - _localUpdateTree.reset(); - _remoteUpdateTree.reset(); - _conflictQueue.reset(); - _syncOps.reset(); - - // Check that there is no memory leak - ASSERT(_interruptSync.use_count() == 0); - ASSERT(_localSnapshot.use_count() == 0); - ASSERT(_remoteSnapshot.use_count() == 0); - ASSERT(_localSnapshotCopy.use_count() == 0); - ASSERT(_remoteSnapshotCopy.use_count() == 0); - ASSERT(_localOperationSet.use_count() == 0); - ASSERT(_remoteOperationSet.use_count() == 0); - ASSERT(_localUpdateTree.use_count() == 0); - ASSERT(_remoteUpdateTree.use_count() == 0); - ASSERT(_conflictQueue.use_count() == 0); - ASSERT(_syncOps.use_count() == 0); } ExitCode SyncPal::setSyncPaused(bool value) { @@ -644,6 +658,9 @@ bool SyncPal::createOrOpenDb(const SyncPath &syncDbPath, const std::string &vers return false; } + // Init SyncNode table cache + SyncNodeCache::instance()->initCache(syncDbId(), _syncDb); + return true; } @@ -943,8 +960,8 @@ ExitCode SyncPal::updateSyncNode(SyncNodeType syncNodeType) { auto nodeIdIt = nodeIdSet.begin(); while (nodeIdIt != nodeIdSet.end()) { - const bool ok = syncNodeType == SyncNodeType::TmpLocalBlacklist ? snapshot(ReplicaSide::Local, true)->exists(*nodeIdIt) - : snapshot(ReplicaSide::Remote, true)->exists(*nodeIdIt); + const bool ok = syncNodeType == SyncNodeType::TmpLocalBlacklist ? snapshotCopy(ReplicaSide::Local)->exists(*nodeIdIt) + : snapshotCopy(ReplicaSide::Remote)->exists(*nodeIdIt); if (!ok) { nodeIdIt = nodeIdSet.erase(nodeIdIt); } else { @@ -1193,15 +1210,12 @@ void SyncPal::start() { } setVfsMode(sync.virtualFileMode()); - // Reset shared objects - resetSharedObjects(); - // Clear tmp blacklist SyncNodeCache::instance()->update(syncDbId(), SyncNodeType::TmpRemoteBlacklist, std::unordered_set()); SyncNodeCache::instance()->update(syncDbId(), SyncNodeType::TmpLocalBlacklist, std::unordered_set()); - // Create ProgressInfo - createProgressInfo(); + // Create and init shared objects + createSharedObjects(); // Create workers createWorkers(); @@ -1281,13 +1295,11 @@ void SyncPal::stop(bool pausedByUser, bool quit, bool clear) { } } - if (quit) { - // Free workers - free(); + // Free workers + freeWorkers(); - // Free progressInfo - _progressInfo.reset(); - } + // Free shared objects + freeSharedObjects(); _syncDb->setAutoDelete(clear); } @@ -1459,33 +1471,44 @@ ExitInfo SyncPal::handleAccessDeniedItem(const SyncPath &relativePath, ExitCause NodeId localNodeId; if (localNodeId = snapshot(ReplicaSide::Local)->itemId(relativePath); localNodeId.empty()) { - // The file does not exit yet on local file system, or we do not have sufficient right on a parent folder. - LOGW_DEBUG(_logger, L"Item " << Utility::formatSyncPath(relativePath) - << L"is not present local file system, blacklisting the parent item."); - return handleAccessDeniedItem(relativePath.parent_path(), cause); + SyncPath absolutePath = localPath() / relativePath; + if (!IoHelper::getNodeId(absolutePath, localNodeId)) { + bool exists = false; + IoError ioError = IoError::Success; + if (!IoHelper::checkIfPathExists(absolutePath, exists, ioError)) { + LOGW_WARN(_logger, L"IoHelper::checkIfPathExists failed with: " << Utility::formatIoError(absolutePath, ioError)); + return ExitCode::SystemError; + } + if (ioError == IoError::AccessDenied) { // A parent of the file does not have sufficient right + LOGW_DEBUG(_logger, L"A parent of " << Utility::formatSyncPath(relativePath) + << L"does not have sufficient right, blacklisting the parent item."); + return handleAccessDeniedItem(relativePath.parent_path(), cause); + } + } } LOGW_SYNCPAL_DEBUG(_logger, L"Item " << Utility::formatSyncPath(relativePath) << L" (NodeId: " << Utility::s2ws(localNodeId) << L" is blacklisted temporarily because of a denied access."); - NodeId correspondingNodeId; - correspondingNodeId = snapshot(ReplicaSide::Remote)->itemId(relativePath); - if (bool found; correspondingNodeId.empty() && - !_syncDb->correspondingNodeId(ReplicaSide::Local, localNodeId, correspondingNodeId, found)) { + NodeId remoteNodeId = snapshot(ReplicaSide::Remote)->itemId(relativePath); + if (bool found; remoteNodeId.empty() && !localNodeId.empty() && + !_syncDb->correspondingNodeId(ReplicaSide::Local, localNodeId, remoteNodeId, found)) { LOG_SYNCPAL_WARN(_logger, "Error in SyncDb::correspondingNodeId"); return {ExitCode::DbError, ExitCause::Unknown}; } // Blacklist the item - _tmpBlacklistManager->blacklistItem(localNodeId, relativePath, ReplicaSide::Local); - if (!updateTree(ReplicaSide::Local)->deleteNode(localNodeId)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: " << Utility::formatSyncPath(relativePath)); + if (!localNodeId.empty()) { + _tmpBlacklistManager->blacklistItem(localNodeId, relativePath, ReplicaSide::Local); + if (!updateTree(ReplicaSide::Local)->deleteNode(localNodeId)) { + LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: " << Utility::formatSyncPath(relativePath)); + } } - if (!correspondingNodeId.empty()) { - _tmpBlacklistManager->blacklistItem(correspondingNodeId, relativePath, ReplicaSide::Remote); - if (!updateTree(ReplicaSide::Remote)->deleteNode(correspondingNodeId)) { + if (!remoteNodeId.empty()) { + _tmpBlacklistManager->blacklistItem(remoteNodeId, relativePath, ReplicaSide::Remote); + if (!updateTree(ReplicaSide::Remote)->deleteNode(remoteNodeId)) { LOGW_SYNCPAL_WARN(_logger, L"Error in UpdateTree::deleteNode: " << Utility::formatSyncPath(relativePath)); } } diff --git a/src/libsyncengine/syncpal/syncpal.h b/src/libsyncengine/syncpal/syncpal.h index 2a23b2d7a..29069f6cc 100644 --- a/src/libsyncengine/syncpal/syncpal.h +++ b/src/libsyncengine/syncpal/syncpal.h @@ -307,7 +307,7 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this { std::shared_ptr _syncDb{nullptr}; // Shared objects - std::shared_ptr _interruptSync{new bool(false)}; + std::shared_ptr _interruptSync{nullptr}; std::shared_ptr _localSnapshot{nullptr}; // Real time local snapshot std::shared_ptr _remoteSnapshot{nullptr}; // Real time remote snapshot std::shared_ptr _localSnapshotCopy{ @@ -320,6 +320,7 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this { std::shared_ptr _remoteUpdateTree{nullptr}; std::shared_ptr _conflictQueue{nullptr}; std::shared_ptr _syncOps{nullptr}; + std::shared_ptr _progressInfo{nullptr}; // Workers std::shared_ptr _syncPalWorker{nullptr}; @@ -335,14 +336,14 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this { std::shared_ptr _operationsSorterWorker{nullptr}; std::shared_ptr _executorWorker{nullptr}; - std::shared_ptr _progressInfo{nullptr}; - std::shared_ptr _tmpBlacklistManager{nullptr}; void createSharedObjects(); + void freeSharedObjects(); + void initSharedObjects(); void resetSharedObjects(); void createWorkers(); - void free(); + void freeWorkers(); ExitCode setSyncPaused(bool value); bool createOrOpenDb(const SyncPath &syncDbPath, const std::string &version, const std::string &targetNodeId = std::string()); @@ -353,7 +354,7 @@ class SYNCENGINE_EXPORT SyncPal : public std::enable_shared_from_this { ExitCode updateSyncNode(SyncNodeType syncNodeType); ExitCode updateSyncNode(); std::shared_ptr snapshot(ReplicaSide side, bool copy = false) const; - const std::shared_ptr snapshotCopy(ReplicaSide side) { return snapshot(side, true); }; + const std::shared_ptr snapshotCopy(ReplicaSide side) { return snapshot(side, true); } std::shared_ptr operationSet(ReplicaSide side) const; std::shared_ptr updateTree(ReplicaSide side) const; diff --git a/src/libsyncengine/syncpal/syncpalworker.cpp b/src/libsyncengine/syncpal/syncpalworker.cpp index fdbb0c580..a32370bed 100644 --- a/src/libsyncengine/syncpal/syncpalworker.cpp +++ b/src/libsyncengine/syncpal/syncpalworker.cpp @@ -51,6 +51,13 @@ void SyncPalWorker::execute() { LOG_SYNCPAL_WARN(_logger, "Error in resetVfsFilesStatus for syncDbId=" << _syncPal->syncDbId()); } + // Manage stop + if (stopAsked()) { + // Exit + exitCode = ExitCode::Ok; + setDone(exitCode); + return; + } if (_syncPal->vfsMode() == VirtualFileMode::Mac) { // Reset nodes syncing flag if (!_syncPal->_syncDb->updateNodesSyncing(false)) { @@ -74,7 +81,7 @@ void SyncPalWorker::execute() { // Pause sync LOG_SYNCPAL_DEBUG(_logger, "Stop FSO worker " << index); isFSOInProgress[index] = false; - fsoWorkers[index]->stop(); + stopAndWaitForExitOfWorker(fsoWorkers[index]); pause(); } else { // Start worker @@ -136,6 +143,7 @@ void SyncPalWorker::execute() { SyncStep step = nextStep(); if (step != _step) { LOG_SYNCPAL_INFO(_logger, "***** Step " << stepName(_step).c_str() << " has finished"); + waitForExitOfWorkers(stepWorkers); initStep(step, stepWorkers, inputSharedObject); isStepInProgress = false; } @@ -220,8 +228,8 @@ void SyncPalWorker::execute() { Utility::msleep(LOOP_EXEC_SLEEP_PERIOD); } - setDone(exitCode); LOG_SYNCPAL_INFO(_logger, "Worker " << name().c_str() << " stoped"); + setDone(exitCode); } std::string SyncPalWorker::stepName(SyncStep step) { @@ -287,12 +295,12 @@ void SyncPalWorker::initStep(SyncStep step, std::shared_ptr (&worke workers[0] = _syncPal->computeFSOperationsWorker(); workers[1] = nullptr; _syncPal->copySnapshots(); - assert(_syncPal->snapshot(ReplicaSide::Local, true)->checkIntegrityRecursively() && + assert(_syncPal->snapshotCopy(ReplicaSide::Local)->checkIntegrityRecursively() && "Local snapshot is corrupted, see logs for details"); - assert(_syncPal->snapshot(ReplicaSide::Remote, true)->checkIntegrityRecursively() && + assert(_syncPal->snapshotCopy(ReplicaSide::Remote)->checkIntegrityRecursively() && "Remote snapshot is corrupted, see logs for details"); - inputSharedObject[0] = _syncPal->snapshot(ReplicaSide::Local, true); - inputSharedObject[1] = _syncPal->snapshot(ReplicaSide::Remote, true); + inputSharedObject[0] = nullptr; + inputSharedObject[1] = nullptr; _syncPal->setRestart(false); break; case SyncStep::UpdateDetection2: @@ -463,6 +471,11 @@ SyncStep SyncPalWorker::nextStep() const { } } +void SyncPalWorker::stopAndWaitForExitOfWorker(std::shared_ptr worker) { + worker->stop(); + worker->waitForExit(); +} + void SyncPalWorker::stopWorkers(std::shared_ptr workers[2]) { for (int index = 0; index < 2; index++) { if (workers[index]) { @@ -536,6 +549,9 @@ bool SyncPalWorker::resetVfsFilesStatus() { return false; } for (; dirIt != std::filesystem::recursive_directory_iterator(); ++dirIt) { + if (stopAsked()) { + return true; + } #ifdef _WIN32 // skip_permission_denied doesn't work on Windows try { diff --git a/src/libsyncengine/syncpal/syncpalworker.h b/src/libsyncengine/syncpal/syncpalworker.h index 9e00b2cfd..4eaf02270 100644 --- a/src/libsyncengine/syncpal/syncpalworker.h +++ b/src/libsyncengine/syncpal/syncpalworker.h @@ -30,7 +30,7 @@ class SyncPalWorker : public ISyncWorker { public: SyncPalWorker(std::shared_ptr syncPal, const std::string &name, const std::string &shortName); - void execute(); + void execute() override; inline SyncStep step() const { return _step; } inline std::chrono::time_point pauseTime() const { return _pauseTime; } static std::string stepName(SyncStep step); @@ -45,6 +45,7 @@ class SyncPalWorker : public ISyncWorker { bool reset); bool interruptCondition() const; SyncStep nextStep() const; + void stopAndWaitForExitOfWorker(std::shared_ptr worker); void stopWorkers(std::shared_ptr workers[2]); void waitForExitOfWorkers(std::shared_ptr workers[2]); void stopAndWaitForExitOfWorkers(std::shared_ptr workers[2]); diff --git a/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.cpp b/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.cpp index 9b32bda16..c68b57626 100644 --- a/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.cpp @@ -101,8 +101,8 @@ void ContentChecksumWorker::execute() { Utility::msleep(10); } - setDone(exitCode); LOG_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } } // namespace KDC diff --git a/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.h b/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.h index 5f9688738..47048c559 100644 --- a/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.h +++ b/src/libsyncengine/update_detection/file_system_observer/checksum/contentchecksumworker.h @@ -44,7 +44,7 @@ class ContentChecksumWorker : public ISyncWorker { static void callback(UniqueId jobId); protected: - virtual void execute() override; + void execute() override; private: std::shared_ptr _localSnapshot; diff --git a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp index fa80ffdae..8f6749146 100644 --- a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.cpp @@ -29,15 +29,11 @@ namespace KDC { ComputeFSOperationWorker::ComputeFSOperationWorker(std::shared_ptr syncPal, const std::string &name, const std::string &shortName) : - ISyncWorker(syncPal, name, shortName), _syncDb(syncPal->_syncDb), _localSnapshot(syncPal->_localSnapshot), - _remoteSnapshot(syncPal->_remoteSnapshot) {} + ISyncWorker(syncPal, name, shortName), _syncDb(syncPal->syncDb()) {} -ComputeFSOperationWorker::ComputeFSOperationWorker(const std::shared_ptr testSyncDb, - const std::shared_ptr testLocalSnapshot, - const std::shared_ptr testRemoteSnapshot, const std::string &name, +ComputeFSOperationWorker::ComputeFSOperationWorker(const std::shared_ptr testSyncDb, const std::string &name, const std::string &shortName) : - ISyncWorker(nullptr, name, shortName, true), _syncDb(testSyncDb), _localSnapshot(testLocalSnapshot), - _remoteSnapshot(testRemoteSnapshot) {} + ISyncWorker(nullptr, name, shortName, true), _syncDb(testSyncDb) {} void ComputeFSOperationWorker::execute() { ExitCode exitCode(ExitCode::Unknown); @@ -60,8 +56,8 @@ void ComputeFSOperationWorker::execute() { ok = false; } if (!ok) { - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); return; } @@ -102,8 +98,8 @@ void ComputeFSOperationWorker::execute() { std::chrono::duration elapsed_seconds = std::chrono::steady_clock::now() - start; LOG_SYNCPAL_INFO(_logger, "FS operation sets generated in: " << elapsed_seconds.count() << "s"); - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } @@ -250,9 +246,13 @@ ExitCode ComputeFSOperationWorker::inferChangeFromDbNode(const ReplicaSide side, } SyncPath snapshotPath; - if (!snapshot->path(nodeId, snapshotPath)) { - LOGW_SYNCPAL_WARN(_logger, L"Failed to retrieve path from snapshot for item " << SyncName2WStr(dbName) << L" (" - << Utility::s2ws(nodeId) << L")"); + if (bool ignore = false; !snapshot->path(nodeId, snapshotPath, ignore)) { + if (ignore) { + notifyIgnoredItem(nodeId, snapshotPath, dbNode.type()); + return ExitCode::Ok; + } + LOGW_SYNCPAL_WARN(_logger, L"Failed to retrieve path from snapshot for item " << SyncName2WStr(dbName).c_str() << L" (" + << Utility::s2ws(nodeId).c_str() << L")"); setExitCause(ExitCause::InvalidSnapshot); return ExitCode::DataError; } @@ -477,8 +477,13 @@ ExitCode ComputeFSOperationWorker::exploreSnapshotTree(ReplicaSide side, const N } SyncPath snapshotPath; - if (!snapshot->path(nodeId, snapshotPath)) { - LOGW_SYNCPAL_WARN(_logger, L"Failed to retrieve path from snapshot for item " << Utility::s2ws(nodeId)); + if (bool ignore = false; !snapshot->path(nodeId, snapshotPath, ignore)) { + if (ignore) { + notifyIgnoredItem(nodeId, snapshotPath, type); + continue; + } + + LOG_SYNCPAL_WARN(_logger, "Failed to retrieve path from snapshot for item " << nodeId.c_str()); setExitCause(ExitCause::InvalidSnapshot); return ExitCode::DataError; } @@ -574,29 +579,35 @@ ExitCode ComputeFSOperationWorker::checkFileIntegrity(const DbNode &dbNode) { return ExitCode::Ok; } - if (!_syncPal->snapshot(ReplicaSide::Local, true)->exists(dbNode.nodeIdLocal().value()) || - !_syncPal->snapshot(ReplicaSide::Remote, true)->exists(dbNode.nodeIdRemote().value())) { + if (!_syncPal->snapshotCopy(ReplicaSide::Local)->exists(dbNode.nodeIdLocal().value()) || + !_syncPal->snapshotCopy(ReplicaSide::Remote)->exists(dbNode.nodeIdRemote().value())) { // Ignore if item does not exist return ExitCode::Ok; } - if (const bool localSnapshotIsLink = _syncPal->snapshot(ReplicaSide::Local, true)->isLink(dbNode.nodeIdLocal().value()); + if (const bool localSnapshotIsLink = _syncPal->snapshotCopy(ReplicaSide::Local)->isLink(dbNode.nodeIdLocal().value()); localSnapshotIsLink) { // Local and remote links sizes are not always the same (macOS aliases, Windows junctions) return ExitCode::Ok; } - int64_t localSnapshotSize = _syncPal->snapshot(ReplicaSide::Local, true)->size(dbNode.nodeIdLocal().value()); - int64_t remoteSnapshotSize = _syncPal->snapshot(ReplicaSide::Remote, true)->size(dbNode.nodeIdRemote().value()); - SyncTime localSnapshotLastModified = _syncPal->snapshot(ReplicaSide::Local, true)->lastModified(dbNode.nodeIdLocal().value()); + int64_t localSnapshotSize = _syncPal->snapshotCopy(ReplicaSide::Local)->size(dbNode.nodeIdLocal().value()); + int64_t remoteSnapshotSize = _syncPal->snapshotCopy(ReplicaSide::Remote)->size(dbNode.nodeIdRemote().value()); + SyncTime localSnapshotLastModified = _syncPal->snapshotCopy(ReplicaSide::Local)->lastModified(dbNode.nodeIdLocal().value()); SyncTime remoteSnapshotLastModified = - _syncPal->snapshot(ReplicaSide::Remote, true)->lastModified(dbNode.nodeIdRemote().value()); + _syncPal->snapshotCopy(ReplicaSide::Remote)->lastModified(dbNode.nodeIdRemote().value()); // A mismatch is detected if all timestamps are equal but the sizes in snapshots differ. if (localSnapshotSize != remoteSnapshotSize && localSnapshotLastModified == dbNode.lastModifiedLocal().value() && localSnapshotLastModified == remoteSnapshotLastModified) { SyncPath localSnapshotPath; - if (!_syncPal->snapshot(ReplicaSide::Local, true)->path(dbNode.nodeIdLocal().value(), localSnapshotPath)) { + if (bool ignore = false; + !_syncPal->snapshotCopy(ReplicaSide::Local)->path(dbNode.nodeIdLocal().value(), localSnapshotPath, ignore)) { + if (ignore) { + notifyIgnoredItem(dbNode.nodeIdLocal().value(), localSnapshotPath, dbNode.type()); + return ExitCode::Ok; + } + LOGW_SYNCPAL_WARN(_logger, L"Failed to retrieve path from snapshot for item " << SyncName2WStr(dbNode.nameLocal()) << L" (" << Utility::s2ws(dbNode.nodeIdLocal().value()) << L")"); @@ -757,7 +768,12 @@ bool ComputeFSOperationWorker::isTooBig(const std::shared_ptr re // On first sync after migration from version under 3.4.0, the DB is empty but a big folder might as been whitelisted // Therefor check also with path SyncPath relativePath; - if (remoteSnapshot->path(remoteNodeId, relativePath)) { + if (bool ignore = false; remoteSnapshot->path(remoteNodeId, relativePath, ignore)) { + if (ignore) { + notifyIgnoredItem(remoteNodeId, relativePath, remoteSnapshot->type(remoteNodeId)); + return false; + } + localNodeId = _syncPal->snapshotCopy(ReplicaSide::Local)->itemId(relativePath); if (!localNodeId.empty()) { // We already synchronize the item locally, keep it @@ -940,4 +956,10 @@ bool ComputeFSOperationWorker::checkIfPathIsInDeletedFolder(const SyncPath &path return true; } +void ComputeFSOperationWorker::notifyIgnoredItem(const NodeId &nodeId, const SyncPath &path, const NodeType nodeType) { + LOGW_SYNCPAL_INFO(_logger, L"Item (or one of its descendants) has been ignored: " << Utility::formatSyncPath(path)); + const Error err(_syncPal->syncDbId(), "", nodeId, nodeType, path, ConflictType::None, InconsistencyType::ReservedName); + _syncPal->addError(err); +} + } // namespace KDC diff --git a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.h b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.h index ee3f702ac..e613bc653 100644 --- a/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.h +++ b/src/libsyncengine/update_detection/file_system_observer/computefsoperationworker.h @@ -30,19 +30,15 @@ class ComputeFSOperationWorker : public ISyncWorker { /** * Constructor used for testing only * @param testSyncDb - * @param testLocalSnapshot - * @param testRemoteSnapshot * @param name * @param shortName */ - ComputeFSOperationWorker(const std::shared_ptr testSyncDb, const std::shared_ptr testLocalSnapshot, - const std::shared_ptr testRemoteSnapshot, const std::string &name, - const std::string &shortName); + ComputeFSOperationWorker(const std::shared_ptr testSyncDb, const std::string &name, const std::string &shortName); const std::unordered_map getFileSizeMismatchMap() const { return _fileSizeMismatchMap; } protected: - virtual void execute() override; + void execute() override; private: using NodeIdSet = std::unordered_set; @@ -77,9 +73,9 @@ class ComputeFSOperationWorker : public ISyncWorker { void logOperationGeneration(const ReplicaSide side, const FSOpPtr fsOp); + void notifyIgnoredItem(const NodeId &nodeId, const SyncPath &path, NodeType nodeType); + const std::shared_ptr _syncDb; - const std::shared_ptr _localSnapshot; - const std::shared_ptr _remoteSnapshot; Sync _sync; NodeIdSet _remoteUnsyncedList; diff --git a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h index 30137ec88..ff6da91b5 100644 --- a/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h +++ b/src/libsyncengine/update_detection/file_system_observer/filesystemobserverworker.h @@ -38,7 +38,7 @@ class FileSystemObserverWorker : public ISyncWorker { virtual void forceUpdate(); virtual inline bool updating() const { return _updating; } - std::shared_ptr snapshot() const { return _snapshot; }; + std::shared_ptr snapshot() const { return _snapshot; } protected: std::shared_ptr _syncDb; @@ -55,7 +55,6 @@ class FileSystemObserverWorker : public ISyncWorker { virtual bool isFolderWatcherReliable() const { return true; } private: - static void *executeFunc(void *thisWorker); virtual ReplicaSide getSnapshotType() const = 0; friend class TestLocalFileSystemObserverWorker; diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.cpp index d67d9c917..10a309ba7 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.cpp @@ -26,17 +26,11 @@ namespace KDC { FolderWatcher::FolderWatcher(LocalFileSystemObserverWorker *parent, const SyncPath &path) : _logger(Log::instance()->getLogger()), _parent(parent), _folder(path) {} -FolderWatcher::~FolderWatcher() { - if (_thread) { - stop(); - } -} - void FolderWatcher::start() { LOG_DEBUG(_logger, "Start Folder Watcher"); _stop = false; - _thread = std::unique_ptr(new std::thread(executeFunc, this)); + _thread = std::make_unique(executeFunc, this); #if defined(__APPLE__) _thread->detach(); @@ -52,16 +46,15 @@ void FolderWatcher::stop() { #if !defined(__APPLE__) if (_thread && _thread->joinable()) { _thread->join(); - _thread.release(); } #endif _thread = nullptr; } -void *FolderWatcher::executeFunc(void *thisWorker) { +void FolderWatcher::executeFunc(void *thisWorker) { ((FolderWatcher *) thisWorker)->startWatching(); - return nullptr; + log4cplus::threadCleanup(); } } // namespace KDC diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h index ee459df8e..7cd59ecb4 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher.h @@ -19,6 +19,7 @@ #pragma once #include "libcommon/utility/types.h" +#include "libcommon/utility/utility.h" #include "libcommonserver/log/log.h" #include @@ -31,7 +32,7 @@ class LocalFileSystemObserverWorker; class FolderWatcher { public: FolderWatcher(LocalFileSystemObserverWorker *parent, const SyncPath &rootFolder); - virtual ~FolderWatcher(); + virtual ~FolderWatcher() = default; const log4cplus::Logger &logger() const { return _logger; } @@ -54,7 +55,7 @@ class FolderWatcher { bool _stop = false; private: - static void *executeFunc(void *thisWorker); + static void executeFunc(void *thisWorker); std::unique_ptr _thread = nullptr; ExitInfo _exitInfo = ExitCode::Ok; diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp index cc3766595..8ae2d121b 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_linux.cpp @@ -37,10 +37,13 @@ namespace KDC { FolderWatcher_linux::FolderWatcher_linux(LocalFileSystemObserverWorker *parent, const SyncPath &path) : FolderWatcher(parent, path) {} -FolderWatcher_linux::~FolderWatcher_linux() {} +SyncPath FolderWatcher_linux::makeSyncPath(const SyncPath &watchedFolderPath, const char *fileName) { + const auto syncName = SyncName(fileName); + return syncName.empty() ? watchedFolderPath : watchedFolderPath / syncName; +} void FolderWatcher_linux::startWatching() { - LOG4CPLUS_DEBUG(_logger, L"Start watching folder: " << Path2WStr(_folder).c_str()); + LOG4CPLUS_DEBUG(_logger, L"Start watching folder " << Utility::formatSyncPath(_folder)); LOG4CPLUS_DEBUG(_logger, "File system format: " << Utility::fileSystemName(_folder).c_str()); _fileDescriptor = inotify_init(); @@ -88,16 +91,18 @@ void FolderWatcher_linux::startWatching() { } if (!skip && !_stop) { - if (_watchToPath.find(event->wd) != _watchToPath.end()) { - const SyncPath path = _watchToPath[event->wd] / SyncName(event->name); + if (_watchToPath.contains(event->wd)) { + // `event->name` is empty for instance if the event is a permission change on a watched + // directory (see inotify man page). + const SyncPath path = makeSyncPath(_watchToPath[event->wd], event->name); if (ParametersCache::isExtendedLogEnabled()) { - LOGW_DEBUG(_logger, L"Operation " << opType << L" detected on item with " - << Utility::formatSyncPath(path).c_str()); + LOGW_DEBUG(_logger, + L"Operation " << opType << L" detected on item with " << Utility::formatSyncPath(path)); } changeDetected(path, opType); bool isDirectory = false; - IoError ioError = IoError::Success; + auto ioError = IoError::Success; const bool isDirSuccess = IoHelper::checkIfIsDirectory(path, isDirectory, ioError); if (!isDirSuccess) { LOGW_WARN(_logger, L"Error in IoHelper::checkIfIsDirectory: " @@ -106,7 +111,7 @@ void FolderWatcher_linux::startWatching() { } if (ioError == IoError::AccessDenied) { - LOGW_WARN(_logger, L"The item misses search/exec permission - path=" << Path2WStr(path).c_str()); + LOGW_WARN(_logger, L"The item misses search/exec permission - " << Utility::formatSyncPath(path)); } if ((event->mask & (IN_MOVED_TO | IN_CREATE)) && isDirectory) { @@ -125,23 +130,25 @@ void FolderWatcher_linux::startWatching() { Utility::msleep(SLEEP_TIME); } } + + LOGW_DEBUG(_logger, L"Folder watching stopped: " << Utility::formatSyncPath(_folder)); } bool FolderWatcher_linux::findSubFolders(const SyncPath &dir, std::list &fullList) { bool ok = true; bool isReadable = access(dir.c_str(), R_OK) == 0; if (!isReadable) { - LOG4CPLUS_WARN(_logger, L"SyncDir is not readable: " << Utility::formatSyncPath(dir).c_str()); + LOG4CPLUS_WARN(_logger, L"SyncDir is not readable: " << Utility::formatSyncPath(dir)); setExitInfo({ExitCode::SystemError, ExitCause::SyncDirAccesError}); return false; } std::error_code ec; if (!(std::filesystem::exists(dir, ec) && isReadable)) { if (ec) { - LOG4CPLUS_WARN(_logger, L"Failed to check existence of " << Utility::formatSyncPath(dir).c_str() << L": " - << Utility::formatStdError(ec).c_str()); + LOG4CPLUS_WARN(_logger, L"Failed to check existence of " << Utility::formatSyncPath(dir) << L": " + << Utility::formatStdError(ec)); } else { - LOG4CPLUS_WARN(_logger, L"Non existing path coming in: " << Path2WStr(dir).c_str()); + LOG4CPLUS_WARN(_logger, L"Non existing path coming in: " << Utility::formatSyncPath(dir)); } ok = false; } else { @@ -150,7 +157,7 @@ bool FolderWatcher_linux::findSubFolders(const SyncPath &dir, std::list +struct inotify_event; + namespace KDC { class LocalFileSystemObserverWorker; @@ -29,7 +31,6 @@ class LocalFileSystemObserverWorker; class FolderWatcher_linux : public FolderWatcher { public: FolderWatcher_linux(LocalFileSystemObserverWorker *parent, const SyncPath &path); - ~FolderWatcher_linux(); void startWatching() override; void stopWatching() override; @@ -45,10 +46,15 @@ class FolderWatcher_linux : public FolderWatcher { bool addFolderRecursive(const SyncPath &path); void removeFoldersBelow(const SyncPath &dirPath); + void changeDetected(const SyncPath &path, OperationType opType); std::unordered_map _watchToPath; std::map _pathToWatch; + + static SyncPath makeSyncPath(const SyncPath &path, const char *name); + + friend class TestFolderWatcherLinux; }; } // namespace KDC diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp index 18fcb5989..0510779a2 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.cpp @@ -28,8 +28,6 @@ namespace KDC { FolderWatcher_mac::FolderWatcher_mac(LocalFileSystemObserverWorker *parent, const SyncPath &path) : FolderWatcher(parent, path), _stream(nullptr) {} -FolderWatcher_mac::~FolderWatcher_mac() {} - static void callback([[maybe_unused]] ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPathsVoid, const FSEventStreamEventFlags eventFlags[], [[maybe_unused]] const FSEventStreamEventId eventIds[]) { @@ -90,7 +88,7 @@ static void callback([[maybe_unused]] ConstFSEventStreamRef streamRef, void *cli } void FolderWatcher_mac::startWatching() { - LOGW_DEBUG(_logger, L"Start watching folder: " << Path2WStr(_folder).c_str()); + LOGW_DEBUG(_logger, L"Start watching folder: " << Utility::formatSyncPath(_folder)); LOG_DEBUG(_logger, "File system format: " << Utility::fileSystemName(_folder).c_str()); CFStringRef path = CFStringCreateWithCString(nullptr, _folder.c_str(), kCFStringEncodingUTF8); @@ -108,6 +106,8 @@ void FolderWatcher_mac::startWatching() { FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); FSEventStreamStart(_stream); CFRunLoopRun(); + + LOGW_DEBUG(_logger, L"Folder watching stopped: " << Utility::formatSyncPath(_folder)); } void FolderWatcher_mac::doNotifyParent(const std::list> &changes) { @@ -133,7 +133,7 @@ OperationType FolderWatcher_mac::getOpType(const FSEventStreamEventFlags eventFl void KDC::FolderWatcher_mac::stopWatching() { if (_stream) { - LOGW_DEBUG(_logger, L"Stop watching folder: " << Path2WStr(_folder).c_str()); + LOGW_DEBUG(_logger, L"Stop watching folder: " << Utility::formatSyncPath(_folder)); FSEventStreamStop(_stream); FSEventStreamInvalidate(_stream); FSEventStreamRelease(_stream); diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.h index 7a6d729e2..3db20b779 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.h +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_mac.h @@ -29,7 +29,6 @@ class LocalFileSystemObserverWorker; class FolderWatcher_mac : public FolderWatcher { public: FolderWatcher_mac(LocalFileSystemObserverWorker *parent, const SyncPath &path); - ~FolderWatcher_mac(); void startWatching() override; void stopWatching() override; diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp index 99da64c60..a4a93caeb 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.cpp @@ -30,8 +30,6 @@ namespace KDC { FolderWatcher_win::FolderWatcher_win(LocalFileSystemObserverWorker *parent, const SyncPath &path) : FolderWatcher(parent, path) {} -FolderWatcher_win::~FolderWatcher_win() {} - bool FolderWatcher_win::ready() const { return _ready; } @@ -118,8 +116,7 @@ void FolderWatcher_win::watchChanges() { _ready = true; HANDLE handles[] = {_resultEventHandle, _stopEventHandle}; - DWORD result = WaitForMultipleObjects(2, handles, false // awake once one of them arrives - , + DWORD result = WaitForMultipleObjects(2, handles, false, // awake once one of them arrives INFINITE); if (result == 1) { @@ -180,6 +177,8 @@ void FolderWatcher_win::watchChanges() { << L" detected on item with " << Utility::formatSyncPath(longfilepath)); } + if (opType == OperationType::MoveOut) opType = OperationType::Move; // "MoveOut" is considered as Move from now on + changeDetected(longfilepath, opType); } @@ -213,20 +212,15 @@ void FolderWatcher_win::closeHandle() { OperationType FolderWatcher_win::operationFromAction(DWORD action) { switch (action) { case FILE_ACTION_RENAMED_OLD_NAME: - return OperationType::Move; - break; + return OperationType::MoveOut; case FILE_ACTION_RENAMED_NEW_NAME: return OperationType::Move; - break; case FILE_ACTION_ADDED: return OperationType::Create; - break; case FILE_ACTION_REMOVED: return OperationType::Delete; - break; case FILE_ACTION_MODIFIED: return OperationType::Edit; - break; } return OperationType::None; diff --git a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h index ac44fa9ef..cc0537b8c 100644 --- a/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h +++ b/src/libsyncengine/update_detection/file_system_observer/folderwatcher_win.h @@ -32,7 +32,6 @@ class LocalFileSystemObserverWorker; class FolderWatcher_win : public FolderWatcher { public: FolderWatcher_win(LocalFileSystemObserverWorker *parent, const SyncPath &path); - ~FolderWatcher_win(); bool ready() const; diff --git a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp index 0bf20577a..acf47042f 100644 --- a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.cpp @@ -86,21 +86,45 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listlocalPath(), absolutePath); - // Check if exists with same nodeId + IoError ioError = IoError::Success; + bool exists = true; + +#ifdef __APPLE__ + if (opTypeFromOS == OperationType::Create) { + // Clear extended attributes of new items + if (!IoHelper::removeLiteSyncXAttrs(absolutePath, ioError)) { + LOGW_SYNCPAL_WARN(_logger, + L"Error in IoHelper::removeLiteSyncXAttrs: " << Utility::formatIoError(absolutePath, ioError)); + invalidateSnapshot(); + return; + } + + if (ioError == IoError::AccessDenied) { + LOGW_SYNCPAL_DEBUG(_logger, L"Item: " << Utility::formatSyncPath(absolutePath) << L" misses search permissions!"); + sendAccessDeniedError(absolutePath); + continue; + } else if (ioError == IoError::NoSuchFileOrDirectory) { + exists = false; + } + } +#endif + if (opTypeFromOS == OperationType::Delete) { + // Check if exists with same nodeId NodeId prevNodeId = _snapshot->itemId(relativePath); bool existsWithSameId = false; NodeId otherNodeId; - IoError ioError = IoError::Success; if (!prevNodeId.empty()) { if (_syncPal->isTmpBlacklisted(prevNodeId, ReplicaSide::Local)) _syncPal->removeItemFromTmpBlacklist(relativePath); - if (IoHelper::checkIfPathExistsWithSameNodeId(absolutePath, prevNodeId, existsWithSameId, otherNodeId, ioError) && + if (auto checkError = IoError::Success; + IoHelper::checkIfPathExistsWithSameNodeId(absolutePath, prevNodeId, existsWithSameId, otherNodeId, + checkError) && !existsWithSameId) { if (_snapshot->removeItem(prevNodeId)) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " - << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(prevNodeId).c_str() << L")"); + LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " << Utility::formatSyncPath(absolutePath) + << L" (" << Utility::s2ws(prevNodeId) + << L")"); } continue; } @@ -108,37 +132,37 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listisTmpBlacklisted(nodeId, ReplicaSide::Local)) { - _syncPal->removeItemFromTmpBlacklist(relativePath); - if (opTypeFromOS == OperationType::Edit) { - NodeId itemId = _snapshot->itemId(relativePath); - if (!itemId.empty()) _snapshot->setLastModified(itemId, 0); + if (_syncPal->isTmpBlacklisted(nodeId, ReplicaSide::Local)) { + _syncPal->removeItemFromTmpBlacklist(relativePath); + if (opTypeFromOS == OperationType::Edit) { + NodeId itemId = _snapshot->itemId(relativePath); + if (!itemId.empty()) _snapshot->setLastModified(itemId, 0); + } } - } - bool isLink = false; - if (exists) { ItemType itemType; if (!IoHelper::getItemType(absolutePath, itemType)) { LOGW_SYNCPAL_WARN(_logger, @@ -164,7 +188,7 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listremoveItem(itemId); - LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from sync because it's hidden: " - << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_DEBUG(_logger, + L"Item removed from sync because it is hidden: " << Utility::formatSyncPath(absolutePath)); } else { - LOGW_SYNCPAL_DEBUG(_logger, L"Item not processed because it's excluded: " - << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_DEBUG(_logger, + L"Item not processed because it is excluded: " << Utility::formatSyncPath(absolutePath)); } if (!_syncPal->vfsExclude( absolutePath)) { // TODO : This class should never set any attribute or change anything on a file - LOGW_SYNCPAL_WARN(_logger, L"Error in vfsExclude: " << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_WARN(_logger, L"Error in vfsExclude: " << Utility::formatSyncPath(absolutePath)); } continue; @@ -208,12 +232,11 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listremoveItemFromTmpBlacklist(itemId, ReplicaSide::Local); if (_snapshot->removeItem(itemId)) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " << Utility::formatSyncPath(absolutePath).c_str() - << L" (" << Utility::s2ws(itemId).c_str() - << L")"); + LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " << Utility::formatSyncPath(absolutePath) + << L" (" << Utility::s2ws(itemId) << L")"); } else { - LOGW_SYNCPAL_WARN(_logger, L"Fail to remove item: " << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(itemId).c_str() << L")"); + LOGW_SYNCPAL_WARN(_logger, L"Fail to remove item: " << Utility::formatSyncPath(absolutePath) << L" (" + << Utility::s2ws(itemId) << L")"); invalidateSnapshot(); return; } @@ -227,7 +250,7 @@ void LocalFileSystemObserverWorker::changesDetected(const std::list_syncDb->rootNode().nodeIdLocal(); } else { if (!IoHelper::getNodeId(parentPath, parentNodeId)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in IoHelper::getNodeId for " << Utility::formatSyncPath(parentPath).c_str()); + LOGW_SYNCPAL_WARN(_logger, L"Error in IoHelper::getNodeId for " << Utility::formatSyncPath(parentPath)); continue; } } @@ -238,12 +261,11 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listsize(nodeId), _snapshot->lastModified(nodeId), changed, ioError); if (!success) { - LOGW_SYNCPAL_WARN(_logger, L"Error in IoHelper::checkIfFileChanged: " - << Utility::formatIoError(absolutePath, ioError).c_str()); + LOGW_SYNCPAL_WARN(_logger, + L"Error in IoHelper::checkIfFileChanged: " << Utility::formatIoError(absolutePath, ioError)); } if (ioError == IoError::AccessDenied) { - LOGW_SYNCPAL_DEBUG(_logger, - L"Item: " << Utility::formatSyncPath(absolutePath).c_str() << L" misses search permissions!"); + LOGW_SYNCPAL_DEBUG(_logger, L"Item: " << Utility::formatSyncPath(absolutePath) << L" misses search permissions!"); sendAccessDeniedError(absolutePath); } else if (ioError == IoError::NoSuchFileOrDirectory) { continue; @@ -256,14 +278,14 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listvfsStatus(absolutePath, isPlaceholder, isHydrated, isSyncing, progress)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in vfsStatus: " << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_WARN(_logger, L"Error in vfsStatus: " << Utility::formatSyncPath(absolutePath)); invalidateSnapshot(); return; } PinState pinstate = PinState::Unspecified; if (!_syncPal->vfsPinState(absolutePath, pinstate)) { - LOGW_SYNCPAL_WARN(_logger, L"Error in vfsPinState: " << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_WARN(_logger, L"Error in vfsPinState: " << Utility::formatSyncPath(absolutePath)); invalidateSnapshot(); return; } @@ -274,7 +296,7 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listvfsFileStatusChanged(absolutePath, SyncFileStatus::Syncing)) { LOGW_SYNCPAL_WARN(_logger, L"Error in SyncPal::vfsFileStatusChanged: " - << Utility::formatSyncPath(absolutePath).c_str()); + << Utility::formatSyncPath(absolutePath)); invalidateSnapshot(); return; } @@ -284,8 +306,8 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listitemId(relativePath); if (_snapshot->removeItem(itemId)) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " - << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(itemId).c_str() << L")"); + LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " << Utility::formatSyncPath(absolutePath) + << L" (" << Utility::s2ws(itemId) << L")"); } else { - LOGW_SYNCPAL_WARN(_logger, L"Failed to remove item: " << Utility::formatSyncPath(absolutePath).c_str() - << L" (" << Utility::s2ws(itemId).c_str() << L")"); + LOGW_SYNCPAL_WARN(_logger, L"Failed to remove item: " << Utility::formatSyncPath(absolutePath) << L" (" + << Utility::s2ws(itemId) << L")"); invalidateSnapshot(); return; } @@ -317,13 +338,12 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listremoveItem(previousItemId)) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " - << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(previousItemId).c_str() << L")"); + LOGW_SYNCPAL_DEBUG(_logger, L"Item removed from local snapshot: " << Utility::formatSyncPath(absolutePath) + << L" (" << Utility::s2ws(previousItemId) + << L")"); } else { - LOGW_SYNCPAL_WARN(_logger, L"Failed to delete item: " << Utility::formatSyncPath(absolutePath).c_str() - << L" (" << Utility::s2ws(previousItemId).c_str() - << L")"); + LOGW_SYNCPAL_WARN(_logger, L"Failed to delete item: " << Utility::formatSyncPath(absolutePath) << L" (" + << Utility::s2ws(previousItemId) << L")"); invalidateSnapshot(); return; } @@ -334,16 +354,16 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listupdateItem(item)) { - LOGW_SYNCPAL_WARN(_logger, L"Failed to insert item: " << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(nodeId).c_str() << L")"); + LOGW_SYNCPAL_WARN(_logger, L"Failed to insert item: " << Utility::formatSyncPath(absolutePath) << L" (" + << Utility::s2ws(nodeId) << L")"); invalidateSnapshot(); return; } if (ParametersCache::isExtendedLogEnabled()) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item inserted in local snapshot: " << Utility::formatSyncPath(absolutePath).c_str() - << L" (" << Utility::s2ws(nodeId).c_str() - << L") at " << fileStat.modtime); + LOGW_SYNCPAL_DEBUG(_logger, L"Item inserted in local snapshot: " << Utility::formatSyncPath(absolutePath) << L" (" + << Utility::s2ws(nodeId) << L") at " + << fileStat.modtime); // if (nodeType == NodeType::File) { // if (canComputeChecksum(absolutePath)) { @@ -356,8 +376,8 @@ void LocalFileSystemObserverWorker::changesDetected(const std::list CommonUtility::maxPathLength()) { - LOGW_SYNCPAL_WARN(_logger, L"Ignore item: " << Utility::formatSyncPath(absolutePath).c_str() - << L" because size > " << CommonUtility::maxPathLength()); + LOGW_SYNCPAL_WARN(_logger, L"Ignore item: " << Utility::formatSyncPath(absolutePath) << L" because size > " + << CommonUtility::maxPathLength()); continue; } @@ -395,9 +415,8 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listupdateItem(SnapshotItem(nodeId, parentNodeId, absolutePath.filename().native(), fileStat.creationTime, fileStat.modtime, nodeType, fileStat.size, isLink, true, true))) { if (ParametersCache::isExtendedLogEnabled()) { - LOGW_SYNCPAL_DEBUG(_logger, L"Item: " << Utility::formatSyncPath(absolutePath).c_str() << L" (" - << Utility::s2ws(nodeId).c_str() << L") updated in local snapshot at " - << fileStat.modtime); + LOGW_SYNCPAL_DEBUG(_logger, L"Item: " << Utility::formatSyncPath(absolutePath) << L" (" << Utility::s2ws(nodeId) + << L") updated in local snapshot at " << fileStat.modtime); } if (nodeType == NodeType::File) { @@ -407,8 +426,8 @@ void LocalFileSystemObserverWorker::changesDetected(const std::listvfsExclude( absolutePath)) { // TODO : This class should never set any attribute or change anything on a file - LOGW_SYNCPAL_WARN(_logger, L"Error in vfsExclude : " << Utility::formatSyncPath(absolutePath).c_str()); + LOGW_SYNCPAL_WARN(_logger, L"Error in vfsExclude : " << Utility::formatSyncPath(absolutePath)); } } @@ -756,20 +772,20 @@ ExitInfo LocalFileSystemObserverWorker::exploreDir(const SyncPath &absoluteParen FileStat parentFileStat; IoError ioError = IoError::Success; if (!IoHelper::getFileStat(absolutePath.parent_path(), &parentFileStat, ioError)) { - LOGW_WARN(_logger, L"Error in IoHelper::getFileStat: " - << Utility::formatIoError(absolutePath.parent_path(), ioError).c_str()); + LOGW_WARN(_logger, + L"Error in IoHelper::getFileStat: " << Utility::formatIoError(absolutePath.parent_path(), ioError)); setExitCause(ExitCause::FileAccessError); return ExitCode::SystemError; } if (ioError == IoError::NoSuchFileOrDirectory) { - LOGW_SYNCPAL_DEBUG(_logger, L"Directory doesn't exist anymore: " - << Utility::formatSyncPath(absolutePath.parent_path()).c_str()); + LOGW_SYNCPAL_DEBUG( + _logger, L"Directory doesn't exist anymore: " << Utility::formatSyncPath(absolutePath.parent_path())); dirIt.disableRecursionPending(); continue; } else if (ioError == IoError::AccessDenied) { LOGW_SYNCPAL_DEBUG(_logger, L"Directory misses search permission: " - << Utility::formatSyncPath(absolutePath.parent_path()).c_str()); + << Utility::formatSyncPath(absolutePath.parent_path())); dirIt.disableRecursionPending(); sendAccessDeniedError(absolutePath); continue; @@ -779,8 +795,8 @@ ExitInfo LocalFileSystemObserverWorker::exploreDir(const SyncPath &absoluteParen } if (!IoHelper::getItemType(absolutePath, itemType)) { - LOGW_SYNCPAL_DEBUG(_logger, L"Error in IoHelper::getItemType: " - << Utility::formatIoError(absolutePath, itemType.ioError).c_str()); + LOGW_SYNCPAL_DEBUG(_logger, + L"Error in IoHelper::getItemType: " << Utility::formatIoError(absolutePath, itemType.ioError)); dirIt.disableRecursionPending(); continue; } @@ -796,15 +812,15 @@ ExitInfo LocalFileSystemObserverWorker::exploreDir(const SyncPath &absoluteParen if (_snapshot->updateItem(item)) { if (ParametersCache::isExtendedLogEnabled()) { LOGW_SYNCPAL_DEBUG(_logger, L"Item inserted in local snapshot: " - << Utility::formatSyncPath(absolutePath.filename()).c_str() << L" inode:" - << Utility::s2ws(nodeId.c_str()) << L" parent inode:" - << Utility::s2ws(parentNodeId).c_str() << L" createdAt:" - << fileStat.creationTime << L" modtime:" << fileStat.modtime << L" isDir:" + << Utility::formatSyncPath(absolutePath.filename()) << L" inode:" + << Utility::s2ws(nodeId) << L" parent inode:" + << Utility::s2ws(parentNodeId) << L" createdAt:" << fileStat.creationTime + << L" modtime:" << fileStat.modtime << L" isDir:" << (itemType.nodeType == NodeType::Directory) << L" size:" << fileStat.size); } } else { - LOGW_SYNCPAL_WARN(_logger, L"Failed to insert item: " << Utility::formatSyncPath(absolutePath.filename()).c_str() + LOGW_SYNCPAL_WARN(_logger, L"Failed to insert item: " << Utility::formatSyncPath(absolutePath.filename()) << L" into local snapshot!!!"); } } diff --git a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.h b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.h index 0fd2c313a..643424439 100644 --- a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.h +++ b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker.h @@ -32,7 +32,7 @@ class LocalFileSystemObserverWorker : public FileSystemObserverWorker { void start() override; void stop() override; - void changesDetected(const std::list> &changes); + virtual void changesDetected(const std::list> &changes); virtual void forceUpdate() override; protected: diff --git a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.cpp b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.cpp index 22657d848..d3591b88a 100644 --- a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.cpp @@ -38,9 +38,4 @@ LocalFileSystemObserverWorker_unix::LocalFileSystemObserverWorker_unix(std::shar #endif } -LocalFileSystemObserverWorker_unix::~LocalFileSystemObserverWorker_unix() { - FolderWatcher *ptr = _folderWatcher.release(); - delete ptr; -} - } // namespace KDC diff --git a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.h b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.h index 389c5d648..2fdc6a70c 100644 --- a/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.h +++ b/src/libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.h @@ -26,7 +26,6 @@ class LocalFileSystemObserverWorker_unix : public LocalFileSystemObserverWorker public: LocalFileSystemObserverWorker_unix(std::shared_ptr syncPal, const std::string &name, const std::string &shortName); - ~LocalFileSystemObserverWorker_unix(); protected: virtual bool isFolderWatcherReliable() const override { return (_folderWatcher && _folderWatcher->isReliable()); } diff --git a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp index 34900ff9e..d9390ea4a 100644 --- a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp @@ -82,8 +82,8 @@ void RemoteFileSystemObserverWorker::execute() { Utility::msleep(LOOP_EXEC_SLEEP_PERIOD); } - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } ExitCode RemoteFileSystemObserverWorker::generateInitialSnapshot() { @@ -354,7 +354,7 @@ ExitCode RemoteFileSystemObserverWorker::getItemsInDir(const NodeId &dirId, cons << SyncName2WStr(_snapshot->name(item.parentId())).c_str() << L"\""); SyncPath path; - _snapshot->path(item.parentId(), path); + _snapshot->path(item.parentId(), path, ignore); path /= item.name(); Error err(_syncPal->syncDbId(), "", item.id(), NodeType::Directory, path, ConflictType::None, InconsistencyType::None, diff --git a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.cpp b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.cpp index 23509e9e2..70dfa58d5 100644 --- a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.cpp @@ -77,6 +77,27 @@ bool Snapshot::updateItem(const SnapshotItem &newItem) { return false; } + // Check if `newItem` already exists with the same path but a different Id + if (auto itNewParent = _items.find(newItem.parentId()); itNewParent != _items.end()) { + for (const NodeId &childId: itNewParent->second.childrenIds()) { + auto child = _items.find(childId); + if (child == _items.end()) { + assert(false && "Child not found in snapshot"); + LOG_WARN(Log::instance()->getLogger(), "Child " << childId.c_str() << " not found in snapshot"); + continue; + } + + if (child->second.name() == newItem.name() && child->second.id() != newItem.id()) { + LOGW_DEBUG(Log::instance()->getLogger(), + L"Item: " << SyncName2WStr(newItem.name()) << L" (" << Utility::s2ws(newItem.id()) + << L") already exists in parent: " << Utility::s2ws(newItem.parentId()) + << L" with a different id. Removing it and adding the new one."); + removeItem(childId); + break; // There should be (at most) only one item with the same name in a folder + } + } + } + const SnapshotItem &prevItem = _items[newItem.id()]; // Update parent's children lists @@ -110,15 +131,15 @@ bool Snapshot::updateItem(const SnapshotItem &newItem) { } if (ParametersCache::isExtendedLogEnabled()) { - LOGW_DEBUG(Log::instance()->getLogger(), L"Item: " << SyncName2WStr(newItem.name()).c_str() << L" (" - << Utility::s2ws(newItem.id()).c_str() << L") updated at:" + LOGW_DEBUG(Log::instance()->getLogger(), L"Item: " << SyncName2WStr(newItem.name()) << L" (" + << Utility::s2ws(newItem.id()) << L") updated at:" << newItem.lastModified()); } return true; } -bool Snapshot::removeItem(const NodeId &id) { +bool Snapshot::removeItem(const NodeId id) { const std::scoped_lock lock(_mutex); if (id.empty()) { @@ -206,8 +227,9 @@ bool Snapshot::setParentId(const NodeId &itemId, const NodeId &newParentId) { return false; } -bool Snapshot::path(const NodeId &itemId, SyncPath &path) const noexcept { +bool Snapshot::path(const NodeId &itemId, SyncPath &path, bool &ignore) const noexcept { path.clear(); + ignore = false; if (itemId.empty()) { LOG_WARN(Log::instance()->getLogger(), "Error in Snapshot::path: empty item ID argument."); @@ -235,12 +257,16 @@ bool Snapshot::path(const NodeId &itemId, SyncPath &path) const noexcept { } // Construct path - SyncPath tmp; + SyncPath tmpParentPath; while (!names.empty()) { - tmp /= names.back(); + path /= names.back(); names.pop_back(); + if (path.parent_path() != tmpParentPath) { + ignore = true; + return false; + } + tmpParentPath = path; } - path = tmp; return ok; } @@ -482,26 +508,25 @@ void Snapshot::setValid(bool newIsValid) { _isValid = newIsValid; } -bool Snapshot::checkIntegrityRecursively() { +bool Snapshot::checkIntegrityRecursively() const { return checkIntegrityRecursively(rootFolderId()); } -bool Snapshot::checkIntegrityRecursively(const NodeId &parentId) { +bool Snapshot::checkIntegrityRecursively(const NodeId &parentId) const { // Check that we do not have the same file twice in the same folder - const auto &parentItem = _items[parentId]; + const auto &parentItem = _items.at(parentId); std::set names; for (auto childId = parentItem.childrenIds().begin(), end = parentItem.childrenIds().end(); childId != end; childId++) { if (!checkIntegrityRecursively(*childId)) { return false; } - auto result = names.insert(_items[*childId].name()); + auto result = names.insert(_items.at(*childId).name()); if (!result.second) { - LOGW_WARN(Log::instance()->getLogger(), L"Snapshot integrity check failed, the folder named: \"" - << SyncName2WStr(parentItem.name()).c_str() << L"\"(" - << Utility::s2ws(parentItem.id()).c_str() << L") contains: \"" - << SyncName2WStr(_items[*childId].name()).c_str() - << L"\" twice with two differents NodeId"); + LOGW_WARN(Log::instance()->getLogger(), + L"Snapshot integrity check failed, the folder named: \"" + << SyncName2WStr(parentItem.name()) << L"\"(" << Utility::s2ws(parentItem.id()) << L") contains: \"" + << SyncName2WStr(_items.at(*childId).name()) << L"\" twice with two different NodeIds"); return false; } } diff --git a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.h b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.h index 311a9eaa6..37c522299 100644 --- a/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.h +++ b/src/libsyncengine/update_detection/file_system_observer/snapshot/snapshot.h @@ -40,12 +40,12 @@ class Snapshot : public SharedObject { void init(); bool updateItem(const SnapshotItem &newItem); - bool removeItem(const NodeId &id); + bool removeItem(const NodeId id); // Do not pass by reference to avoid dangling references NodeId itemId(const SyncPath &path) const; NodeId parentId(const NodeId &itemId) const; bool setParentId(const NodeId &itemId, const NodeId &newParentId); - bool path(const NodeId &itemId, SyncPath &path) const noexcept; + bool path(const NodeId &itemId, SyncPath &path, bool &ignore) const noexcept; SyncName name(const NodeId &itemId) const; bool setName(const NodeId &itemId, const SyncName &newName); SyncTime createdAt(const NodeId &itemId) const; @@ -83,11 +83,11 @@ class Snapshot : public SharedObject { bool isValid() const; void setValid(bool newIsValid); - bool checkIntegrityRecursively(); + bool checkIntegrityRecursively() const; private: void removeChildrenRecursively(const NodeId &parentId); - bool checkIntegrityRecursively(const NodeId &parentId); + bool checkIntegrityRecursively(const NodeId &parentId) const; ReplicaSide _side = ReplicaSide::Unknown; NodeId _rootFolderId; diff --git a/src/libsyncengine/update_detection/update_detector/updatetree.cpp b/src/libsyncengine/update_detection/update_detector/updatetree.cpp index 0633d8241..3a2c0ac46 100644 --- a/src/libsyncengine/update_detection/update_detector/updatetree.cpp +++ b/src/libsyncengine/update_detection/update_detector/updatetree.cpp @@ -156,8 +156,6 @@ void UpdateTree::markAllNodesUnprocessed() { } void UpdateTree::init() { - clear(); - insertNode(_rootNode); _inconsistencyCheckDone = false; } diff --git a/src/libsyncengine/update_detection/update_detector/updatetree.h b/src/libsyncengine/update_detection/update_detector/updatetree.h index c24d397a2..acabdd216 100644 --- a/src/libsyncengine/update_detection/update_detector/updatetree.h +++ b/src/libsyncengine/update_detection/update_detector/updatetree.h @@ -50,6 +50,7 @@ class UpdateTree : public SharedObject { bool isAncestor(const NodeId &nodeId, const NodeId &ancestorNodeId) const; void markAllNodesUnprocessed(); + void clear(); void init(); inline bool inconsistencyCheckDone() const { return _inconsistencyCheckDone; } @@ -67,8 +68,6 @@ class UpdateTree : public SharedObject { bool _inconsistencyCheckDone = false; - void clear(); - friend class TestUpdateTree; friend class TestUpdateTreeWorker; }; diff --git a/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp b/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp index f611618bb..1f1b04c85 100644 --- a/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp +++ b/src/libsyncengine/update_detection/update_detector/updatetreeworker.cpp @@ -92,8 +92,8 @@ void UpdateTreeWorker::execute() { // Clear unexpected operation set once used _operationSet->clear(); - setDone(exitCode); LOG_SYNCPAL_DEBUG(_logger, "Worker stopped: name=" << name().c_str()); + setDone(exitCode); } ExitCode UpdateTreeWorker::step1MoveDirectory() { diff --git a/src/server/appserver.cpp b/src/server/appserver.cpp index 6f4b46610..cfd785f86 100644 --- a/src/server/appserver.cpp +++ b/src/server/appserver.cpp @@ -2106,7 +2106,7 @@ void AppServer::cancelLogUpload() { sendLogUploadStatusUpdated(LogUploadState::CancelRequested, 0); } -void AppServer::uploadLog(bool includeArchivedLogs) { +ExitInfo AppServer::uploadLog(bool includeArchivedLogs) { if (bool found = false; !ParmsDb::instance()->updateAppState(AppStateKey::LogUploadState, LogUploadState::None, found) || !found) { // Reset status LOG_WARN(_logger, "Error in ParmsDb::updateAppState"); @@ -2143,12 +2143,13 @@ void AppServer::uploadLog(bool includeArchivedLogs) { if (exitCause == ExitCause::OperationCanceled) { LOG_DEBUG(_logger, "Log transfert canceled"); sendLogUploadStatusUpdated(LogUploadState::Canceled, 0); - return; + return {exitCode, exitCause}; } else if (exitCode != ExitCode::Ok) { LOG_WARN(_logger, "Error in LogArchiverHelper::sendLogToSupport: code=" << exitCode << " cause=" << exitCause); addError(Error(errId(), ExitCode::LogUploadFailed, exitCause)); } sendLogUploadStatusUpdated(exitCode == ExitCode::Ok ? LogUploadState::Success : LogUploadState::Failed, 0); + return {exitCode, exitCause}; } ExitCode AppServer::checkIfSyncIsValid(const Sync &sync) { @@ -2391,12 +2392,12 @@ bool AppServer::vfsUpdateMetadata(int syncDbId, const SyncPath &path, const Sync return false; } - QByteArray fileId(id.c_str()); - QString *errorStr = nullptr; - if (!_vfsMap[syncDbId]->updateMetadata(SyncName2QStr(path.native()), creationTime, modtime, size, fileId, errorStr)) { + const QByteArray fileId(id.c_str()); + QString errorStr; + if (!_vfsMap[syncDbId]->updateMetadata(SyncName2QStr(path.native()), creationTime, modtime, size, fileId, &errorStr)) { LOGW_WARN(Log::instance()->getLogger(), L"Error in Vfs::updateMetadata for syncDbId=" << syncDbId << L" and path=" << Path2WStr(path).c_str()); - error = errorStr ? errorStr->toStdString() : ""; + error = errorStr.toStdString(); return false; } @@ -4354,8 +4355,24 @@ void AppServer::onRestartSyncs() { continue; } - // Start sync - syncPalMapElt.second->start(); + // Start SyncPal if not paused + Sync sync; + bool found = false; + if (!ParmsDb::instance()->selectSync(syncPalMapElt.first, sync, found)) { + LOG_WARN(Log::instance()->getLogger(), "Error in ParmsDb::selectSync"); + continue; + } + + if (!found) { + LOG_WARN(Log::instance()->getLogger(), + "Sync not found in sync table for syncDbId=" << syncPalMapElt.first); + continue; + } + + if (!sync.paused()) { + // Start sync + syncPalMapElt.second->start(); + } } } } diff --git a/src/server/appserver.h b/src/server/appserver.h index 313c6277e..2196b1ca5 100644 --- a/src/server/appserver.h +++ b/src/server/appserver.h @@ -36,7 +36,6 @@ #include #include #include -#include #include namespace KDC { @@ -182,7 +181,7 @@ class AppServer : public SharedTools::QtSingleApplication { // See types.h -> AppStateKey for the possible values of status void cancelLogUpload(); - void uploadLog(bool includeArchivedLogs); + ExitInfo uploadLog(bool includeArchivedLogs); void sendLogUploadStatusUpdated(LogUploadState status, int percent); void startSyncPals(); diff --git a/src/server/socketapi.cpp b/src/server/socketapi.cpp index e89734ac2..e311c20bf 100644 --- a/src/server/socketapi.cpp +++ b/src/server/socketapi.cpp @@ -49,10 +49,8 @@ #include #include -#ifdef Q_OS_MAC - +#ifdef __APPLE__ #include - #endif #include @@ -98,23 +96,23 @@ SocketApi::SocketApi(const std::unordered_map QString socketPath; if (OldUtility::isWindows()) { - socketPath = QString("\\\\.\\pipe\\") + QString(APPLICATION_SHORTNAME) + QString("-") + - QString::fromLocal8Bit(qgetenv("USERNAME")); + socketPath = QString(R"(\\.\pipe\%1-%2)").arg(APPLICATION_SHORTNAME, Utility::userName().c_str()); } else if (OldUtility::isMac()) { socketPath = SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN ".socketApi"; #ifdef Q_OS_MAC - CFURLRef url = (CFURLRef) CFAutorelease((CFURLRef) CFBundleCopyBundleURL(CFBundleGetMainBundle())); - QString bundlePath = QUrl::fromCFURL(url).path(); - QString cmd; - // Tell Finder to use the Extension (checking it from System Preferences -> Extensions) - cmd = QString("pluginkit -v -e use -i " APPLICATION_REV_DOMAIN ".Extension"); + QString cmd = QString("pluginkit -v -e use -i " APPLICATION_REV_DOMAIN ".Extension"); system(cmd.toLocal8Bit()); // Add it again. This was needed for Mojave to trigger a load. - cmd = QString("pluginkit -v -a ") + bundlePath + "Contents/PlugIns/Extension.appex/"; - system(cmd.toLocal8Bit()); - + // TODO: Still needed? + CFURLRef url = (CFURLRef) CFAutorelease((CFURLRef) CFBundleCopyBundleURL(CFBundleGetMainBundle())); + QString appexPath = QUrl::fromCFURL(url).path() + "Contents/PlugIns/Extension.appex/"; + if (std::filesystem::exists(QStr2Path(appexPath))) { + // Bundled app (i.e. not test executable) + cmd = QString("pluginkit -v -a ") + appexPath; + system(cmd.toLocal8Bit()); + } #endif } else if (OldUtility::isLinux() || OldUtility::isBSD()) { const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); @@ -1098,8 +1096,8 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, SocketListener * } if (isSingleFile) { - canHydrate = !isSyncing && !isHydrated; - canDehydrate = !isSyncing && isHydrated; + canHydrate = isPlaceholder && !isSyncing && !isHydrated; + canDehydrate = isPlaceholder && !isSyncing && isHydrated; } } diff --git a/src/server/socketapi.h b/src/server/socketapi.h index f24273047..7c78e4bdd 100644 --- a/src/server/socketapi.h +++ b/src/server/socketapi.h @@ -39,10 +39,6 @@ typedef QLocalServer SocketApiServer; #endif - -#define WORKER_GETFILE 0 -#define NB_WORKERS 1 - class QUrl; namespace KDC { diff --git a/src/server/socketapisocket_mac.mm b/src/server/socketapisocket_mac.mm index a64388bd4..62b1e12ce 100644 --- a/src/server/socketapisocket_mac.mm +++ b/src/server/socketapisocket_mac.mm @@ -22,6 +22,9 @@ #include "../extensions/MacOSX/kDriveFinderSync/Extension/xpcExtensionProtocol.h" #include "../extensions/MacOSX/kDriveFinderSync/LoginItemAgent/xpcLoginItemProtocol.h" +#include "libcommon/utility/utility.h" + +#include @interface LocalEnd : NSObject @@ -174,28 +177,36 @@ - (void)connectToLoginAgent { // Setup our connection to the launch item's service // This will start the launch item if it isn't already running NSLog(@"[KD] Setup connection with login item agent"); - NSBundle *appBundle = [NSBundle bundleForClass:[self class]]; - NSString *loginItemAgentMachName = [appBundle objectForInfoDictionaryKey:@"LoginItemAgentMachName"]; - if (!loginItemAgentMachName) { - NSLog(@"[KD] LoginItemAgentMachName undefined"); - return; - } - - NSError *error = nil; - _loginItemAgentConnection = [[NSXPCConnection alloc] initWithLoginItemName:loginItemAgentMachName error:&error]; - if (_loginItemAgentConnection == nil) { - NSLog(@"[KD] Failed to connect to login item agent: %@", [error description]); - return; - } - - /* - // To debug with an existing login item agent - _loginItemAgentConnection = [[NSXPCConnection alloc] initWithMachServiceName:loginItemAgentMachName options:0]; - if (_loginItemAgentConnection == nil) { - NSLog(@"[KD] Failed to connect to login item agent"); - return; + NSString *loginItemAgentMachName = nil; + if (qApp) { + NSBundle *appBundle = [NSBundle bundleForClass:[self class]]; + loginItemAgentMachName = [appBundle objectForInfoDictionaryKey:@"LoginItemAgentMachName"]; + if (!loginItemAgentMachName) { + NSLog(@"[KD] LoginItemAgentMachName undefined"); + return; + } + + NSError *error = nil; + _loginItemAgentConnection = [[NSXPCConnection alloc] + initWithLoginItemName:loginItemAgentMachName + error:&error]; + if (_loginItemAgentConnection == nil) { + NSLog(@"[KD] Failed to connect to login item agent: %@", [error description]); + return; + } + } else { + // For testing + loginItemAgentMachName = [NSString + stringWithUTF8String:KDC::CommonUtility::loginItemAgentId().c_str()]; + + _loginItemAgentConnection = [[NSXPCConnection alloc] + initWithMachServiceName:loginItemAgentMachName + options:0]; + if (_loginItemAgentConnection == nil) { + NSLog(@"[KD] Failed to connect to login item agent"); + return; + } } - */ // Set exported interface NSLog(@"[KD] Set exported interface for connection with ext"); diff --git a/src/server/updater/abstractupdater.cpp b/src/server/updater/abstractupdater.cpp index aa38f9497..4d0bb2112 100644 --- a/src/server/updater/abstractupdater.cpp +++ b/src/server/updater/abstractupdater.cpp @@ -21,6 +21,9 @@ #include "libcommon/utility/utility.h" #include "log/log.h" #include "requests/parameterscache.h" +#if defined(__APPLE__) +#include "sparkleupdater.h" +#endif namespace KDC { @@ -43,6 +46,7 @@ void AbstractUpdater::onAppVersionReceived() { if (!_updateChecker->versionInfo().isValid()) { setState(UpdateState::CheckError); LOG_WARN(Log::instance()->getLogger(), "Error while retrieving latest app version"); + return; } const bool available = @@ -65,6 +69,9 @@ void AbstractUpdater::unskipVersion() { ParametersCache::instance()->parameters().setSeenVersion(""); ParametersCache::instance()->save(); } +#if defined(__APPLE__) + SparkleUpdater::unskipVersion(); +#endif } bool AbstractUpdater::isVersionSkipped(const std::string& version) { diff --git a/src/server/updater/sparkleupdater.h b/src/server/updater/sparkleupdater.h index 605d24e76..0d56a4f25 100644 --- a/src/server/updater/sparkleupdater.h +++ b/src/server/updater/sparkleupdater.h @@ -32,6 +32,8 @@ class SparkleUpdater final : public AbstractUpdater { void setQuitCallback(const std::function &quitCallback) override; void startInstaller() override; + static void unskipVersion(); + private: void reset(const std::string &url); void deleteUpdater(); diff --git a/src/server/updater/sparkleupdater.mm b/src/server/updater/sparkleupdater.mm index 6fc46f59f..b8bf620af 100644 --- a/src/server/updater/sparkleupdater.mm +++ b/src/server/updater/sparkleupdater.mm @@ -126,9 +126,9 @@ - (void)updater:(SPUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update } // Sent when a valid update is not found. -- (void)updaterDidNotFindUpdate:(SPUUpdater *)update { +- (void)updaterDidNotFindUpdate:(SPUUpdater *)update error:(nonnull NSError *)error { (void)update; - LOG_DEBUG(KDC::Log::instance()->getLogger(), "No valid update found"); + LOG_DEBUG(KDC::Log::instance()->getLogger(), "No valid update found - Error code: " << [error code] << ", reason: " << [error.userInfo[SPUNoUpdateFoundReasonKey] integerValue]); } // Sent immediately before installing the specified update. @@ -187,7 +187,14 @@ - (BOOL)supportsGentleScheduledUpdateReminders { } void SparkleUpdater::onUpdateFound() { - if (isVersionSkipped(versionInfo().fullVersion())) return; + if (isVersionSkipped(versionInfo().fullVersion())) { + LOG_INFO(KDC::Log::instance()->getLogger(), "Version " << versionInfo().fullVersion().c_str() << " is skipped."); + return; + } + if ([d->updater sessionInProgress]) { + LOG_INFO(KDC::Log::instance()->getLogger(), "An update window is already opened or installation is in progress. No need to start a new one."); + return; + } startInstaller(); } @@ -202,6 +209,12 @@ - (BOOL)supportsGentleScheduledUpdateReminders { [d->spuStandardUserDriver showUpdateInFocus]; } +void SparkleUpdater::unskipVersion() { + // Discard skipped version in Sparkle + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@ "SUSkippedVersion"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + void SparkleUpdater::reset(const std::string &url) { [d->spuStandardUserDriver dismissUpdateInstallation]; deleteUpdater(); diff --git a/src/server/updater/updatechecker.cpp b/src/server/updater/updatechecker.cpp index 72ae3254f..c87c94436 100644 --- a/src/server/updater/updatechecker.cpp +++ b/src/server/updater/updatechecker.cpp @@ -66,12 +66,7 @@ void UpdateChecker::versionInfoReceived(UniqueId jobId) { SentryHandler::instance()->captureMessage(SentryLevel::Warning, "AbstractUpdater::checkUpdateAvailable", ss.str()); LOG_ERROR(Log::instance()->getLogger(), ss.str().c_str()); } else { - DistributionChannel channel = _channel; - // hasProdNext() is true if and only if the running application has been elected for an upgrade in the context of a - // progressive update - if (channel == DistributionChannel::Prod && getAppVersionJobPtr->hasProdNext()) channel = DistributionChannel::Next; - - _versionInfo = getAppVersionJobPtr->getVersionInfo(channel); + _versionInfo = getAppVersionJobPtr->getProdVersionInfo(); if (!_versionInfo.isValid()) { std::string error = "Invalid version info!"; SentryHandler::instance()->captureMessage(SentryLevel::Warning, "AbstractUpdater::checkUpdateAvailable", error); diff --git a/src/server/updater/updatemanager.cpp b/src/server/updater/updatemanager.cpp index 509c8588d..51fa81536 100644 --- a/src/server/updater/updatemanager.cpp +++ b/src/server/updater/updatemanager.cpp @@ -54,6 +54,10 @@ UpdateManager::UpdateManager(QObject *parent) : QObject(parent) { void UpdateManager::startInstaller() const { LOG_DEBUG(Log::instance()->getLogger(), "startInstaller called!"); + + // Cleanup skipped version + AbstractUpdater::unskipVersion(); + _updater->startInstaller(); } diff --git a/src/server/vfs/mac/CMakeLists.txt b/src/server/vfs/mac/CMakeLists.txt index ae9f261f2..c4c46e0c4 100644 --- a/src/server/vfs/mac/CMakeLists.txt +++ b/src/server/vfs/mac/CMakeLists.txt @@ -20,14 +20,6 @@ set_target_properties("${libsyncengine_NAME}_vfs_mac" AUTOMOC TRUE ) -# for being loadable when client run from build dir -set(vfs_buildoutputdir "${BIN_OUTPUT_DIRECTORY}/${KDRIVE_OSX_BUNDLE}/Contents/PlugIns/") -set_target_properties("${libsyncengine_NAME}_vfs_mac" - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${vfs_buildoutputdir} - RUNTIME_OUTPUT_DIRECTORY ${vfs_buildoutputdir} -) - # Link find_library(SYSTEM_EXTENSION_LIBRARY NAMES SystemExtensions) target_link_libraries("${libsyncengine_NAME}_vfs_mac" diff --git a/src/server/vfs/mac/litesyncextconnector.mm b/src/server/vfs/mac/litesyncextconnector.mm index d8ab01eb4..717529d6c 100644 --- a/src/server/vfs/mac/litesyncextconnector.mm +++ b/src/server/vfs/mac/litesyncextconnector.mm @@ -217,12 +217,19 @@ - (BOOL)connectToExt { // Setup connection with LiteSync extension NSLog(@"[KD] Setup connection with LiteSync extension"); - // Read LiteSyncExtMachName from plist - NSBundle *appBundle = [NSBundle mainBundle]; - NSString *liteSyncExtMachName = [appBundle objectForInfoDictionaryKey:@"LiteSyncExtMachName"]; - if (!liteSyncExtMachName) { - NSLog(@"[KD] LiteSyncExtMachName undefined"); - return FALSE; + NSString *liteSyncExtMachName = nil; + if (qApp) { + // Read LiteSyncExtMachName from plist + NSBundle *appBundle = [NSBundle mainBundle]; + liteSyncExtMachName = [appBundle objectForInfoDictionaryKey:@"LiteSyncExtMachName"]; + if (!liteSyncExtMachName) { + NSLog(@"[KD] LiteSyncExtMachName undefined"); + return FALSE; + } + } else { + // For testing + liteSyncExtMachName = [NSString + stringWithUTF8String:KDC::CommonUtility::liteSyncExtBundleId().c_str()]; } _connection = [[NSXPCConnection alloc] initWithMachServiceName:liteSyncExtMachName options:0]; diff --git a/src/server/vfs/mac/vfs_mac.cpp b/src/server/vfs/mac/vfs_mac.cpp index 48f85e411..b9b680d86 100644 --- a/src/server/vfs/mac/vfs_mac.cpp +++ b/src/server/vfs/mac/vfs_mac.cpp @@ -53,7 +53,7 @@ VfsMac::VfsMac(KDC::VfsSetupParams &vfsSetupParams, QObject *parent) : // Start worker threads for (int i = 0; i < NB_WORKERS; i++) { for (int j = 0; j < s_nb_threads[i]; j++) { - QThread *workerThread = new QThread(); + QtLoggingThread *workerThread = new QtLoggingThread(); _workerInfo[i]._threadList.append(workerThread); Worker *worker = new Worker(this, i, j, logger()); worker->moveToThread(workerThread); @@ -103,6 +103,8 @@ bool VfsMac::startImpl(bool &installationDone, bool &activationDone, bool &conne } if (!installationDone) { + activationDone = false; + connectionDone = false; installationDone = _connector->install(activationDone); if (!installationDone) { LOG_WARN(logger(), "Error in LiteSyncExtConnector::install!"); @@ -112,6 +114,7 @@ bool VfsMac::startImpl(bool &installationDone, bool &activationDone, bool &conne if (!activationDone) { LOG_INFO(logger(), "LiteSync extension activation pending"); + connectionDone = false; return false; } diff --git a/src/server/vfs/mac/vfs_mac.h b/src/server/vfs/mac/vfs_mac.h index 435248850..9d4036319 100644 --- a/src/server/vfs/mac/vfs_mac.h +++ b/src/server/vfs/mac/vfs_mac.h @@ -18,6 +18,7 @@ #pragma once +#include "libcommon/utility/utility.h" #include "libcommonserver/vfs.h" #include "libcommonserver/plugin.h" #include "litesyncextconnector.h" @@ -28,7 +29,6 @@ #include #include #include -#include #include #define WORKER_HYDRATION 0 @@ -44,7 +44,7 @@ struct WorkerInfo { std::deque _queue; QWaitCondition _queueWC; bool _stop = false; - QList _threadList; + QList _threadList; }; class VfsMac : public Vfs { @@ -97,6 +97,8 @@ class VfsMac : public Vfs { bool startImpl(bool &installationDone, bool &activationDone, bool &connectionDone) override; void stopImpl(bool unregister) override; + friend class TestWorkers; + private: LiteSyncExtConnector *_connector{nullptr}; diff --git a/src/server/vfs/win/CMakeLists.txt b/src/server/vfs/win/CMakeLists.txt index 01e188ca8..c268b189f 100644 --- a/src/server/vfs/win/CMakeLists.txt +++ b/src/server/vfs/win/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(Qt6) -add_library("${libsyncengine_NAME}_vfs_win" SHARED +set(libsyncengine_vfs_win_SRCS vfs_win.h vfs_win.cpp ../../../libcommonserver/vfs.h ../../../libcommonserver/vfs.cpp ../../../libcommonserver/plugin.h ../../../libcommonserver/plugin.cpp @@ -8,6 +8,17 @@ add_library("${libsyncengine_NAME}_vfs_win" SHARED ../../../common/filesystembase.h ../../../common/filesystembase.cpp ) +add_library("${libsyncengine_NAME}_vfs_win" SHARED + ${libsyncengine_vfs_win_SRCS} +) + +set_target_properties("${libsyncengine_NAME}_vfs_win" PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} + RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} + PREFIX "" + AUTOMOC TRUE +) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib) find_library(VFS_STATIC_LIBRARY NAMES vfs PATHS ${VFS_DIRECTORY} NO_DEFAULT_PATH) @@ -17,11 +28,9 @@ target_link_libraries("${libsyncengine_NAME}_vfs_win" Qt6::Core ) -set_target_properties("${libsyncengine_NAME}_vfs_win" PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} - RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} - PREFIX "" - AUTOMOC TRUE +generate_export_header("${libsyncengine_NAME}_vfs_win" + BASE_NAME "${libsyncengine_NAME}_vfs_win" + EXPORT_MACRO_NAME SYNCENGINEVFS_EXPORT + EXPORT_FILE_NAME syncenginevfslib.h ) - diff --git a/src/server/vfs/win/vfs_win.cpp b/src/server/vfs/win/vfs_win.cpp index 81b86cc90..259aada4b 100644 --- a/src/server/vfs/win/vfs_win.cpp +++ b/src/server/vfs/win/vfs_win.cpp @@ -59,17 +59,21 @@ VfsWin::VfsWin(VfsSetupParams &vfsSetupParams, QObject *parent) : Vfs(vfsSetupPa return; } - // Start worker threads - for (int i = 0; i < NB_WORKERS; i++) { - for (int j = 0; j < s_nb_threads[i]; j++) { - QThread *workerThread = new QThread(); - _workerInfo[i]._threadList.append(workerThread); - Worker *worker = new Worker(this, i, j, logger()); - worker->moveToThread(workerThread); - connect(workerThread, &QThread::started, worker, &Worker::start); - connect(workerThread, &QThread::finished, worker, &QObject::deleteLater); - connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); - workerThread->start(); + // Start hydration/dehydration workers + // !!! Disabled for testing because no QEventLoop !!! + if (qApp) { + // Start worker threads + for (int i = 0; i < NB_WORKERS; i++) { + for (int j = 0; j < s_nb_threads[i]; j++) { + QThread *workerThread = new QThread(); + _workerInfo[i]._threadList.append(workerThread); + Worker *worker = new Worker(this, i, j, logger()); + worker->moveToThread(workerThread); + connect(workerThread, &QThread::started, worker, &Worker::start); + connect(workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater); + workerThread->start(); + } } } } diff --git a/src/server/vfs/win/vfs_win.h b/src/server/vfs/win/vfs_win.h index c55d8afa0..ec2b421c9 100644 --- a/src/server/vfs/win/vfs_win.h +++ b/src/server/vfs/win/vfs_win.h @@ -18,6 +18,7 @@ #pragma once +#include "server/vfs/win/syncenginevfslib.h" #include "libcommonserver/vfs.h" #include "libcommonserver/plugin.h" @@ -52,14 +53,14 @@ struct WorkerInfo { QList _threadList; }; -class VfsWin : public Vfs { +class SYNCENGINEVFS_EXPORT VfsWin : public Vfs { Q_OBJECT Q_INTERFACES(KDC::Vfs) public: WorkerInfo _workerInfo[NB_WORKERS]; - explicit VfsWin(VfsSetupParams &vfsSetupParams, QObject *parent); + explicit VfsWin(VfsSetupParams &vfsSetupParams, QObject *parent = nullptr); ~VfsWin(); void debugCbk(TraceLevel level, const wchar_t *msg); @@ -107,6 +108,8 @@ class VfsWin : public Vfs { bool startImpl(bool &installationDone, bool &activationDone, bool &connectionDone) override; void stopImpl(bool unregister) override; + friend class TestWorkers; + private: log4cplus::Logger _logger; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f823d153e..de1a8d327 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,4 +28,4 @@ add_subdirectory(libcommonserver) add_subdirectory(libcommon) add_subdirectory(libparms) add_subdirectory(libsyncengine) -#add_subdirectory(server) +add_subdirectory(server) diff --git a/test/libcommon/CMakeLists.txt b/test/libcommon/CMakeLists.txt index 2203afaa8..223cd647b 100644 --- a/test/libcommon/CMakeLists.txt +++ b/test/libcommon/CMakeLists.txt @@ -33,17 +33,6 @@ target_link_libraries(${testcommon_NAME} ${libcommonserver_NAME} ) -if (WIN32) - target_link_libraries(${testcommon_NAME} - log4cplus::log4cplusU) -elseif (APPLE) - target_link_libraries(${testcommon_NAME} - "/usr/local/lib/liblog4cplusU.dylib") -else () - target_link_libraries(${testcommon_NAME} - "/usr/local/lib/liblog4cplusU.so") -endif () - if (WIN32) target_link_libraries(${testcommon_NAME} debug diff --git a/test/libcommonserver/CMakeLists.txt b/test/libcommonserver/CMakeLists.txt index 84a7122fb..4f2510687 100644 --- a/test/libcommonserver/CMakeLists.txt +++ b/test/libcommonserver/CMakeLists.txt @@ -9,7 +9,7 @@ set(testcommonserver_SRCS ../test.cpp ../test_utility/localtemporarydirectory.cpp ../test_utility/testhelpers.h ../test_utility/testhelpers.cpp - + ../test_utility/timechecker.h test.cpp # Utility utility/testutility.h utility/testutility.cpp @@ -19,11 +19,11 @@ set(testcommonserver_SRCS db/testdb.h db/testdb.cpp # io io/testio.h io/testio.cpp io/testgetitemtype.cpp io/testgetfilesize.cpp io/testcheckifpathexists.cpp io/testgetnodeid.cpp io/testgetfilestat.cpp io/testgetrights.cpp io/testisfileaccessible.cpp io/testfilechanged.cpp - io/testcheckifisdirectory.cpp io/testcreatesymlink.cpp io/testcheckifdehydrated.cpp io/testcheckdirectoryiterator.cpp io/testchecksetgetrights.cpp + io/testcheckifisdirectory.cpp io/testcreatesymlink.cpp io/testcheckifdehydrated.cpp io/testcheckdirectoryiterator.cpp io/testchecksetgetrights.cpp io/testopenfile.cpp ) if (APPLE) - list(APPEND testcommonserver_SRCS io/testcreatealias.cpp io/testgetxattrvalue.cpp io/testsetxattrvalue.cpp) + list(APPEND testcommonserver_SRCS io/testcreatealias.cpp io/testgetxattrvalue.cpp io/testsetxattrvalue.cpp io/testremovexattr.cpp) endif () if (WIN32) @@ -47,17 +47,6 @@ target_link_libraries(${testcommonserver_NAME} ${libcommonserver_NAME} ) -if (WIN32) - target_link_libraries(${testcommonserver_NAME} - log4cplus::log4cplusU) -elseif (APPLE) - target_link_libraries(${testcommonserver_NAME} - "/usr/local/lib/liblog4cplusU.dylib") -else () - target_link_libraries(${testcommonserver_NAME} - "/usr/local/lib/liblog4cplusU.so") -endif () - if (WIN32) target_link_libraries(${testcommonserver_NAME} debug diff --git a/test/libcommonserver/io/testcheckdirectoryiterator.cpp b/test/libcommonserver/io/testcheckdirectoryiterator.cpp index bfcb38395..7f563565c 100644 --- a/test/libcommonserver/io/testcheckdirectoryiterator.cpp +++ b/test/libcommonserver/io/testcheckdirectoryiterator.cpp @@ -322,5 +322,4 @@ void TestIo::testCheckDirectoryPermissionLost() { CPPUNIT_ASSERT(endOfDirectory); } } - } // namespace KDC diff --git a/test/libcommonserver/io/testio.cpp b/test/libcommonserver/io/testio.cpp index 67d34a42c..111bbc62c 100644 --- a/test/libcommonserver/io/testio.cpp +++ b/test/libcommonserver/io/testio.cpp @@ -149,4 +149,27 @@ void TestIo::testLogDirectoryPath() { } } +void TestIo::testAccesDeniedOnLockedFiles() { +#ifdef _WIN32 // This test is only relevant on Windows, as on Unix systems, there is no standard way to lock files. + LocalTemporaryDirectory tmpDir("TestIo-testAccesDeniedOnLockedFiles"); + const SyncPath lockedFile = tmpDir.path() / "lockedFile.txt"; + std::ofstream file(lockedFile); + CPPUNIT_ASSERT(file.is_open()); + file.close(); + + // Lock the file + auto hFile = CreateFile(lockedFile.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + CPPUNIT_ASSERT(hFile != INVALID_HANDLE_VALUE); + + // Try to delete the file + std::error_code ec; + std::filesystem::remove_all(lockedFile, ec); + const IoError ioError = IoHelper::stdError2ioError(ec); + + // Unlock the file + CloseHandle(hFile); + CPPUNIT_ASSERT_EQUAL(IoError::AccessDenied, ioError); +#endif +} + } // namespace KDC diff --git a/test/libcommonserver/io/testio.h b/test/libcommonserver/io/testio.h index 1d8cf93c6..135888385 100644 --- a/test/libcommonserver/io/testio.h +++ b/test/libcommonserver/io/testio.h @@ -65,12 +65,18 @@ class TestIo : public CppUnit::TestFixture { #endif #if defined(__APPLE__) + CPPUNIT_TEST(testRemoveXAttr); CPPUNIT_TEST(testCreateAlias); #endif #if defined(_WIN32) CPPUNIT_TEST(testCreateJunction); #endif CPPUNIT_TEST(testCheckIfFileIsDehydrated); + CPPUNIT_TEST(testAccesDeniedOnLockedFiles); + CPPUNIT_TEST(testOpenFileSuccess); + CPPUNIT_TEST(testOpenFileAccessDenied); + CPPUNIT_TEST(testOpenFileNonExisting); + CPPUNIT_TEST(testOpenLockedFileRemovedBeforeTimedOut); CPPUNIT_TEST_SUITE_END(); public: @@ -99,6 +105,7 @@ class TestIo : public CppUnit::TestFixture { void testSetXAttrValue(void); #endif #if defined(__APPLE__) + void testRemoveXAttr(void); void testCreateAlias(void); #elif defined(_WIN32) void testCreateJunction(void); @@ -126,6 +133,11 @@ class TestIo : public CppUnit::TestFixture { void testCheckDirectoryRecursive(); void testCheckDirectoryIteratorUnexpectedDelete(); void testCheckDirectoryPermissionLost(); + void testAccesDeniedOnLockedFiles(); + void testOpenFileSuccess(); + void testOpenFileAccessDenied(); + void testOpenFileNonExisting(); + void testOpenLockedFileRemovedBeforeTimedOut(); private: IoHelperTests *_testObj; diff --git a/test/libcommonserver/io/testopenfile.cpp b/test/libcommonserver/io/testopenfile.cpp new file mode 100644 index 000000000..df2444784 --- /dev/null +++ b/test/libcommonserver/io/testopenfile.cpp @@ -0,0 +1,112 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testio.h" +#include "test_utility/testhelpers.h" +#include "test_utility/timechecker.h" + +#include +using namespace CppUnit; + +namespace KDC { + +bool checkContent(std::ifstream &file) { + std::string content; + std::getline(file, content); + return content.find("test") != std::string::npos; +} + +void TestIo::testOpenFileSuccess() { + LocalTemporaryDirectory tempDir("testOpenFileSuccess"); + SyncPath filePath = tempDir.path() / "testOpenFileSuccess.txt"; + testhelpers::generateOrEditTestFile(filePath); + std::ifstream file; + CPPUNIT_ASSERT(IoHelper::openFile(filePath, file)); + CPPUNIT_ASSERT(file.is_open()); + CPPUNIT_ASSERT(checkContent(file)); + file.close(); +} + +void TestIo::testOpenFileAccessDenied() { + LocalTemporaryDirectory tempDir("testOpenFileAccessDenied"); + SyncPath filePath = tempDir.path() / "testOpenFileAccessDenied.txt"; + testhelpers::generateOrEditTestFile(filePath); + IoError ioError; + IoHelper::setRights(filePath, false, true, true, ioError); + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); + + // Without timeout + std::ifstream file; + TimeChecker timeOutChecker(true); + CPPUNIT_ASSERT_EQUAL(ExitInfo(ExitCode::SystemError, ExitCause::FileAccessError), IoHelper::openFile(filePath, file, 0)); + timeOutChecker.stop(); + CPPUNIT_ASSERT(timeOutChecker.lessOrEqualThan(200)); + CPPUNIT_ASSERT(!file.is_open()); + + // Check timeout + timeOutChecker.start(); + CPPUNIT_ASSERT_EQUAL(ExitInfo(ExitCode::SystemError, ExitCause::FileAccessError), IoHelper::openFile(filePath, file, 1)); + timeOutChecker.stop(); + CPPUNIT_ASSERT(timeOutChecker.between(1000, 1200)); + CPPUNIT_ASSERT(!file.is_open()); +} + +void TestIo::testOpenFileNonExisting() { + LocalTemporaryDirectory tempDir("testOpenFileNonExisting"); + SyncPath filePath = tempDir.path() / "testOpenFileNonExisting.txt"; + std::ifstream file; + TimeChecker timeOutChecker(true); + CPPUNIT_ASSERT_EQUAL(ExitInfo(ExitCode::SystemError, ExitCause::NotFound), IoHelper::openFile(filePath, file, 5)); + timeOutChecker.stop(); + CPPUNIT_ASSERT(timeOutChecker.lessOrEqualThan(200)); + CPPUNIT_ASSERT(!file.is_open()); +} + +void TestIo::testOpenLockedFileRemovedBeforeTimedOut() { + LocalTemporaryDirectory tempDir("testOpenLockedFileRemovedBeforeTimedOut"); + SyncPath filePath = tempDir.path() / "testOpenLockedFileRemovedBeforeTimedOut.txt"; + testhelpers::generateOrEditTestFile(filePath); + IoError ioError; + IoHelper::setRights(filePath, false, true, true, ioError); + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); + + std::ifstream file; + ExitInfo exitInfo = IoHelper::openFile(filePath, file, 5); + CPPUNIT_ASSERT_EQUAL(ExitInfo(ExitCode::SystemError, ExitCause::FileAccessError), exitInfo); + CPPUNIT_ASSERT(!file.is_open()); + + IoHelper::setRights(filePath, false, true, true, ioError); + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); + + std::function restoreRights = [filePath, &ioError]() { + Utility::msleep(900); + IoHelper::setRights(filePath, true, true, true, ioError); + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); + }; + + std::thread restoreRightsThread(restoreRights); + TimeChecker timeOutChecker(true); + CPPUNIT_ASSERT_EQUAL(ExitInfo(ExitCode::Ok), IoHelper::openFile(filePath, file, 4)); + timeOutChecker.stop(); + restoreRightsThread.join(); + CPPUNIT_ASSERT(timeOutChecker.between(1000, 1200)); + CPPUNIT_ASSERT(file.is_open()); + CPPUNIT_ASSERT(checkContent(file)); + file.close(); +} +} // namespace KDC diff --git a/test/libcommonserver/io/testremovexattr.cpp b/test/libcommonserver/io/testremovexattr.cpp new file mode 100644 index 000000000..582ff1e49 --- /dev/null +++ b/test/libcommonserver/io/testremovexattr.cpp @@ -0,0 +1,147 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testio.h" + +#include + +#ifdef _WIN32 +#include +#endif + +using namespace CppUnit; + +namespace KDC { + +void TestIo::testRemoveXAttr() { + // A regular file + { + const LocalTemporaryDirectory temporaryDirectory; + const SyncPath path = temporaryDirectory.path() / "file.txt"; + { + std::ofstream ofs(path); + ofs << "Some content.\n"; + ofs.close(); + } + + IoError ioError = IoError::Success; + CPPUNIT_ASSERT(_testObj->setXAttrValue(path, "status1", "to-be-deleted", ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + CPPUNIT_ASSERT(_testObj->setXAttrValue(path, "status2", "to-be-deleted", ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + CPPUNIT_ASSERT(_testObj->removeXAttrs(path, {"status1", "status2"}, ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + std::string value; + CPPUNIT_ASSERT(_testObj->getXAttrValue(path, "status1", value, ioError)); + CPPUNIT_ASSERT(ioError == IoError::AttrNotFound); + CPPUNIT_ASSERT(value.empty()); + + CPPUNIT_ASSERT(_testObj->getXAttrValue(path, "status2", value, ioError)); + CPPUNIT_ASSERT(ioError == IoError::AttrNotFound); + CPPUNIT_ASSERT(value.empty()); + } + + // A symlink + { + const LocalTemporaryDirectory temporaryDirectory; + const SyncPath targetPath = temporaryDirectory.path() / "file.txt"; + { + std::ofstream ofs(targetPath); + ofs << "Some content.\n"; + ofs.close(); + } + + const SyncPath linkPath = temporaryDirectory.path() / "link.txt"; + std::error_code ec; + std::filesystem::create_symlink(targetPath, linkPath, ec); + CPPUNIT_ASSERT(!ec); + + IoError ioError = IoError::Success; + CPPUNIT_ASSERT(_testObj->setXAttrValue(linkPath, "link-status", "to-be-deleted", ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + CPPUNIT_ASSERT(_testObj->removeXAttrs(linkPath, {"link-status"}, ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + std::string value; + CPPUNIT_ASSERT(_testObj->getXAttrValue(linkPath, "link-status", value, ioError)); + CPPUNIT_ASSERT(ioError == IoError::AttrNotFound); + CPPUNIT_ASSERT(value.empty()); + } + + // An alias + { + const LocalTemporaryDirectory temporaryDirectory; + const SyncPath targetPath = temporaryDirectory.path() / "file.txt"; + { + std::ofstream ofs(targetPath); + ofs << "Some content.\n"; + ofs.close(); + } + + const SyncPath aliasPath = temporaryDirectory.path() / "alias.txt"; + IoError ioError = IoError::Success; + CPPUNIT_ASSERT(IoHelper::createAliasFromPath(targetPath, aliasPath, ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + CPPUNIT_ASSERT(_testObj->setXAttrValue(aliasPath, "alias-status", "to-be-deleted", ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + CPPUNIT_ASSERT(_testObj->removeXAttrs(aliasPath, {"alias-status"}, ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + std::string value; + CPPUNIT_ASSERT(_testObj->getXAttrValue(aliasPath, "alias-status", value, ioError)); + CPPUNIT_ASSERT(ioError == IoError::AttrNotFound); + CPPUNIT_ASSERT(value.empty()); + } + + // A non-existing file + { + const SyncPath path = _localTestDirPath / "non-existing.jpg"; // This file does not exist. + IoError ioError = IoError::Success; + CPPUNIT_ASSERT(_testObj->removeXAttrs(path, {"status"}, ioError)); + CPPUNIT_ASSERT(ioError == IoError::NoSuchFileOrDirectory); + } + + // A regular file missing owner write permission: access denied expected + { + const LocalTemporaryDirectory temporaryDirectory; + const SyncPath path = temporaryDirectory.path() / "permission_less_file.txt"; + { + std::ofstream ofs(path); + ofs << "Some content.\n"; + } + + IoError ioError = IoError::Success; + CPPUNIT_ASSERT(_testObj->setXAttrValue(path, "status", "to-be-deleted", ioError)); + CPPUNIT_ASSERT(ioError == IoError::Success); + + std::filesystem::permissions(path, std::filesystem::perms::owner_write, std::filesystem::perm_options::remove); + + CPPUNIT_ASSERT(_testObj->removeXAttrs(path, {"status"}, ioError)); + CPPUNIT_ASSERT(ioError == IoError::AccessDenied); + + std::filesystem::permissions(path, std::filesystem::perms::owner_read, std::filesystem::perm_options::add); + } +} + +} // namespace KDC diff --git a/test/libcommonserver/log/testlog.cpp b/test/libcommonserver/log/testlog.cpp index b37030184..fc65aafe8 100644 --- a/test/libcommonserver/log/testlog.cpp +++ b/test/libcommonserver/log/testlog.cpp @@ -16,17 +16,20 @@ * along with this program. If not, see . */ +#include "test_utility/testhelpers.h" #include "testlog.h" + #include "libcommonserver/log/log.h" #include "libcommonserver/utility/utility.h" -#include "test_utility/localtemporarydirectory.h" #include "libcommonserver/io/iohelper.h" #include "libcommon/utility/utility.h" #include +#include #include + #include -#include +#include using namespace CppUnit; namespace KDC { @@ -37,14 +40,21 @@ void TestLog::setUp() { } void TestLog::testLog() { - LOG4CPLUS_TRACE(_logger, "Test trace log"); - LOG4CPLUS_DEBUG(_logger, "Test debug log"); - LOG4CPLUS_INFO(_logger, "Test info log"); - LOG4CPLUS_WARN(_logger, "Test warn log"); - LOG4CPLUS_ERROR(_logger, "Test error log"); - LOG4CPLUS_FATAL(_logger, "Test fatal log"); - - LOG4CPLUS_DEBUG(_logger, L"家屋香袈睷晦"); + LOG_DEBUG(_logger, "Test debug log " << (int) 1 << " " << true << " " << (double) 1.0); + LOG_INFO(_logger, "Test info log " << (unsigned int) 2 << " " << false << " " << (float) 1.0); + LOG_WARN(_logger, "Test warn log " << (long unsigned int) 3 << " " << false << " " << std::error_code{}); + LOG_ERROR(_logger, "Test error log " << (long long unsigned int) 4 << " " << true); + const QIODevice *device = nullptr; + LOG_FATAL(_logger, "Test fatal log" << std::error_code{} << " " << device); + + LOGW_DEBUG(_logger, L"Test debug log " << (int) 1 << L" " << true << L" " << (double) 1.0); + LOGW_INFO(_logger, L"Test info log " << (unsigned int) 2 << L" " << false << L" " << (float) 1.0); + LOGW_WARN(_logger, L"Test warn log " << (long unsigned int) 3 << L" " << false << L" " << std::error_code{}); + LOGW_ERROR(_logger, L"Test error log " << (long long unsigned int) 4 << L" " << true); + LOGW_FATAL(_logger, L"Test fatal log " << std::error_code{} << L" " << device << L" " << (long unsigned int) 5); + + // Wide characters are mandatory in this case as a bare call to LOG4CPLUS_DEBUG fails to print the string below. + LOGW_DEBUG(_logger, L"Test debug log " << L" " << L"家屋香袈睷晦"); CPPUNIT_ASSERT(true); } @@ -59,11 +69,13 @@ void TestLog::testLargeLogRolling(void) { customRollingFileAppender->setMaxFileSize(maxSize); LOG_DEBUG(_logger, "Ensure the log file is created"); + CPPUNIT_ASSERT_GREATER(1, countFilesInDirectory(_logDir)); // Generate a log larger than the max log file size. (log header is 50bytes) const auto testLog = std::string(maxSize, 'a'); LOG_DEBUG(_logger, testLog.c_str()); + CPPUNIT_ASSERT_GREATER(2, countFilesInDirectory(_logDir)); SyncPath rolledFile = _logDir / (Log::instance()->getLogFilePath().filename().string() + ".1.gz"); @@ -76,27 +88,31 @@ void TestLog::testLargeLogRolling(void) { } void TestLog::testExpiredLogFiles(void) { - // This test check that old archived log files are deleted after a certain time + // This test checks that old archived log files are deleted after a certain time clearLogDirectory(); // Generate a fake log file std::ofstream fakeLogFile(_logDir / APPLICATION_NAME "_fake.log.gz"); fakeLogFile << "Fake old log file" << std::endl; fakeLogFile.close(); - LOG_INFO(_logger, "Test log file expiration"); // Ensure the log file is created + LOG_INFO(_logger, "Test log file expiration"); // Ensure the log file is created. - CPPUNIT_ASSERT_EQUAL(2, countFilesInDirectory(_logDir)); // The current log file and the fake archived log file + CPPUNIT_ASSERT_EQUAL(2, countFilesInDirectory(_logDir)); // The current log file and the fake archived log file. auto *appender = static_cast(_logger.getAppender(Log::rfName).get()); - appender->setExpire(2); // 1 seconds + appender->setExpire(2); // 2 seconds Utility::msleep(1000); appender->checkForExpiredFiles(); - LOG_INFO(_logger, "Test log file expiration"); // Ensure the current log file is not older than 2 seconds - CPPUNIT_ASSERT_EQUAL(2, countFilesInDirectory(_logDir)); // The fake log file should not be deleted (< 2 seconds) + + const auto now = std::chrono::system_clock::now(); + KDC::testhelpers::setModificationDate(Log::instance()->getLogFilePath(), now); + + CPPUNIT_ASSERT_EQUAL(2, countFilesInDirectory(_logDir)); // The fake log file should not be deleted yet (< 2 seconds). Utility::msleep(1000); appender->checkForExpiredFiles(); - CPPUNIT_ASSERT_EQUAL(1, countFilesInDirectory(_logDir)); // The fake log file should be deleted + + CPPUNIT_ASSERT_EQUAL(1, countFilesInDirectory(_logDir)); // The fake log file should be deleted now. appender->setExpire(CommonUtility::logsPurgeRate * 24 * 3600); } @@ -113,6 +129,7 @@ int TestLog::countFilesInDirectory(const SyncPath &directory) const { count++; } CPPUNIT_ASSERT(endOfDirectory); + return count; } diff --git a/test/libcommonserver/utility/testutility.cpp b/test/libcommonserver/utility/testutility.cpp index 73b7a20e3..2ac06794b 100644 --- a/test/libcommonserver/utility/testutility.cpp +++ b/test/libcommonserver/utility/testutility.cpp @@ -19,9 +19,10 @@ #include "testutility.h" #include "test_utility/localtemporarydirectory.h" #include "config.h" - #include "libcommon/utility/utility.h" // CommonUtility::isSubDir -#include "Poco/URI.h" +#include "libcommonserver/log/log.h" + +#include #include #include @@ -105,8 +106,9 @@ void TestUtility::testMsSleep() { auto end = std::chrono::high_resolution_clock::now(); auto timeSpan = std::chrono::duration_cast(end - start); std::cout << " timeSpan=" << timeSpan.count(); - // CPPUNIT_ASSERT(timeSpan.count() > 800 && timeSpan.count() < 1200); - CPPUNIT_ASSERT(true); + long long timeSpanCount = static_cast(timeSpan.count()); + CPPUNIT_ASSERT_GREATER((long long) (800), timeSpanCount); + CPPUNIT_ASSERT_LESS((long long) (1200), timeSpanCount); } void TestUtility::testV2ws() { @@ -482,4 +484,29 @@ void TestUtility::testIsSameOrParentPath() { CPPUNIT_ASSERT(Utility::isDescendantOrEqual("a/b/c", "a")); } +void TestUtility::testUserName() { + std::string userName(Utility::userName()); + LOG_DEBUG(Log::instance()->getLogger(), "userName=" << userName.c_str()); + +#ifdef _WIN32 + const char *value = std::getenv("USERPROFILE"); + CPPUNIT_ASSERT(value); + const SyncPath homeDir(value); + LOGW_DEBUG(Log::instance()->getLogger(), L"homeDir=" << Utility::formatSyncPath(homeDir)); + + if (homeDir.filename().native() == std::wstring(L"systemprofile")) { + // CI execution + CPPUNIT_ASSERT_EQUAL(std::string("SYSTEM"), userName); + } else { + CPPUNIT_ASSERT_EQUAL(SyncName2Str(homeDir.filename().native()), userName); + } +#else + const char *value = std::getenv("HOME"); + CPPUNIT_ASSERT(value); + const SyncPath homeDir(value); + LOGW_DEBUG(Log::instance()->getLogger(), L"homeDir=" << Utility::formatSyncPath(homeDir)); + CPPUNIT_ASSERT_EQUAL(homeDir.filename().native(), userName); +#endif +} + } // namespace KDC diff --git a/test/libcommonserver/utility/testutility.h b/test/libcommonserver/utility/testutility.h index 37b85d564..0d936f39f 100644 --- a/test/libcommonserver/utility/testutility.h +++ b/test/libcommonserver/utility/testutility.h @@ -17,6 +17,7 @@ */ #include "testincludes.h" +#include "test_utility/testhelpers.h" #include "libcommonserver/utility/utility.h" using namespace CppUnit; @@ -61,6 +62,7 @@ class TestUtility : public CppUnit::TestFixture { CPPUNIT_TEST(testNormalizedSyncName); CPPUNIT_TEST(testNormalizedSyncPath); CPPUNIT_TEST(testIsSameOrParentPath); + CPPUNIT_TEST(testUserName); CPPUNIT_TEST_SUITE_END(); public: @@ -106,6 +108,7 @@ class TestUtility : public CppUnit::TestFixture { void testNormalizedSyncName(); void testNormalizedSyncPath(); void testIsSameOrParentPath(); + void testUserName(); private: std::unique_ptr _testObj; diff --git a/test/libparms/CMakeLists.txt b/test/libparms/CMakeLists.txt index 341de4955..fca3f59a1 100644 --- a/test/libparms/CMakeLists.txt +++ b/test/libparms/CMakeLists.txt @@ -34,17 +34,6 @@ target_link_libraries(${testparms_NAME} ${SQLITE3_LIBRARIES} ) -if (WIN32) - target_link_libraries(${testparms_NAME} - log4cplus::log4cplusU) -elseif(APPLE) - target_link_libraries(${testparms_NAME} - "/usr/local/lib/liblog4cplusU.dylib") -else() - target_link_libraries(${testparms_NAME} - "/usr/local/lib/liblog4cplusU.so") -endif() - if (WIN32) target_link_libraries(${testparms_NAME} debug diff --git a/test/libparms/db/testparmsdb.cpp b/test/libparms/db/testparmsdb.cpp index 956c1fc36..a3b1dfb89 100644 --- a/test/libparms/db/testparmsdb.cpp +++ b/test/libparms/db/testparmsdb.cpp @@ -394,6 +394,14 @@ void TestParmsDb::testAppState(void) { CPPUNIT_ASSERT_EQUAL(std::string("value"), std::get(valueRes)); i++; }; + + // Test add defaut value if and only if value is empty + CPPUNIT_ASSERT(ParmsDb::instance()->insertAppState(AppStateKey::Unknown, "test1", true)); + CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(AppStateKey::Unknown, value, found) && found); + CPPUNIT_ASSERT_EQUAL(std::string("test1"), std::get(value)); + CPPUNIT_ASSERT(ParmsDb::instance()->insertAppState(AppStateKey::Unknown, "test2", true)); + CPPUNIT_ASSERT(ParmsDb::instance()->selectAppState(AppStateKey::Unknown, value, found) && found); + CPPUNIT_ASSERT_EQUAL(std::string("test1"), std::get(value)); } #ifdef __APPLE__ diff --git a/test/libsyncengine/CMakeLists.txt b/test/libsyncengine/CMakeLists.txt index 417c562a2..d0e78417e 100644 --- a/test/libsyncengine/CMakeLists.txt +++ b/test/libsyncengine/CMakeLists.txt @@ -55,6 +55,8 @@ set(testsyncengine_SRCS if (APPLE) list(APPEND testsyncengine_SRCS update_detection/file_system_observer/testfolderwatchermac.h update_detection/file_system_observer/testfolderwatchermac.cpp) +elseif (UNIX) + list(APPEND testsyncengine_SRCS update_detection/file_system_observer/testfolderwatcherlinux.h update_detection/file_system_observer/testfolderwatcherlinux.cpp) endif () if (USE_OUR_OWN_SQLITE3) @@ -78,17 +80,6 @@ target_link_libraries(${testsyncengine_NAME} ${SQLITE3_LIBRARIES} ) -if (WIN32) - target_link_libraries(${testsyncengine_NAME} - log4cplus::log4cplusU) -elseif (APPLE) - target_link_libraries(${testsyncengine_NAME} - "/usr/local/lib/liblog4cplusU.dylib") -else () - target_link_libraries(${testsyncengine_NAME} - "/usr/local/lib/liblog4cplusU.so") -endif () - if (WIN32) target_link_libraries(${testsyncengine_NAME} debug @@ -104,12 +95,6 @@ else () endif () if (APPLE) - # Default sync exclude list - install(FILES ${CMAKE_SOURCE_DIR}/sync-exclude-osx.lst DESTINATION ${BIN_OUTPUT_DIRECTORY} RENAME sync-exclude.lst) - - # Default Lite Sync app exclude list - install(FILES ${CMAKE_SOURCE_DIR}/litesync-exclude.lst DESTINATION ${BIN_OUTPUT_DIRECTORY}) - install(CODE " message(STATUS \"Fixing library paths for ${testsyncengine_NAME}...\") execute_process(COMMAND \"install_name_tool\" -change libxxhash.0.dylib @rpath/libxxhash.0.8.2.dylib ${BIN_OUTPUT_DIRECTORY}/${testsyncengine_NAME}) diff --git a/test/libsyncengine/jobs/local/testlocaljobs.cpp b/test/libsyncengine/jobs/local/testlocaljobs.cpp index ec6e1e488..fad5d2716 100644 --- a/test/libsyncengine/jobs/local/testlocaljobs.cpp +++ b/test/libsyncengine/jobs/local/testlocaljobs.cpp @@ -33,7 +33,7 @@ namespace KDC { class LocalDeleteJobMockingTrash : public LocalDeleteJob { public: - LocalDeleteJobMockingTrash(const SyncPath &absolutePath) : LocalDeleteJob(absolutePath){}; + LocalDeleteJobMockingTrash(const SyncPath &absolutePath) : LocalDeleteJob(absolutePath) {}; void setMoveToTrashFailed(bool failed) { _moveToTrashFailed = failed; }; protected: @@ -159,7 +159,7 @@ void KDC::TestLocalJobs::testLocalDeleteJob() { public: LocalDeleteJobMock(const SyncPalInfo &syncInfo, const SyncPath &relativePath, bool isDehydratedPlaceholder, NodeId remoteId, bool forceToTrash = false) : - LocalDeleteJob(syncInfo, relativePath, isDehydratedPlaceholder, remoteId, forceToTrash){}; + LocalDeleteJob(syncInfo, relativePath, isDehydratedPlaceholder, remoteId, forceToTrash) {}; void setReturnedItemPath(const SyncPath &remoteItemPath) { _remoteItemPath = remoteItemPath; } protected: diff --git a/test/libsyncengine/jobs/network/testnetworkjobs.cpp b/test/libsyncengine/jobs/network/testnetworkjobs.cpp index 026083215..d60aac850 100644 --- a/test/libsyncengine/jobs/network/testnetworkjobs.cpp +++ b/test/libsyncengine/jobs/network/testnetworkjobs.cpp @@ -130,8 +130,10 @@ void TestNetworkJobs::tearDown() { ParmsDb::instance()->close(); ParmsDb::reset(); + MockIoHelperTestNetworkJobs::resetStdFunctions(); } + void TestNetworkJobs::testCreateDir() { const RemoteTemporaryDirectory remoteTmpDir(_driveDbId, _remoteDirId, "testCreateDir"); @@ -260,18 +262,56 @@ void TestNetworkJobs::testDelete() { } void TestNetworkJobs::testDownload() { - const LocalTemporaryDirectory temporaryDirectory("testDownload"); - SyncPath localDestFilePath = temporaryDirectory.path() / "test_file.txt"; - DownloadJob job(_driveDbId, testFileRemoteId, localDestFilePath, 0, 0, 0, false); - const ExitCode exitCode = job.runSynchronously(); - CPPUNIT_ASSERT(exitCode == ExitCode::Ok); - - CPPUNIT_ASSERT(std::filesystem::exists(localDestFilePath)); + { + const LocalTemporaryDirectory temporaryDirectory("testDownload"); + SyncPath localDestFilePath = temporaryDirectory.path() / "test_file.txt"; + DownloadJob job(_driveDbId, testFileRemoteId, localDestFilePath, 0, 0, 0, false); + const ExitCode exitCode = job.runSynchronously(); + CPPUNIT_ASSERT(exitCode == ExitCode::Ok); + + CPPUNIT_ASSERT(std::filesystem::exists(localDestFilePath)); + + // Check file content + std::ifstream ifs(localDestFilePath.string().c_str()); + std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + CPPUNIT_ASSERT(content == "test"); + } - // Check file content - std::ifstream ifs(localDestFilePath.string().c_str()); - std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - CPPUNIT_ASSERT(content == "test"); + // Cross Device Link + { + const LocalTemporaryDirectory tmpdirectory("testDownloadCrossDeviceLnk_TMP"); + const LocalTemporaryDirectory destDirectory("testDownloadCrossDeviceLnk_TMP"); + SyncPath localDestFilePath = destDirectory.path() / bigFileName; + + std::function MockTempDirectoryPath = [&tmpdirectory](std::error_code &ec) { + ec.clear(); + return tmpdirectory.path(); + }; + std::function MockRename = + [](const SyncPath &srcPath, const SyncPath &destPath, std::error_code &ec) { +#ifdef _WIN32 + ec = std::make_error_code(static_cast(ERROR_NOT_SAME_DEVICE)); +#else + ec = std::make_error_code(std::errc::cross_device_link); +#endif + }; + MockIoHelperTestNetworkJobs::setStdRename(MockRename); + MockIoHelperTestNetworkJobs::setStdTempDirectoryPath(MockTempDirectoryPath); + + DownloadJob job(_driveDbId, testFileRemoteId, localDestFilePath, 0, 0, 0, false); + const ExitCode exitCode = job.runSynchronously(); + CPPUNIT_ASSERT(exitCode == ExitCode::Ok); + + CPPUNIT_ASSERT(std::filesystem::exists(localDestFilePath)); + + // Check that the tmp file has been deleted + CPPUNIT_ASSERT(std::filesystem::is_empty(tmpdirectory.path())); + + // Check file content + std::ifstream ifs(localDestFilePath.string().c_str()); + std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + CPPUNIT_ASSERT(content == "test"); + } } void TestNetworkJobs::testDownloadAborted() { @@ -649,8 +689,8 @@ void TestNetworkJobs::testRename() { } void TestNetworkJobs::testUpload() { + // Successful upload const RemoteTemporaryDirectory remoteTmpDir(_driveDbId, _remoteDirId, "testUpload"); - SyncPath localFilePath = testhelpers::localTestDirPath / bigFileDirName / bigFileName; UploadJob job(_driveDbId, localFilePath, localFilePath.filename().native(), remoteTmpDir.id(), 0); @@ -899,7 +939,6 @@ void TestNetworkJobs::testDriveUploadSessionAsynchronousAborted() { void TestNetworkJobs::testGetAppVersionInfo() { const auto appUid = "1234567890"; - // Without user IDs { GetAppVersionJob job(CommonUtility::platform(), appUid); @@ -909,27 +948,35 @@ void TestNetworkJobs::testGetAppVersionInfo() { CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid()); + CPPUNIT_ASSERT(job.getProdVersionInfo().isValid()); } // With 1 user ID { - GetAppVersionJob job(CommonUtility::platform(), appUid, {123}); - job.runSynchronously(); - CPPUNIT_ASSERT(!job.hasHttpError()); - CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Internal).isValid()); - CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid()); - CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid()); - CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid()); - } - // With several user IDs - { - GetAppVersionJob job(CommonUtility::platform(), appUid, {123, 456, 789}); + User user; + bool found = false; + ParmsDb::instance()->selectUser(_userDbId, user, found); + + GetAppVersionJob job(CommonUtility::platform(), appUid, {user.userId()}); job.runSynchronously(); CPPUNIT_ASSERT(!job.hasHttpError()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Internal).isValid()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid()); CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid()); + CPPUNIT_ASSERT(job.getProdVersionInfo().isValid()); } + // // With several user IDs + // TODO : commented out because we need valid user IDs but we have only one available in tests for now + // { + // GetAppVersionJob job(CommonUtility::platform(), appUid, {123, 456, 789}); + // job.runSynchronously(); + // CPPUNIT_ASSERT(!job.hasHttpError()); + // CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Internal).isValid()); + // CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Beta).isValid()); + // CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Next).isValid()); + // CPPUNIT_ASSERT(job.getVersionInfo(DistributionChannel::Prod).isValid()); + // CPPUNIT_ASSERT(job.getProdVersionInfo().isValid()); + // } } void TestNetworkJobs::testDirectDownload() { diff --git a/test/libsyncengine/jobs/network/testnetworkjobs.h b/test/libsyncengine/jobs/network/testnetworkjobs.h index d5b2c60ca..13bf34837 100644 --- a/test/libsyncengine/jobs/network/testnetworkjobs.h +++ b/test/libsyncengine/jobs/network/testnetworkjobs.h @@ -20,9 +20,29 @@ #include "testincludes.h" #include "utility/types.h" +#include "libcommonserver/io/iohelper.h" using namespace CppUnit; namespace KDC { +class MockIoHelperTestNetworkJobs : public IoHelper { + public: + static void setStdRename(std::function rename) { + IoHelper::_rename = rename; + } + static void setStdTempDirectoryPath(std::function tempDirectoryPath) { + IoHelper::_tempDirectoryPath = tempDirectoryPath; + } + static void resetStdFunctions() { + _isDirectory = static_cast(&std::filesystem::is_directory); + _isSymlink = static_cast(&std::filesystem::is_symlink); + _rename = static_cast( + std::filesystem::rename); + _readSymlink = static_cast(&std::filesystem::read_symlink); + _fileSize = static_cast(&std::filesystem::file_size); + _tempDirectoryPath = static_cast(&std::filesystem::temp_directory_path); + } +}; + class TestNetworkJobs : public CppUnit::TestFixture { public: CPPUNIT_TEST_SUITE(TestNetworkJobs); diff --git a/test/libsyncengine/jobs/network/testsnapshotitemhandler.h b/test/libsyncengine/jobs/network/testsnapshotitemhandler.h index bb4685753..a66cbee97 100644 --- a/test/libsyncengine/jobs/network/testsnapshotitemhandler.h +++ b/test/libsyncengine/jobs/network/testsnapshotitemhandler.h @@ -49,8 +49,6 @@ struct Result { bool success{true}; std::string message; }; -static std::string makeMessage(const CppUnit::Exception &e); -static Result compare(const SnapshotItem &lhs, const SnapshotItem &rhs) noexcept; } // namespace snapshotitem_checker } // namespace KDC diff --git a/test/libsyncengine/jobs/testjobmanager.cpp b/test/libsyncengine/jobs/testjobmanager.cpp index 5117d0e0b..9b6db9a1e 100644 --- a/test/libsyncengine/jobs/testjobmanager.cpp +++ b/test/libsyncengine/jobs/testjobmanager.cpp @@ -417,7 +417,8 @@ void TestJobManager::generateBigFiles(const SyncPath &dirPath, int size, int cou bigFilePath = SyncPath(dirPath) / fileName.str(); { std::ofstream ofs(bigFilePath, std::ios_base::in | std::ios_base::trunc); - for (int i = 0; i < static_cast(round(static_cast(size * 1000000) / static_cast(str.length()))); i++) { + for (int i = 0; i < static_cast(round(static_cast(size * 1000000) / static_cast(str.length()))); + i++) { ofs << str; } } diff --git a/test/libsyncengine/propagation/executor/testexecutorworker.cpp b/test/libsyncengine/propagation/executor/testexecutorworker.cpp index aeb04e229..e05200e4e 100644 --- a/test/libsyncengine/propagation/executor/testexecutorworker.cpp +++ b/test/libsyncengine/propagation/executor/testexecutorworker.cpp @@ -69,8 +69,11 @@ void TestExecutorWorker::setUp() { } _syncPal = std::make_shared(_sync.dbId(), KDRIVE_VERSION_STRING); + _syncPal->createSharedObjects(); _syncPal->createWorkers(); _syncPal->syncDb()->setAutoDelete(true); + + _executorWorker = std::shared_ptr(new ExecutorWorker(_syncPal, "Executor", "EXEC")); } void TestExecutorWorker::tearDown() { @@ -103,7 +106,7 @@ void TestExecutorWorker::testCheckLiteSyncInfoForCreate() { }); bool isDehydratedPlaceholder = false; - _syncPal->_executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); + _executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); CPPUNIT_ASSERT(!isDehydratedPlaceholder); } @@ -120,7 +123,7 @@ void TestExecutorWorker::testCheckLiteSyncInfoForCreate() { }); bool isDehydratedPlaceholder = false; - _syncPal->_executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); + _executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); CPPUNIT_ASSERT(isDehydratedPlaceholder); } @@ -137,7 +140,7 @@ void TestExecutorWorker::testCheckLiteSyncInfoForCreate() { }); bool isDehydratedPlaceholder = false; - _syncPal->_executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); + _executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); CPPUNIT_ASSERT(!isDehydratedPlaceholder); } @@ -154,14 +157,15 @@ void TestExecutorWorker::testCheckLiteSyncInfoForCreate() { }); bool isDehydratedPlaceholder = false; - _syncPal->_executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); + _executorWorker->checkLiteSyncInfoForCreate(opPtr, "/", isDehydratedPlaceholder); CPPUNIT_ASSERT(!isDehydratedPlaceholder); } #endif } -SyncOpPtr TestExecutorWorker::generateSyncOperation(const DbNodeId dbNodeId, const SyncName &filename) { +SyncOpPtr TestExecutorWorker::generateSyncOperation(const DbNodeId dbNodeId, const SyncName &filename, + const OperationType opType) { auto node = std::make_shared(dbNodeId, ReplicaSide::Local, filename, NodeType::File, OperationType::None, "lid", testhelpers::defaultTime, testhelpers::defaultTime, testhelpers::defaultFileSize, _syncPal->updateTree(ReplicaSide::Local)->rootNode()); @@ -172,16 +176,203 @@ SyncOpPtr TestExecutorWorker::generateSyncOperation(const DbNodeId dbNodeId, con SyncOpPtr op = std::make_shared(); op->setAffectedNode(node); op->setCorrespondingNode(correspondingNode); + op->setType(opType); + + return op; +} + + +SyncOpPtr TestExecutorWorker::generateSyncOperationWithNestedNodes(const DbNodeId dbNodeId, const SyncName &parentFilename, + const OperationType opType, const NodeType nodeType) { + auto parentNode = + std::make_shared(dbNodeId, ReplicaSide::Local, parentFilename, NodeType::Directory, OperationType::None, + "local_parent_id", testhelpers::defaultTime, testhelpers::defaultTime, + testhelpers::defaultFileSize, _syncPal->updateTree(ReplicaSide::Local)->rootNode()); + + auto node = std::make_shared(dbNodeId, ReplicaSide::Local, Str("test_file.txt"), nodeType, OperationType::None, + "local_child_id", testhelpers::defaultTime, testhelpers::defaultTime, + testhelpers::defaultFileSize, parentNode); + + + auto correspondingNode = + std::make_shared(dbNodeId, ReplicaSide::Remote, Str("test_file.txt"), nodeType, OperationType::None, + "remote_child_id", testhelpers::defaultTime, testhelpers::defaultTime, + testhelpers::defaultFileSize, _syncPal->updateTree(ReplicaSide::Remote)->rootNode()); + + SyncOpPtr op = std::make_shared(); + op->setAffectedNode(node); + op->setCorrespondingNode(correspondingNode); + op->setType(opType); return op; } +class ExecutorWorkerMock : public ExecutorWorker { + public: + ExecutorWorkerMock(std::shared_ptr syncPal, const std::string &name, const std::string &shortName) : + ExecutorWorker(syncPal, name, shortName) {}; + + using ArgsMap = std::map, std::shared_ptr>; + void setCorrespondingNodeInOtherTree(ArgsMap nodeMap) { _correspondingNodeInOtherTree = nodeMap; }; + + protected: + ArgsMap _correspondingNodeInOtherTree; + virtual std::shared_ptr correspondingNodeInOtherTree(const std::shared_ptr node) { + if (auto it = _correspondingNodeInOtherTree.find(node); it != _correspondingNodeInOtherTree.cend()) { + return it->second; + } + + return nullptr; + }; +}; + +void TestExecutorWorker::testIsValidDestination() { + // Always true if the target side is local or unknown + { + SyncOpPtr op = generateSyncOperation(1, Str("test_file.txt")); + CPPUNIT_ASSERT(_executorWorker->isValidDestination(op)); + } + // Always true if the operation is not of type Create + { + SyncOpPtr op = generateSyncOperation(1, Str("test_file.txt")); + op->setTargetSide(ReplicaSide::Remote); + CPPUNIT_ASSERT(_executorWorker->isValidDestination(op)); + } + // Always true if the item is created on the local replica at the root of the synchronisation folder + { + SyncOpPtr op = generateSyncOperation(1, Str("parent_dir")); + op->setTargetSide(ReplicaSide::Remote); + CPPUNIT_ASSERT(_executorWorker->isValidDestination(op)); + } + + + const auto executorWorkerMock = std::shared_ptr(new ExecutorWorkerMock(_syncPal, "Executor", "EXEC")); + // False if the item created on the local replica is not at the root of the synchronisation folder and has no + // corresponding parent node. + { + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_file.txt"), OperationType::Create, NodeType::File); + executorWorkerMock->setCorrespondingNodeInOtherTree({{op->affectedNode()->parentNode(), nullptr}}); + op->setTargetSide(ReplicaSide::Remote); + CPPUNIT_ASSERT(!executorWorkerMock->isValidDestination(op)); + } + + const auto root = _syncPal->updateTree(ReplicaSide::Remote)->rootNode(); + + // False if the item created on the local replica is not at the root of the synchronisation folder and has a + // corresponding parent node with no id. + { + const auto correspondingParentNode = std::make_shared( + 666, ReplicaSide::Remote, Str("parent_dir"), NodeType::Directory, OperationType::None, std::nullopt, + testhelpers::defaultTime, testhelpers::defaultTime, testhelpers::defaultFileSize, root); + + + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_file.txt"), OperationType::Create, NodeType::File); + executorWorkerMock->setCorrespondingNodeInOtherTree({{op->affectedNode()->parentNode(), correspondingParentNode}}); + op->setTargetSide(ReplicaSide::Remote); + CPPUNIT_ASSERT(!executorWorkerMock->isValidDestination(op)); + } + + const auto correspondingParentCommonDocsNode = std::make_shared( + 666, ReplicaSide::Remote, Utility::commonDocumentsFolderName(), NodeType::Directory, OperationType::None, + "common_docs_id", testhelpers::defaultTime, testhelpers::defaultTime, testhelpers::defaultFileSize, root); + + // False if the item created on the local replica is a file and has Common Documents as corresponding parent node. + { + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_file.txt"), OperationType::Create, NodeType::File); + op->setTargetSide(ReplicaSide::Remote); + executorWorkerMock->setCorrespondingNodeInOtherTree( + {{op->affectedNode()->parentNode(), correspondingParentCommonDocsNode}}); + CPPUNIT_ASSERT(!executorWorkerMock->isValidDestination(op)); + } + + // True if the item created on the local replica is a directory and has Common Documents as corresponding parent node. + { + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_dir"), OperationType::Create, NodeType::Directory); + op->setTargetSide(ReplicaSide::Remote); + executorWorkerMock->setCorrespondingNodeInOtherTree( + {{op->affectedNode()->parentNode(), correspondingParentCommonDocsNode}}); + CPPUNIT_ASSERT(executorWorkerMock->isValidDestination(op)); + } + + const auto correspondingParentSharedNode = std::make_shared( + 777, ReplicaSide::Remote, Utility::sharedFolderName(), NodeType::Directory, OperationType::None, "shared_id", + testhelpers::defaultTime, testhelpers::defaultTime, testhelpers::defaultFileSize, root); + + // False if the item is created on the local replica is a file and has Shared as corresponding parent node. + { + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_file.txt"), OperationType::Create, NodeType::File); + op->setTargetSide(ReplicaSide::Remote); + executorWorkerMock->setCorrespondingNodeInOtherTree({{op->affectedNode()->parentNode(), correspondingParentSharedNode}}); + CPPUNIT_ASSERT(!executorWorkerMock->isValidDestination(op)); + } + + // False if the item created on the local replica is a directory and has Shared as corresponding parent node. + { + SyncOpPtr op = generateSyncOperationWithNestedNodes(1, Str("test_dir"), OperationType::Create, NodeType::Directory); + op->setTargetSide(ReplicaSide::Remote); + executorWorkerMock->setCorrespondingNodeInOtherTree({{op->affectedNode()->parentNode(), correspondingParentSharedNode}}); + CPPUNIT_ASSERT(!executorWorkerMock->isValidDestination(op)); + } +} + +void TestExecutorWorker::testTerminatedJobsQueue() { + TerminatedJobsQueue terminatedJobsQueue; + + int ended = 0; // count the number of ended threads + + // Function objects to be used in the thread + std::function inserter = [&terminatedJobsQueue, &ended](const UniqueId id) { + terminatedJobsQueue.push(id); + ended++; + }; + std::function popper = [&terminatedJobsQueue, &ended]() { + terminatedJobsQueue.pop(); + ended++; + }; + std::function fronter = [&terminatedJobsQueue, &ended]() { + [[maybe_unused]] auto foo = terminatedJobsQueue.front(); + ended++; + }; + std::function emptyChecker = [&terminatedJobsQueue, &ended]() { + [[maybe_unused]] auto foo = terminatedJobsQueue.empty(); + ended++; + }; + + // Check that all functions are thread safe + terminatedJobsQueue.lock(); // Lock the queue for the current thread + + std::thread t1(inserter, 1); + Utility::msleep(10); // Give enough time for the thread to terminate + CPPUNIT_ASSERT_EQUAL(0, ended); + + std::thread t2(fronter); + Utility::msleep(10); + CPPUNIT_ASSERT_EQUAL(0, ended); + + std::thread t3(popper); + Utility::msleep(10); + CPPUNIT_ASSERT_EQUAL(0, ended); + + std::thread t4(emptyChecker); + Utility::msleep(10); + CPPUNIT_ASSERT_EQUAL(0, ended); + + terminatedJobsQueue.unlock(); // Unlock the queue for the current thread + Utility::msleep(10); + CPPUNIT_ASSERT_EQUAL(4, ended); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); // Wait for all threads to finish. +} + void TestExecutorWorker::testLogCorrespondingNodeErrorMsg() { SyncOpPtr op = generateSyncOperation(1, Str("test_file.txt")); - _syncPal->_executorWorker->logCorrespondingNodeErrorMsg(op); + _executorWorker->logCorrespondingNodeErrorMsg(op); op->setCorrespondingNode(nullptr); - _syncPal->_executorWorker->logCorrespondingNodeErrorMsg(op); + _executorWorker->logCorrespondingNodeErrorMsg(op); } void TestExecutorWorker::testFixModificationDate() { @@ -204,7 +395,7 @@ void TestExecutorWorker::testFixModificationDate() { _syncPal->syncDb()->insertNode(dbNode, dbNodeId, constraintError); SyncOpPtr op = generateSyncOperation(dbNodeId, filename); - CPPUNIT_ASSERT(_syncPal->_executorWorker->fixModificationDate(op, path)); + CPPUNIT_ASSERT(_executorWorker->fixModificationDate(op, path)); FileStat filestat; IoError ioError = IoError::Unknown; @@ -219,28 +410,28 @@ void TestExecutorWorker::testAffectedUpdateTree() { auto syncOp = std::make_shared(); syncOp->setTargetSide(ReplicaSide::Local); - CPPUNIT_ASSERT_EQUAL(ReplicaSide::Remote, _syncPal->_executorWorker->affectedUpdateTree(syncOp)->side()); + CPPUNIT_ASSERT_EQUAL(ReplicaSide::Remote, _executorWorker->affectedUpdateTree(syncOp)->side()); syncOp->setTargetSide(ReplicaSide::Remote); - CPPUNIT_ASSERT_EQUAL(ReplicaSide::Local, _syncPal->_executorWorker->affectedUpdateTree(syncOp)->side()); + CPPUNIT_ASSERT_EQUAL(ReplicaSide::Local, _executorWorker->affectedUpdateTree(syncOp)->side()); // ReplicaSide::Unknown case syncOp->setTargetSide(ReplicaSide::Unknown); - CPPUNIT_ASSERT_EQUAL(std::shared_ptr(nullptr), _syncPal->_executorWorker->affectedUpdateTree(syncOp)); + CPPUNIT_ASSERT_EQUAL(std::shared_ptr(nullptr), _executorWorker->affectedUpdateTree(syncOp)); } void TestExecutorWorker::testTargetUpdateTree() { // Normal cases auto syncOp = std::make_shared(); syncOp->setTargetSide(ReplicaSide::Local); - CPPUNIT_ASSERT_EQUAL(ReplicaSide::Local, _syncPal->_executorWorker->targetUpdateTree(syncOp)->side()); + CPPUNIT_ASSERT_EQUAL(ReplicaSide::Local, _executorWorker->targetUpdateTree(syncOp)->side()); syncOp->setTargetSide(ReplicaSide::Remote); - CPPUNIT_ASSERT_EQUAL(ReplicaSide::Remote, _syncPal->_executorWorker->targetUpdateTree(syncOp)->side()); + CPPUNIT_ASSERT_EQUAL(ReplicaSide::Remote, _executorWorker->targetUpdateTree(syncOp)->side()); // ReplicaSide::Unknown case syncOp->setTargetSide(ReplicaSide::Unknown); - CPPUNIT_ASSERT_EQUAL(std::shared_ptr(nullptr), _syncPal->_executorWorker->targetUpdateTree(syncOp)); + CPPUNIT_ASSERT_EQUAL(std::shared_ptr(nullptr), _executorWorker->targetUpdateTree(syncOp)); } void TestExecutorWorker::testRemoveDependentOps() { @@ -272,8 +463,8 @@ void TestExecutorWorker::testRemoveDependentOps() { _syncPal->_syncOps->pushOp(op2Create); _syncPal->_syncOps->pushOp(op3Create); - _syncPal->_executorWorker->_opList = _syncPal->_syncOps->opSortedList(); - _syncPal->_executorWorker->removeDependentOps(op1Create); // op1Create failed, we should remove op2Create and op3Create. + _executorWorker->_opList = _syncPal->_syncOps->opSortedList(); + _executorWorker->removeDependentOps(op1Create); // op1Create failed, we should remove op2Create and op3Create. CPPUNIT_ASSERT(opsExist(op1Create)); CPPUNIT_ASSERT(!opsExist(op2Create)); @@ -304,8 +495,8 @@ void TestExecutorWorker::testRemoveDependentOps() { _syncPal->_syncOps->pushOp(op1Create); _syncPal->_syncOps->pushOp(op2Move); - _syncPal->_executorWorker->_opList = _syncPal->_syncOps->opSortedList(); - _syncPal->_executorWorker->removeDependentOps(op1Create); // op2Move failed, we should remove op2Edit. + _executorWorker->_opList = _syncPal->_syncOps->opSortedList(); + _executorWorker->removeDependentOps(op1Create); // op2Move failed, we should remove op2Edit. CPPUNIT_ASSERT(opsExist(op1Create)); CPPUNIT_ASSERT(!opsExist(op2Move)); } @@ -338,15 +529,15 @@ void TestExecutorWorker::testRemoveDependentOps() { _syncPal->_syncOps->pushOp(op1Move); _syncPal->_syncOps->pushOp(op2Move); - _syncPal->_executorWorker->_opList = _syncPal->_syncOps->opSortedList(); - _syncPal->_executorWorker->removeDependentOps(op1Move); // op2Move failed, we should remove op2Edit. + _executorWorker->_opList = _syncPal->_syncOps->opSortedList(); + _executorWorker->removeDependentOps(op1Move); // op2Move failed, we should remove op2Edit. CPPUNIT_ASSERT(opsExist(op1Move)); CPPUNIT_ASSERT(!opsExist(op2Move)); } } bool TestExecutorWorker::opsExist(SyncOpPtr op) { - for (const auto &opId: _syncPal->_executorWorker->_opList) { + for (const auto &opId: _executorWorker->_opList) { if (_syncPal->_syncOps->getOp(opId) == op) { return true; } diff --git a/test/libsyncengine/propagation/executor/testexecutorworker.h b/test/libsyncengine/propagation/executor/testexecutorworker.h index d4de7790f..8d2d47a23 100644 --- a/test/libsyncengine/propagation/executor/testexecutorworker.h +++ b/test/libsyncengine/propagation/executor/testexecutorworker.h @@ -19,7 +19,6 @@ #pragma once #include "testincludes.h" -#include "vfs.h" #include "propagation/executor/executorworker.h" #include "test_utility/localtemporarydirectory.h" @@ -33,6 +32,8 @@ class TestExecutorWorker : public CppUnit::TestFixture { CPPUNIT_TEST(testTargetUpdateTree); CPPUNIT_TEST(testLogCorrespondingNodeErrorMsg); CPPUNIT_TEST(testRemoveDependentOps); + CPPUNIT_TEST(testIsValidDestination); + CPPUNIT_TEST(testTerminatedJobsQueue); CPPUNIT_TEST_SUITE_END(); public: @@ -46,12 +47,18 @@ class TestExecutorWorker : public CppUnit::TestFixture { void testTargetUpdateTree(); void testLogCorrespondingNodeErrorMsg(); void testRemoveDependentOps(); + void testIsValidDestination(); + void testTerminatedJobsQueue(); bool opsExist(SyncOpPtr op); - SyncOpPtr generateSyncOperation(const DbNodeId dbNodeId, const SyncName &filename); + SyncOpPtr generateSyncOperation(const DbNodeId dbNodeId, const SyncName &filename, + const OperationType opType = OperationType::None); + SyncOpPtr generateSyncOperationWithNestedNodes(const DbNodeId dbNodeId, const SyncName &filename, + const OperationType opType, const NodeType nodeType); std::shared_ptr _syncPal; Sync _sync; + std::shared_ptr _executorWorker; LocalTemporaryDirectory _localTempDir{"TestExecutorWorker"}; }; diff --git a/test/libsyncengine/propagation/executor/testintegration.cpp b/test/libsyncengine/propagation/executor/testintegration.cpp index e222e84cf..5ee9c99b5 100644 --- a/test/libsyncengine/propagation/executor/testintegration.cpp +++ b/test/libsyncengine/propagation/executor/testintegration.cpp @@ -108,6 +108,7 @@ void TestIntegration::setUp() { } _syncPal = std::make_shared(sync.dbId(), KDRIVE_VERSION_STRING); + _syncPal->createSharedObjects(); // Insert items to blacklist SyncNodeCache::instance()->update(_syncPal->syncDbId(), SyncNodeType::BlackList, {test_beaucoupRemoteId}); diff --git a/test/libsyncengine/propagation/operation_sorter/testoperationsorterworker.cpp b/test/libsyncengine/propagation/operation_sorter/testoperationsorterworker.cpp index 803d47b10..c277ff5c3 100644 --- a/test/libsyncengine/propagation/operation_sorter/testoperationsorterworker.cpp +++ b/test/libsyncengine/propagation/operation_sorter/testoperationsorterworker.cpp @@ -42,6 +42,7 @@ void TestOperationSorterWorker::setUp() { std::filesystem::remove(syncDbPath); _syncPal = std::make_shared(syncDbPath, KDRIVE_VERSION_STRING, true); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->_operationsSorterWorker = std::make_shared(_syncPal, "Operation Sorter", "OPSO"); } @@ -53,16 +54,16 @@ void TestOperationSorterWorker::tearDown() { } void TestOperationSorterWorker::testMoveFirstAfterSecond() { - const auto node1 = std::make_shared(ReplicaSide::Local, Str("1"), NodeType::Directory, - OperationType::None, std::nullopt, 0, 0, 12345, nullptr); - const auto node2 = std::make_shared(ReplicaSide::Local, Str("2"), NodeType::Directory, - OperationType::None, std::nullopt, 0, 0, 12345, nullptr); - const auto node3 = std::make_shared(ReplicaSide::Local, Str("3"), NodeType::Directory, - OperationType::None, std::nullopt, 0, 0, 12345, nullptr); - const auto node4 = std::make_shared(ReplicaSide::Local, Str("4"), NodeType::Directory, - OperationType::None, std::nullopt, 0, 0, 12345, nullptr); - const auto node5 = std::make_shared(ReplicaSide::Local, Str("5"), NodeType::Directory, - OperationType::None, std::nullopt, 0, 0, 12345, nullptr); + const auto node1 = std::make_shared(ReplicaSide::Local, Str("1"), NodeType::Directory, OperationType::None, + std::nullopt, 0, 0, 12345, nullptr); + const auto node2 = std::make_shared(ReplicaSide::Local, Str("2"), NodeType::Directory, OperationType::None, + std::nullopt, 0, 0, 12345, nullptr); + const auto node3 = std::make_shared(ReplicaSide::Local, Str("3"), NodeType::Directory, OperationType::None, + std::nullopt, 0, 0, 12345, nullptr); + const auto node4 = std::make_shared(ReplicaSide::Local, Str("4"), NodeType::Directory, OperationType::None, + std::nullopt, 0, 0, 12345, nullptr); + const auto node5 = std::make_shared(ReplicaSide::Local, Str("5"), NodeType::Directory, OperationType::None, + std::nullopt, 0, 0, 12345, nullptr); const auto op1 = std::make_shared(); const auto op2 = std::make_shared(); const auto op3 = std::make_shared(); diff --git a/test/libsyncengine/reconciliation/conflict_finder/testconflictfinderworker.cpp b/test/libsyncengine/reconciliation/conflict_finder/testconflictfinderworker.cpp index f704c07a7..c4d6e872e 100644 --- a/test/libsyncengine/reconciliation/conflict_finder/testconflictfinderworker.cpp +++ b/test/libsyncengine/reconciliation/conflict_finder/testconflictfinderworker.cpp @@ -32,8 +32,9 @@ void TestConflictFinderWorker::setUp() { SyncPath syncDbPath = Db::makeDbName(1, 1, 1, 1, alreadyExists); std::filesystem::remove(syncDbPath); - _syncPal = std::shared_ptr(new SyncPal(syncDbPath, KDRIVE_VERSION_STRING, true)); + _syncPal = std::make_shared(syncDbPath, KDRIVE_VERSION_STRING, true); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->_conflictFinderWorker = std::shared_ptr(new ConflictFinderWorker(_syncPal, "Conflict Finder", "COFD")); @@ -521,8 +522,7 @@ void TestConflictFinderWorker::testCase55b() { _syncPal->updateTree(ReplicaSide::Remote)->insertNode(rNodeA); // Conflict Situation - nodeA->setChangeEvents( - OperationType::Edit); + nodeA->setChangeEvents(OperationType::Edit); nodeA->setMoveOrigin("A"); nodeA->setName(Str("B")); rNodeA->setChangeEvents(OperationType::Edit); diff --git a/test/libsyncengine/reconciliation/conflict_resolver/testconflictresolverworker.cpp b/test/libsyncengine/reconciliation/conflict_resolver/testconflictresolverworker.cpp index e7183a0cd..14c6a348e 100644 --- a/test/libsyncengine/reconciliation/conflict_resolver/testconflictresolverworker.cpp +++ b/test/libsyncengine/reconciliation/conflict_resolver/testconflictresolverworker.cpp @@ -35,6 +35,7 @@ void TestConflictResolverWorker::setUp() { std::filesystem::remove(syncDbPath); _syncPal = std::make_shared(syncDbPath, KDRIVE_VERSION_STRING, true); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->_conflictResolverWorker = std::make_shared(_syncPal, "Conflict Resolver", "CORE"); diff --git a/test/libsyncengine/reconciliation/operation_generator/testoperationgeneratorworker.cpp b/test/libsyncengine/reconciliation/operation_generator/testoperationgeneratorworker.cpp index 3a7560d58..8d85b3465 100644 --- a/test/libsyncengine/reconciliation/operation_generator/testoperationgeneratorworker.cpp +++ b/test/libsyncengine/reconciliation/operation_generator/testoperationgeneratorworker.cpp @@ -38,6 +38,7 @@ void KDC::TestOperationGeneratorWorker::setUp() { std::filesystem::remove(syncDbPath); _syncPal = std::make_shared(syncDbPath, KDRIVE_VERSION_STRING, true); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->_operationsGeneratorWorker = std::make_shared(_syncPal, "Operation Generator", "OPGE"); diff --git a/test/libsyncengine/reconciliation/platform_inconsistency_checker/testplatforminconsistencycheckerworker.cpp b/test/libsyncengine/reconciliation/platform_inconsistency_checker/testplatforminconsistencycheckerworker.cpp index a7d823add..70c2fb43b 100644 --- a/test/libsyncengine/reconciliation/platform_inconsistency_checker/testplatforminconsistencycheckerworker.cpp +++ b/test/libsyncengine/reconciliation/platform_inconsistency_checker/testplatforminconsistencycheckerworker.cpp @@ -65,6 +65,7 @@ void TestPlatformInconsistencyCheckerWorker::setUp() { // Create SyncPal _syncPal = std::make_shared(sync.dbId(), KDRIVE_VERSION_STRING); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->_tmpBlacklistManager = std::make_shared(_syncPal); _syncPal->_platformInconsistencyCheckerWorker = @@ -278,7 +279,7 @@ void TestPlatformInconsistencyCheckerWorker::testExecute() { #if defined(WIN32) || defined(__APPLE__) CPPUNIT_ASSERT(exactly1exist); #else - (void)exactly1exist; + (void) exactly1exist; CPPUNIT_ASSERT(_syncPal->updateTree(ReplicaSide::Remote)->exists(*nodeUpper->id()) && _syncPal->updateTree(ReplicaSide::Remote)->exists(*nodeLower->id())); #endif diff --git a/test/libsyncengine/syncpal/testsyncpal.cpp b/test/libsyncengine/syncpal/testsyncpal.cpp index 9ef536b74..3a0c3038c 100644 --- a/test/libsyncengine/syncpal/testsyncpal.cpp +++ b/test/libsyncengine/syncpal/testsyncpal.cpp @@ -72,7 +72,8 @@ void TestSyncPal::setUp() { Proxy::instance(parameters.proxyConfig()); } - _syncPal = std::shared_ptr(new SyncPal(sync.dbId(), KDRIVE_VERSION_STRING)); + _syncPal = std::make_shared(sync.dbId(), KDRIVE_VERSION_STRING); + _syncPal->createSharedObjects(); } void TestSyncPal::tearDown() { diff --git a/test/libsyncengine/test.cpp b/test/libsyncengine/test.cpp index e66b19270..6acb4dbae 100644 --- a/test/libsyncengine/test.cpp +++ b/test/libsyncengine/test.cpp @@ -45,6 +45,8 @@ #ifdef __APPLE__ #include "update_detection/file_system_observer/testfolderwatchermac.h" +#elif __unix__ +#include "update_detection/file_system_observer/testfolderwatcherlinux.h" #endif namespace KDC { @@ -60,6 +62,8 @@ CPPUNIT_TEST_SUITE_REGISTRATION(TestFsOperationSet); CPPUNIT_TEST_SUITE_REGISTRATION(TestLocalFileSystemObserverWorker); #ifdef __APPLE__ CPPUNIT_TEST_SUITE_REGISTRATION(TestFolderWatcher_mac); +#elif __unix__ +CPPUNIT_TEST_SUITE_REGISTRATION(TestFolderWatcherLinux); #endif CPPUNIT_TEST_SUITE_REGISTRATION(TestSnapshotItemHandler); CPPUNIT_TEST_SUITE_REGISTRATION(TestRemoteFileSystemObserverWorker); diff --git a/test/libsyncengine/update_detection/file_system_observer/testcomputefsoperationworker.cpp b/test/libsyncengine/update_detection/file_system_observer/testcomputefsoperationworker.cpp index 8f30ca9c2..ee9ebf1e7 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testcomputefsoperationworker.cpp +++ b/test/libsyncengine/update_detection/file_system_observer/testcomputefsoperationworker.cpp @@ -77,6 +77,7 @@ void TestComputeFSOperationWorker::setUp() { _syncPal = std::make_shared(sync.dbId(), KDRIVE_VERSION_STRING); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); /// Insert node "AC" in blacklist SyncNodeCache::instance()->update(_syncPal->syncDbId(), SyncNodeType::BlackList, {"lac"}); diff --git a/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.cpp b/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.cpp new file mode 100644 index 000000000..775c66e34 --- /dev/null +++ b/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.cpp @@ -0,0 +1,31 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testfolderwatcherlinux.h" + +#include "update_detection/file_system_observer/folderwatcher_linux.h" + + +namespace KDC { + +void TestFolderWatcherLinux::testMakeSyncPath() { + CPPUNIT_ASSERT(!FolderWatcher_linux::makeSyncPath("/A/B", "file.txt").filename().empty()); + CPPUNIT_ASSERT(!FolderWatcher_linux::makeSyncPath("/A/B", "").filename().empty()); +} + +} // namespace KDC diff --git a/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.h b/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.h new file mode 100644 index 000000000..074f3c6f0 --- /dev/null +++ b/test/libsyncengine/update_detection/file_system_observer/testfolderwatcherlinux.h @@ -0,0 +1,34 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "testincludes.h" + +namespace KDC { + +class TestFolderWatcherLinux final : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TestFolderWatcherLinux); + CPPUNIT_TEST(testMakeSyncPath); + CPPUNIT_TEST_SUITE_END(); + + private: + void testMakeSyncPath(); +}; + +} // namespace KDC diff --git a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp index 3d65ac1c6..5cb2d6bcb 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp +++ b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.cpp @@ -81,6 +81,7 @@ void TestLocalFileSystemObserverWorker::setUp() { // Create SyncPal _syncPal = std::make_shared(syncDbPath, KDRIVE_VERSION_STRING, true); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); _syncPal->setLocalPath(_rootFolderPath); _syncPal->_tmpBlacklistManager = std::make_shared(_syncPal); #if defined(_WIN32) @@ -123,8 +124,9 @@ void TestLocalFileSystemObserverWorker::testLFSOWithInitialSnapshot() { const NodeId parentId = _syncPal->snapshot(ReplicaSide::Local)->parentId(id); SyncPath parentPath; - if (!parentId.empty() && _syncPal->snapshot(ReplicaSide::Local)->path(parentId, parentPath) && - parentPath.filename() == _subDirPath.filename()) { + if (bool ignore = false; !parentId.empty() && + _syncPal->snapshot(ReplicaSide::Local)->path(parentId, parentPath, ignore) && + parentPath.filename() == _subDirPath.filename()) { fileCounter++; } } @@ -149,7 +151,8 @@ void TestLocalFileSystemObserverWorker::testLFSOWithFiles() { CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(itemId)); SyncPath testSyncPath; - CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(itemId, testSyncPath) && testSyncPath == filename); + bool ignore = false; + CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(itemId, testSyncPath, ignore) && testSyncPath == filename); } { @@ -233,17 +236,18 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDuplicateFileNames() { CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(nfcNamedItemId)); CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(nfdNamedItemId)); SyncPath testSyncPath; + bool ignore = false; #ifdef __APPLE__ - const bool foundNfcItem = - _syncPal->snapshot(ReplicaSide::Local)->path(nfcNamedItemId, testSyncPath) && testSyncPath == makeNfcSyncName(); - const bool foundNfdItem = - _syncPal->snapshot(ReplicaSide::Local)->path(nfdNamedItemId, testSyncPath) && testSyncPath == makeNfdSyncName(); + const bool foundNfcItem = _syncPal->snapshot(ReplicaSide::Local)->path(nfcNamedItemId, testSyncPath, ignore) && + testSyncPath == makeNfcSyncName(); + const bool foundNfdItem = _syncPal->snapshot(ReplicaSide::Local)->path(nfdNamedItemId, testSyncPath, ignore) && + testSyncPath == makeNfdSyncName(); CPPUNIT_ASSERT(foundNfcItem || foundNfdItem); #else - CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(nfcNamedItemId, testSyncPath) && + CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(nfcNamedItemId, testSyncPath, ignore) && testSyncPath == makeNfcSyncName()); - CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(nfdNamedItemId, testSyncPath) && + CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->path(nfdNamedItemId, testSyncPath, ignore) && testSyncPath == makeNfdSyncName()); #endif } @@ -267,7 +271,8 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() { itemId = std::to_string(fileStat.inode); CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(itemId)); SyncPath path; - _syncPal->snapshot(ReplicaSide::Local)->path(itemId, path); + bool ignore = false; + _syncPal->snapshot(ReplicaSide::Local)->path(itemId, path, ignore); CPPUNIT_ASSERT(path == CommonUtility::relativePath(_rootFolderPath, testAbsolutePath)); } @@ -282,7 +287,8 @@ void TestLocalFileSystemObserverWorker::testLFSOWithDirs() { Utility::msleep(1000); // Wait 1sec SyncPath path; - _syncPal->snapshot(ReplicaSide::Local)->path(itemId, path); + bool ignore = false; + _syncPal->snapshot(ReplicaSide::Local)->path(itemId, path, ignore); CPPUNIT_ASSERT(path == CommonUtility::relativePath(_rootFolderPath, destinationPath)); testAbsolutePath = destinationPath; } @@ -408,19 +414,54 @@ void TestLocalFileSystemObserverWorker::testLFSOWithSpecialCases2() { CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->name(initItemId) == testFilename); } -void TestLocalFileSystemObserverWorker::testLFSOFastMoveDelete() { +void TestLocalFileSystemObserverWorker::testLFSOFastMoveDeleteMove() { // MS Office test LOGW_DEBUG(_logger, L"***** Test fast move/delete *****"); + _syncPal->_localFSObserverWorker->stop(); + _syncPal->_localFSObserverWorker->waitForExit(); + _syncPal->_localFSObserverWorker.reset(); + + // Create a slow observer + auto slowObserver = std::make_shared(_syncPal, "Local File System Observer", "LFSO"); + _syncPal->_localFSObserverWorker = slowObserver; + _syncPal->_localFSObserverWorker->start(); + + int count = 0; + while (!_syncPal->snapshot(ReplicaSide::Local)->isValid()) { // Wait for the snapshot generation + Utility::msleep(100); + CPPUNIT_ASSERT(count++ < 20); // Do not wait more than 2s + } + CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(_testFiles[0].first)); IoError ioError = IoError::Unknown; SyncPath destinationPath = _testFiles[0].second.parent_path() / (_testFiles[0].second.filename().string() + "2"); - CPPUNIT_ASSERT(IoHelper::renameItem(_testFiles[0].second, destinationPath, ioError)); + CPPUNIT_ASSERT(IoHelper::renameItem(_testFiles[0].second, destinationPath, ioError)); // test0.txt -> test0.txt2 CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); - CPPUNIT_ASSERT(IoHelper::deleteItem(destinationPath, ioError)); + CPPUNIT_ASSERT(IoHelper::deleteItem(destinationPath, ioError)); // Delete test0.txt2 (before the previous rename is processed) + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); + CPPUNIT_ASSERT(IoHelper::renameItem(_testFiles[1].second, _testFiles[0].second, + ioError)); // test1.txt -> test0.txt (before the previous rename and delete is processed) CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); - Utility::msleep(1000); // Wait 1sec + CPPUNIT_ASSERT_MESSAGE("No update detected in the expected time.", slowObserver->waitForUpdate()); + + FileStat fileStat; + CPPUNIT_ASSERT(IoHelper::getFileStat(_testFiles[0].second, &fileStat, ioError)); + CPPUNIT_ASSERT_EQUAL(IoError::Success, ioError); CPPUNIT_ASSERT(!_syncPal->snapshot(ReplicaSide::Local)->exists(_testFiles[0].first)); + CPPUNIT_ASSERT(_syncPal->snapshot(ReplicaSide::Local)->exists(std::to_string(fileStat.inode))); +} + +bool MockLocalFileSystemObserverWorker::waitForUpdate(int64_t timeoutMs) const { + using namespace std::chrono; + auto start = system_clock::now(); + while (!_updating && duration_cast(system_clock::now() - start).count() < timeoutMs) { + Utility::msleep(10); + } + while (_updating && duration_cast(system_clock::now() - start).count() < timeoutMs) { + Utility::msleep(10); + } + return duration_cast(system_clock::now() - start).count() < timeoutMs; } } // namespace KDC diff --git a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h index d9f6d3ce9..33fb4cddd 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h +++ b/test/libsyncengine/update_detection/file_system_observer/testlocalfilesystemobserverworker.h @@ -20,9 +20,12 @@ #include "testincludes.h" #include "test_utility/localtemporarydirectory.h" - #include "syncpal/syncpal.h" - +#if defined(_WIN32) +#include "libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_win.h" +#else +#include "libsyncengine/update_detection/file_system_observer/localfilesystemobserverworker_unix.h" +#endif #include using namespace CppUnit; @@ -30,6 +33,34 @@ using namespace CppUnit; namespace KDC { class LocalFileSystemObserverWorker; +#if defined(_WIN32) +class MockLocalFileSystemObserverWorker : public LocalFileSystemObserverWorker_win { + public: + MockLocalFileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name, + const std::string &shortName) : + LocalFileSystemObserverWorker_win(syncPal, name, shortName) {} + + void changesDetected(const std::list> &changes) final { + Utility::msleep(200); + LocalFileSystemObserverWorker_win::changesDetected(changes); + } + + bool waitForUpdate(int64_t timeoutMs = 100000) const; +}; +#else +class MockLocalFileSystemObserverWorker : public LocalFileSystemObserverWorker_unix { + public: + MockLocalFileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name, + const std::string &shortName) : + LocalFileSystemObserverWorker_unix(syncPal, name, shortName) {} + + void changesDetected(const std::list> &changes) final { + Utility::msleep(200); + LocalFileSystemObserverWorker_unix::changesDetected(changes); + } + bool waitForUpdate(int64_t timeoutMs = 100000) const; +}; +#endif class TestLocalFileSystemObserverWorker : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestLocalFileSystemObserverWorker); @@ -38,7 +69,7 @@ class TestLocalFileSystemObserverWorker : public CppUnit::TestFixture { CPPUNIT_TEST(testLFSOWithDuplicateFileNames); CPPUNIT_TEST(testLFSODeleteDir); CPPUNIT_TEST(testLFSOWithDirs); - CPPUNIT_TEST(testLFSOFastMoveDelete); + CPPUNIT_TEST(testLFSOFastMoveDeleteMove); CPPUNIT_TEST(testLFSOWithSpecialCases1); CPPUNIT_TEST(testLFSOWithSpecialCases2); CPPUNIT_TEST_SUITE_END(); @@ -61,7 +92,7 @@ class TestLocalFileSystemObserverWorker : public CppUnit::TestFixture { void testLFSOWithDuplicateFileNames(); void testLFSOWithDirs(); void testLFSODeleteDir(); - void testLFSOFastMoveDelete(); + void testLFSOFastMoveDeleteMove(); void testLFSOWithSpecialCases1(); void testLFSOWithSpecialCases2(); }; diff --git a/test/libsyncengine/update_detection/file_system_observer/testremotefilesystemobserverworker.cpp b/test/libsyncengine/update_detection/file_system_observer/testremotefilesystemobserverworker.cpp index bbfed8fbd..bbfd24d97 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testremotefilesystemobserverworker.cpp +++ b/test/libsyncengine/update_detection/file_system_observer/testremotefilesystemobserverworker.cpp @@ -85,6 +85,7 @@ void TestRemoteFileSystemObserverWorker::setUp() { _syncPal = std::make_shared(sync.dbId(), KDRIVE_VERSION_STRING); _syncPal->syncDb()->setAutoDelete(true); + _syncPal->createSharedObjects(); /// Insert node in blacklist SyncNodeCache::instance()->update(_syncPal->syncDbId(), SyncNodeType::BlackList, {testBlackListedDirId}); diff --git a/test/libsyncengine/update_detection/file_system_observer/testsnapshot.cpp b/test/libsyncengine/update_detection/file_system_observer/testsnapshot.cpp index 78f3c2450..9b1517f22 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testsnapshot.cpp +++ b/test/libsyncengine/update_detection/file_system_observer/testsnapshot.cpp @@ -26,12 +26,6 @@ using namespace CppUnit; namespace KDC { -void TestSnapshot::setUp() { - ParametersCache::instance(true); -} - -void TestSnapshot::tearDown() {} - /** * Tree: * @@ -45,101 +39,232 @@ void TestSnapshot::tearDown() {} * AAA */ -void TestSnapshot::testSnapshot() { - const NodeId rootNodeId = SyncDb::driveRootNode().nodeIdLocal().value(); +void TestSnapshot::setUp() { + ParametersCache::instance(true); + _rootNodeId = SyncDb::driveRootNode().nodeIdLocal().value(); const DbNode dummyRootNode(0, std::nullopt, SyncName(), SyncName(), "1", "1", std::nullopt, std::nullopt, std::nullopt, NodeType::Directory, 0, std::nullopt); - Snapshot snapshot(ReplicaSide::Local, dummyRootNode); + _snapshot = std::make_unique(ReplicaSide::Local, dummyRootNode); // Insert node A - const SnapshotItem itemA("a", rootNodeId, Str("A"), 1640995201, -1640995201, NodeType::Directory, 123, false, true, true); - snapshot.updateItem(itemA); - CPPUNIT_ASSERT(snapshot.exists("a")); - CPPUNIT_ASSERT_EQUAL(std::string("A"), SyncName2Str(snapshot.name("a"))); - CPPUNIT_ASSERT_EQUAL(NodeType::Directory, snapshot.type("a")); + const SnapshotItem itemA("a", _rootNodeId, Str("A"), testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(itemA); + + // Insert node B + const SnapshotItem itemB("b", _rootNodeId, Str("B"), testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(itemB); + + // Insert child nodes + const SnapshotItem itemAA("aa", "a", Str("AA"), testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(itemAA); + + const SnapshotItem itemAAA("aaa", "aa", Str("AAA"), testhelpers::defaultTime, testhelpers::defaultTime, NodeType::File, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(itemAAA); +} + +void TestSnapshot::tearDown() {} + +void TestSnapshot::testSnapshot() { + CPPUNIT_ASSERT(_snapshot->exists("a")); + CPPUNIT_ASSERT_EQUAL(std::string("A"), SyncName2Str(_snapshot->name("a"))); + CPPUNIT_ASSERT_EQUAL(NodeType::Directory, _snapshot->type("a")); std::unordered_set childrenIds; - snapshot.getChildrenIds(rootNodeId, childrenIds); + _snapshot->getChildrenIds(_rootNodeId, childrenIds); CPPUNIT_ASSERT(childrenIds.contains("a")); - // Update node A - snapshot.updateItem( - SnapshotItem("a", rootNodeId, Str("A*"), 1640995202, 1640995202, NodeType::Directory, 123, false, true, true)); - CPPUNIT_ASSERT_EQUAL(std::string("A*"), SyncName2Str(snapshot.name("a"))); - - // Insert node B - const SnapshotItem itemB("b", rootNodeId, Str("B"), 1640995203, 1640995203, NodeType::Directory, 123, false, true, true); - snapshot.updateItem(itemB); - CPPUNIT_ASSERT(snapshot.exists("b")); - snapshot.getChildrenIds(rootNodeId, childrenIds); + CPPUNIT_ASSERT(_snapshot->exists("b")); + _snapshot->getChildrenIds(_rootNodeId, childrenIds); CPPUNIT_ASSERT(childrenIds.contains("b")); - // Insert child nodes - const SnapshotItem itemAA("aa", "a", Str("AA"), 1640995204, 1640995204, NodeType::Directory, 123, false, true, true); - snapshot.updateItem(itemAA); - CPPUNIT_ASSERT(snapshot.exists("aa")); - snapshot.getChildrenIds("a", childrenIds); + CPPUNIT_ASSERT(_snapshot->exists("aa")); + _snapshot->getChildrenIds("a", childrenIds); CPPUNIT_ASSERT(childrenIds.contains("aa")); - const SnapshotItem itemAAA("aaa", "aa", Str("AAA"), 1640995205, 1640995205, NodeType::File, 123, false, true, true); - snapshot.updateItem(itemAAA); - CPPUNIT_ASSERT(snapshot.exists("aaa")); - snapshot.getChildrenIds("aa", childrenIds); + CPPUNIT_ASSERT(_snapshot->exists("aaa")); + _snapshot->getChildrenIds("aa", childrenIds); CPPUNIT_ASSERT(childrenIds.contains("aaa")); + // Update node A + _snapshot->updateItem(SnapshotItem("a", _rootNodeId, Str("A*"), testhelpers::defaultTime, testhelpers::defaultTime + 1, + NodeType::Directory, testhelpers::defaultFileSize, false, true, true)); + CPPUNIT_ASSERT_EQUAL(std::string("A*"), SyncName2Str(_snapshot->name("a"))); SyncPath path; - snapshot.path("aaa", path); + bool ignore = false; + _snapshot->path("aaa", path, ignore); CPPUNIT_ASSERT_EQUAL(SyncPath("A*/AA/AAA"), path); - CPPUNIT_ASSERT_EQUAL(std::string("AAA"), SyncName2Str(snapshot.name("aaa"))); - CPPUNIT_ASSERT_EQUAL(static_cast(1640995205), snapshot.lastModified("aaa")); - CPPUNIT_ASSERT_EQUAL(NodeType::File, snapshot.type("aaa")); - CPPUNIT_ASSERT(snapshot.contentChecksum("aaa").empty()); // Checksum never computed for now - CPPUNIT_ASSERT_EQUAL(NodeId("aaa"), snapshot.itemId(std::filesystem::path("A*/AA/AAA"))); + CPPUNIT_ASSERT_EQUAL(std::string("AAA"), SyncName2Str(_snapshot->name("aaa"))); + CPPUNIT_ASSERT_EQUAL(static_cast(testhelpers::defaultTime), _snapshot->lastModified("aaa")); + CPPUNIT_ASSERT_EQUAL(NodeType::File, _snapshot->type("aaa")); + CPPUNIT_ASSERT(_snapshot->contentChecksum("aaa").empty()); // Checksum never computed for now + CPPUNIT_ASSERT_EQUAL(NodeId("aaa"), _snapshot->itemId(std::filesystem::path("A*/AA/AAA"))); // Move node AA under B - snapshot.updateItem(SnapshotItem("aa", "b", Str("AA"), 1640995204, -1640995204, NodeType::Directory, 123, false, true, true)); - CPPUNIT_ASSERT(snapshot.parentId("aa") == "b"); - snapshot.getChildrenIds("b", childrenIds); + _snapshot->updateItem(SnapshotItem("aa", "b", Str("AA"), testhelpers::defaultTime, testhelpers::defaultTime, + NodeType::Directory, testhelpers::defaultFileSize, false, true, true)); + CPPUNIT_ASSERT(_snapshot->parentId("aa") == "b"); + _snapshot->getChildrenIds("b", childrenIds); CPPUNIT_ASSERT(childrenIds.contains("aa")); - snapshot.getChildrenIds("a", childrenIds); + _snapshot->getChildrenIds("a", childrenIds); CPPUNIT_ASSERT(childrenIds.empty()); // Remove node B - snapshot.removeItem("b"); - snapshot.getChildrenIds(rootNodeId, childrenIds); - CPPUNIT_ASSERT(!snapshot.exists("aaa")); - CPPUNIT_ASSERT(!snapshot.exists("aa")); - CPPUNIT_ASSERT(!snapshot.exists("b")); + _snapshot->removeItem("b"); + _snapshot->getChildrenIds(_rootNodeId, childrenIds); + CPPUNIT_ASSERT(!_snapshot->exists("aaa")); + CPPUNIT_ASSERT(!_snapshot->exists("aa")); + CPPUNIT_ASSERT(!_snapshot->exists("b")); CPPUNIT_ASSERT(!childrenIds.contains("b")); // Reset snapshot - snapshot.init(); - CPPUNIT_ASSERT_EQUAL(static_cast(1), snapshot.nbItems()); + _snapshot->init(); + CPPUNIT_ASSERT_EQUAL(static_cast(1), _snapshot->nbItems()); } -void TestSnapshot::testSnapshotInsertionWithDifferentEncodings() { +void TestSnapshot::testDuplicatedItem() { const NodeId rootNodeId = *SyncDb::driveRootNode().nodeIdLocal(); const DbNode dummyRootNode(0, std::nullopt, Str("Local Drive"), SyncName(), "1", "1", std::nullopt, std::nullopt, std::nullopt, NodeType::Directory, 0, std::nullopt); Snapshot snapshot(ReplicaSide::Local, dummyRootNode); - const SnapshotItem nfcItem("A", rootNodeId, testhelpers::makeNfcSyncName(), 1640995201, -1640995201, NodeType::Directory, 123, - false, true, true); - const SnapshotItem nfdItem("B", rootNodeId, testhelpers::makeNfdSyncName(), 1640995201, -1640995201, NodeType::Directory, 123, - false, true, true); + const SnapshotItem file1("A", rootNodeId, Str("file1"), 1640995201, -1640995201, NodeType::File, 123, false, true, true); + const SnapshotItem file2("B", rootNodeId, Str("file1"), 1640995201, -1640995201, NodeType::File, 123, false, true, true); + + snapshot.updateItem(file1); + snapshot.updateItem(file2); + + CPPUNIT_ASSERT(!snapshot.exists("A")); + CPPUNIT_ASSERT(snapshot.exists("B")); +} + +void TestSnapshot::testSnapshotInsertionWithDifferentEncodings() { + const SnapshotItem nfcItem("A", _rootNodeId, testhelpers::makeNfcSyncName(), testhelpers::defaultTime, + -testhelpers::defaultTime, NodeType::Directory, testhelpers::defaultFileSize, false, true, true); + const SnapshotItem nfdItem("B", _rootNodeId, testhelpers::makeNfdSyncName(), testhelpers::defaultTime, + -testhelpers::defaultTime, NodeType::Directory, testhelpers::defaultFileSize, false, true, true); { - snapshot.updateItem(nfcItem); + _snapshot->updateItem(nfcItem); SyncPath syncPath; - snapshot.path("A", syncPath); + bool ignore = false; + _snapshot->path("A", syncPath, ignore); CPPUNIT_ASSERT_EQUAL(SyncPath(testhelpers::makeNfcSyncName()), syncPath); } { - snapshot.updateItem(nfdItem); + _snapshot->updateItem(nfdItem); SyncPath syncPath; - snapshot.path("B", syncPath); + bool ignore = false; + _snapshot->path("B", syncPath, ignore); CPPUNIT_ASSERT_EQUAL(SyncPath(testhelpers::makeNfdSyncName()), syncPath); } } +void TestSnapshot::testPath() { + // Normal case + { + const auto id = CommonUtility::generateRandomStringAlphaNum(); + const auto name = Str("test"); + const SnapshotItem item(id, "a", name, testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(item); + SyncPath path; + bool ignore = false; + CPPUNIT_ASSERT(_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name, path); + CPPUNIT_ASSERT(!ignore); + } + // Item name starting by pattern "X:", should be ignored (as well as its descendants) on Windows only + { + const auto id = CommonUtility::generateRandomStringAlphaNum(); + const auto name = Str("E:S"); + const SnapshotItem item(id, "a", name, testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(item); + SyncPath path; + bool ignore = false; + // On Windows, if the file name starts with "X:" pattern, the previous element of the path are overrode + // (https://en.cppreference.com/w/cpp/filesystem/path/append) +#ifdef _WIN32 + CPPUNIT_ASSERT(!_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT(ignore); +#else + CPPUNIT_ASSERT(_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name, path); + CPPUNIT_ASSERT(!ignore); +#endif + + const auto childId = CommonUtility::generateRandomStringAlphaNum(); + const auto childName = Str("test"); + const SnapshotItem childItem(childId, id, childName, testhelpers::defaultTime, testhelpers::defaultTime, + NodeType::Directory, testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(childItem); +#ifdef _WIN32 + CPPUNIT_ASSERT(!_snapshot->path(childId, path, ignore)); + CPPUNIT_ASSERT(ignore); +#else + CPPUNIT_ASSERT(_snapshot->path(childId, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name / childName, path); + CPPUNIT_ASSERT(!ignore); +#endif + } + // Item name starting by pattern "X:", should be ignored on Windows only + { + const auto id = CommonUtility::generateRandomStringAlphaNum(); + const auto name = Str("a:b"); + const SnapshotItem item(id, "a", name, testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(item); + SyncPath path; + bool ignore = false; + // On Windows, if the file name starts with "X:" pattern, the previous element of the path are overrode + // (https://en.cppreference.com/w/cpp/filesystem/path/append) +#ifdef _WIN32 + CPPUNIT_ASSERT(!_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT(ignore); +#else + CPPUNIT_ASSERT(_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name, path); + CPPUNIT_ASSERT(!ignore); +#endif + } + // Item name starting by pattern "X:", should be ignored on Windows only + { + const auto id = CommonUtility::generateRandomStringAlphaNum(); + const auto name = Str("C:"); + const SnapshotItem item(id, "a", name, testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(item); + + SyncPath path; + bool ignore = false; + // On Windows, if the file name starts with "X:" pattern, the previous element of the path are overrode + // (https://en.cppreference.com/w/cpp/filesystem/path/append) +#ifdef _WIN32 + CPPUNIT_ASSERT(!_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT(ignore); +#else + CPPUNIT_ASSERT(_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name, path); + CPPUNIT_ASSERT(!ignore); +#endif + } + // Item name starting with more than 1 character before `:`, should be accepted + { + const auto id = CommonUtility::generateRandomStringAlphaNum(); + const auto name = Str("aa:b"); + const SnapshotItem item(id, "a", name, testhelpers::defaultTime, testhelpers::defaultTime, NodeType::Directory, + testhelpers::defaultFileSize, false, true, true); + _snapshot->updateItem(item); + SyncPath path; + bool ignore = false; + CPPUNIT_ASSERT(_snapshot->path(id, path, ignore)); + CPPUNIT_ASSERT_EQUAL(SyncPath("A") / name, path); + CPPUNIT_ASSERT(!ignore); + } +} + } // namespace KDC diff --git a/test/libsyncengine/update_detection/file_system_observer/testsnapshot.h b/test/libsyncengine/update_detection/file_system_observer/testsnapshot.h index c0365b400..ea987fb80 100644 --- a/test/libsyncengine/update_detection/file_system_observer/testsnapshot.h +++ b/test/libsyncengine/update_detection/file_system_observer/testsnapshot.h @@ -28,7 +28,9 @@ namespace KDC { class TestSnapshot : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestSnapshot); CPPUNIT_TEST(testSnapshot); + CPPUNIT_TEST(testDuplicatedItem); CPPUNIT_TEST(testSnapshotInsertionWithDifferentEncodings); + CPPUNIT_TEST(testPath); CPPUNIT_TEST_SUITE_END(); public: @@ -37,7 +39,12 @@ class TestSnapshot : public CppUnit::TestFixture { private: void testSnapshot(); + void testDuplicatedItem(); void testSnapshotInsertionWithDifferentEncodings(); + void testPath(); + + std::unique_ptr _snapshot; + NodeId _rootNodeId; }; } // namespace KDC diff --git a/test/server/CMakeLists.txt b/test/server/CMakeLists.txt index e65af4d97..596db3937 100644 --- a/test/server/CMakeLists.txt +++ b/test/server/CMakeLists.txt @@ -1,36 +1,25 @@ project(testserver) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Svg) -find_package(log4cplus 2.1.0 REQUIRED) +find_package(Qt6 REQUIRED COMPONENTS Network Svg) find_package(Poco 1.13.3 REQUIRED XML Net) set(CMAKE_AUTOMOC ON) -set(server_srcs_path ${CMAKE_SOURCE_DIR}/src/server) + set(testserver_NAME ${APPLICATION_NAME}_test_server) +set(server_srcs_path ${CMAKE_SOURCE_DIR}/src/server) + set(server_SRCS - ${CMAKE_SOURCE_DIR}/src/libcommonserver/plugin.h ${CMAKE_SOURCE_DIR}/src/libcommonserver/plugin.cpp ${CMAKE_SOURCE_DIR}/src/libcommonserver/vfs.h ${CMAKE_SOURCE_DIR}/src/libcommonserver/vfs.cpp + ${CMAKE_SOURCE_DIR}/src/libcommonserver/plugin.h ${CMAKE_SOURCE_DIR}/src/libcommonserver/plugin.cpp ${server_srcs_path}/logarchiver/logarchiver.h ${server_srcs_path}/logarchiver/logarchiver.cpp + ${server_srcs_path}/socketapi.h ${server_srcs_path}/socketapi.cpp + ${server_srcs_path}/socketlistener.h ${server_srcs_path}/socketlistener.cpp ${server_srcs_path}/updater/abstractupdater.h ${server_srcs_path}/updater/abstractupdater.cpp ${server_srcs_path}/updater/updatechecker.h ${server_srcs_path}/updater/updatechecker.cpp ) -set(testserver_SRCS - ../test.cpp - ../test_utility/testhelpers.h ../test_utility/testhelpers.cpp - ../test_utility/localtemporarydirectory.h ../test_utility/localtemporarydirectory.cpp - test.cpp - - logarchiver/testlogarchiver.h logarchiver/testlogarchiver.cpp - updater/testupdatechecker.h updater/testupdatechecker.cpp - updater/testabstractupdater.h updater/testabstractupdater.cpp - workers/testworkers.h workers/testworkers.cpp -) - if(APPLE) - list(APPEND testserver_SRCS vfs/mac/testlitesyncextconnector.h vfs/mac/testlitesyncextconnector.cpp) - list(APPEND server_SRCS ${server_srcs_path}/socketapisocket_mac.mm) set_property(SOURCE ${server_srcs_path}/socketapisocket_mac.mm APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") @@ -43,11 +32,6 @@ if(APPLE) add_definitions(-DHAVE_SPARKLE) list(APPEND server_SRCS ${server_srcs_path}/updater/sparkleupdater.h ${server_srcs_path}/updater/sparkleupdater.mm) list(APPEND updater_DEPS ${SPARKLE_LIBRARY}) - - # Sparkle.framework is installed from here because macdeployqt's CopyFramework breaks on this bundle - # as its logic is tightly tailored around Qt6 frameworks - install(DIRECTORY "${SPARKLE_LIBRARY}" - DESTINATION "${KDRIVE_OSX_BUNDLE}/Contents/Frameworks" USE_SOURCE_PERMISSIONS) endif() elseif (WIN32) list(APPEND server_SRCS ${server_srcs_path}/updater/windowsupdater.h ${server_srcs_path}/updater/windowsupdater.cpp) @@ -55,6 +39,22 @@ else () list(APPEND server_SRCS ${server_srcs_path}/updater/linuxupdater.h ${server_srcs_path}/updater/linuxupdater.cpp) endif() +set(testserver_SRCS + ../test_utility/testhelpers.h ../test_utility/testhelpers.cpp + ../test_utility/localtemporarydirectory.h ../test_utility/localtemporarydirectory.cpp + ../test.cpp + + test.cpp + logarchiver/testlogarchiver.h logarchiver/testlogarchiver.cpp + updater/testupdatechecker.h updater/testupdatechecker.cpp + updater/testabstractupdater.h updater/testabstractupdater.cpp + workers/testworkers.h workers/testworkers.cpp +) + +if(APPLE) + list(APPEND testserver_SRCS vfs/mac/testlitesyncextconnector.h vfs/mac/testlitesyncextconnector.cpp) +endif() + if (WIN32) include_directories("F:/Projects/log4cplus/include") include_directories("C:/Program Files (x86)/cppunit/include") @@ -70,11 +70,11 @@ add_executable(${testserver_NAME} ${testserver_SRCS}) set_target_properties(${testserver_NAME} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}) + RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}) target_link_libraries(${testserver_NAME} - Qt6::Core Qt6::Gui - Poco::XML Poco::Net + Qt6::Svg + updater ) if (APPLE) @@ -82,17 +82,6 @@ if (APPLE) target_link_libraries(${testserver_NAME} ${SERVICE_MANAGEMENT_LIBRARY}) endif() -if (WIN32) - target_link_libraries(${testserver_NAME} - log4cplus::log4cplusU) -elseif (APPLE) - target_link_libraries(${testserver_NAME} - "/usr/local/lib/liblog4cplusU.dylib") -else () - target_link_libraries(${testserver_NAME} - "/usr/local/lib/liblog4cplusU.so") -endif() - if (WIN32) target_link_libraries(${testserver_NAME} debug @@ -107,37 +96,24 @@ else () "/usr/local/lib/libcppunit.so") endif() - -# Loaded after liblog4cplus to avoid an initialization crash if (APPLE) target_link_libraries(${testserver_NAME} "${libsyncengine_NAME}_vfs_mac" "${updater_DEPS}") -else() +elseif (WIN32) target_link_libraries(${testserver_NAME} - ${libsyncengine_NAME}) + "${libsyncengine_NAME}_vfs_win") endif() # Install - -# VFS plugin -set(vfs_installdir "${BIN_OUTPUT_DIRECTORY}") -if (APPLE) - install(TARGETS "${libsyncengine_NAME}_vfs_mac" - LIBRARY DESTINATION "${vfs_installdir}" - RUNTIME DESTINATION "${vfs_installdir}" - ) -endif() - if (APPLE) - # Default sync exclude list - install(FILES ${CMAKE_SOURCE_DIR}/sync-exclude-osx.lst DESTINATION ${BIN_OUTPUT_DIRECTORY} RENAME sync-exclude.lst) - - # Default Lite Sync app exclude list - install(FILES ${CMAKE_SOURCE_DIR}/litesync-exclude.lst DESTINATION ${BIN_OUTPUT_DIRECTORY}) + cmake_path(GET SPARKLE_LIBRARY PARENT_PATH USER_FRAMEWORK_PATH) install(CODE " message(STATUS \"Fixing library paths for ${testserver_NAME}...\") execute_process(COMMAND \"install_name_tool\" -change libxxhash.0.dylib @rpath/libxxhash.0.8.2.dylib ${BIN_OUTPUT_DIRECTORY}/${testserver_NAME}) + execute_process(COMMAND \"install_name_tool\" -delete_rpath ${USER_FRAMEWORK_PATH} ${BIN_OUTPUT_DIRECTORY}/${testserver_NAME}) + execute_process(COMMAND \"install_name_tool\" -add_rpath ${INSTALL_OUTPUT_DIRECTORY}/${KDRIVE_OSX_BUNDLE}/Contents/Frameworks ${BIN_OUTPUT_DIRECTORY}/${testserver_NAME}) + execute_process(COMMAND \"install_name_tool\" -add_rpath ${INSTALL_OUTPUT_DIRECTORY}/${KDRIVE_OSX_BUNDLE}/Contents/Plugins ${BIN_OUTPUT_DIRECTORY}/${testserver_NAME}) " COMPONENT RUNTIME) endif() diff --git a/test/server/logarchiver/testlogarchiver.cpp b/test/server/logarchiver/testlogarchiver.cpp index a592f24db..08d7de3fd 100644 --- a/test/server/logarchiver/testlogarchiver.cpp +++ b/test/server/logarchiver/testlogarchiver.cpp @@ -17,16 +17,19 @@ */ #include "testlogarchiver.h" + #include "server/logarchiver/logarchiver.h" #include "requests/parameterscache.h" -#include "db/parmsdb.h" #include "version.h" +#include "libcommon/utility/utility.h" #include "libcommonserver/log/log.h" #include "libcommonserver/io/iohelper.h" -#include "libcommon/utility/utility.h" #include "libcommonserver/db/db.h" +#include "libparms/db/parmsdb.h" #include "test_utility/localtemporarydirectory.h" +#include + #include using namespace CppUnit; diff --git a/test/server/updater/testabstractupdater.cpp b/test/server/updater/testabstractupdater.cpp index 553b72e49..7c14dabea 100644 --- a/test/server/updater/testabstractupdater.cpp +++ b/test/server/updater/testabstractupdater.cpp @@ -20,8 +20,7 @@ #include "db/parmsdb.h" #include "requests/parameterscache.h" - -#include +#include "version.h" namespace KDC { diff --git a/test/server/updater/testupdatechecker.cpp b/test/server/updater/testupdatechecker.cpp index 2a95c1156..a8de9e0ae 100644 --- a/test/server/updater/testupdatechecker.cpp +++ b/test/server/updater/testupdatechecker.cpp @@ -27,9 +27,9 @@ namespace KDC { static const std::string bigVersionJsonUpdateStr = - R"({"result":"success","data":{"application_id":27,"has_prod_next":false,"version":{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"99.99.99","next_version_rate":0,"published_versions":[{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]}]}}})"; + R"({"result":"success","data":{"application_id":27,"prod_version":"production","version":{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"99.99.99","next_version_rate":0,"published_versions":[{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]},{"tag":"99.99.99","tag_updated_at":"2124-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"21240604","build_min_os_version":"21240604","download_link":"test","data":["[]"]}]}}})"; static const std::string smallVersionJsonUpdateStr = - R"({"result":"success","data":{"application_id":27,"has_prod_next":false,"version":{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"1.1.1","next_version_rate":0,"published_versions":[{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]}]}}})"; + R"({"result":"success","data":{"application_id":27,"prod_version":"production","version":{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},"application":{"id":27,"name":"com.infomaniak.drive","platform":"mac-os","store":"kStore","api_id":"com.infomaniak.drive","min_version":"1.1.1","next_version_rate":0,"published_versions":[{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:37","version_changelog":"test","type":"production","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:06:12","version_changelog":"test","type":"beta","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:05:44","version_changelog":"test","type":"internal","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]},{"tag":"1.1.1","tag_updated_at":"2020-06-04 15:03:29","version_changelog":"test","type":"production-next","build_version":"20200604","build_min_os_version":"20200604","download_link":"test","data":["[]"]}]}}})"; class GetAppVersionJobTest final : public GetAppVersionJob { public: diff --git a/test/server/workers/testworkers.cpp b/test/server/workers/testworkers.cpp index fe1617047..97e09f253 100644 --- a/test/server/workers/testworkers.cpp +++ b/test/server/workers/testworkers.cpp @@ -19,19 +19,33 @@ #include "testworkers.h" #include "propagation/executor/executorworker.h" -#include "keychainmanager/keychainmanager.h" -#include "network/proxy.h" -#include "io/iohelper.h" +#include "libcommon/keychainmanager/keychainmanager.h" +#include "libcommonserver/network/proxy.h" +#include "libcommonserver/io/iohelper.h" #include "test_utility/testhelpers.h" +#ifdef _WIN32 +#include +#endif + namespace KDC { #if defined(__APPLE__) -std::unique_ptr TestWorkers::_vfsPtr = nullptr; +std::shared_ptr TestWorkers::_vfsPtr = nullptr; #elif defined(_WIN32) -std::unique_ptr TestWorkers::_vfsPtr = nullptr; +std::shared_ptr TestWorkers::_vfsPtr = nullptr; #else -std::unique_ptr TestWorkers::_vfsPtr = nullptr; +std::shared_ptr TestWorkers::_vfsPtr = nullptr; +#endif + +bool TestWorkers::_vfsInstallationDone = false; +bool TestWorkers::_vfsActivationDone = false; +bool TestWorkers::_vfsConnectionDone = false; + +#ifdef __APPLE__ +// TODO: On macOS, SIP should be deactivated and LiteSync extension signed to be able to install the Lite Sync extension. +// Set to true if the Login Item Agent and the Lite Sync extensions are already installed on the test machine. +constexpr bool connectorsAreAlreadyInstalled = false; #endif bool TestWorkers::createPlaceholder(int syncDbId, const SyncPath &relativeLocalPath, const SyncFileItem &item) { @@ -120,23 +134,52 @@ void TestWorkers::setUp() { vfsSetupParams._localPath = _sync.localPath(); vfsSetupParams._targetPath = _sync.targetPath(); vfsSetupParams._logger = _logger; + vfsSetupParams._executeCommand = [](const char *) {}; #if defined(__APPLE__) - _vfsPtr = std::unique_ptr(new VfsMac(vfsSetupParams)); + _vfsPtr = std::shared_ptr(new VfsMac(vfsSetupParams)); #elif defined(_WIN32) - //_vfsPtr = std::unique_ptr(new VfsWin(vfsSetupParams, nullptr)); + _vfsPtr = std::shared_ptr(new VfsWin(vfsSetupParams)); #else - _vfsPtr = std::unique_ptr(new VfsOff(vfsSetupParams)); + _vfsPtr = std::shared_ptr(new VfsOff(vfsSetupParams)); +#endif + +#if defined(__APPLE__) + _vfsPtr->setExclusionAppListCallback([](QString &) {}); #endif // Setup SyncPal _syncPal = std::make_shared(_sync.dbId(), KDRIVE_VERSION_STRING); + _syncPal->createSharedObjects(); _syncPal->createWorkers(); _syncPal->syncDb()->setAutoDelete(true); _syncPal->createProgressInfo(); _syncPal->setVfsCreatePlaceholderCallback(createPlaceholder); _syncPal->setVfsConvertToPlaceholderCallback(convertToPlaceholder); _syncPal->setVfsSetPinStateCallback(setPinState); + + // Setup SocketApi + std::unordered_map> syncPalMap; + syncPalMap[_sync.dbId()] = _syncPal; + std::unordered_map> vfsMap; + vfsMap[_sync.dbId()] = _vfsPtr; + _socketApi = std::make_unique(syncPalMap, vfsMap); + +#ifdef _WIN32 + // Initializes the COM library + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); +#endif + + // Start Vfs +#ifdef __APPLE__ + if (connectorsAreAlreadyInstalled) { + _vfsInstallationDone = true; + _vfsActivationDone = true; + startVfs(); + } +#else + startVfs(); +#endif } void TestWorkers::tearDown() { @@ -146,10 +189,33 @@ void TestWorkers::tearDown() { _syncPal->syncDb()->close(); } if (_vfsPtr) { + // Stop Vfs + _vfsPtr->stopImpl(true); _vfsPtr = nullptr; } } +void TestWorkers::testStartVfs() { +#if defined(__APPLE__) + if (connectorsAreAlreadyInstalled) { + // Make sure that Vfs is installed/activated/connected + CPPUNIT_ASSERT(_vfsInstallationDone); + CPPUNIT_ASSERT(_vfsActivationDone); + CPPUNIT_ASSERT(_vfsConnectionDone); + + // Try to start Vfs another time + CPPUNIT_ASSERT(startVfs()); + CPPUNIT_ASSERT(_vfsInstallationDone); + CPPUNIT_ASSERT(_vfsActivationDone); + CPPUNIT_ASSERT(_vfsConnectionDone); + } +#elif defined(_WIN32) + // Try to start Vfs another time + // => WinRT error caught : hr 8007017a - The cloud sync root is already connected with another cloud sync provider. + CPPUNIT_ASSERT(!startVfs()); +#endif +} + void TestWorkers::testCreatePlaceholder() { _syncPal->resetEstimateUpdates(); ExitInfo exitInfo; @@ -167,6 +233,10 @@ void TestWorkers::testCreatePlaceholder() { syncItem.setPath(relativeFolderPath); syncItem.setType(NodeType::Directory); syncItem.setDirection(SyncDirection::Down); +#ifdef _WIN32 + syncItem.setRemoteNodeId("1"); +#endif + CPPUNIT_ASSERT(_syncPal->initProgress(syncItem)); // Folder doesn't exist (normal case) @@ -189,6 +259,10 @@ void TestWorkers::testCreatePlaceholder() { syncItem.setPath(relativeFilePath); syncItem.setType(NodeType::File); syncItem.setDirection(SyncDirection::Down); +#ifdef _WIN32 + syncItem.setRemoteNodeId("2"); +#endif + CPPUNIT_ASSERT(_syncPal->initProgress(syncItem)); #if defined(__APPLE__) || defined(_WIN32) @@ -198,8 +272,22 @@ void TestWorkers::testCreatePlaceholder() { ioError == IoError::Success); exitInfo = _syncPal->_executorWorker->createPlaceholder(relativeFilePath); +#ifdef __APPLE__ CPPUNIT_ASSERT_EQUAL(ExitCode::SystemError, exitInfo.code()); CPPUNIT_ASSERT_EQUAL(ExitCause::FileAccessError, exitInfo.cause()); +#else + // Strangely (bug?), the Windows api is able to create a placeholder in a folder for which the user does not have rights + CPPUNIT_ASSERT_EQUAL(ExitCode::Ok, exitInfo.code()); + CPPUNIT_ASSERT_EQUAL(ExitCause::Unknown, exitInfo.cause()); + + // Remove placeholder + std::error_code ec; + std::filesystem::remove(_syncPal->localPath() / relativeFilePath); + if (ec) { + // Cannot remove file + CPPUNIT_ASSERT(false); + } +#endif ioError = IoError::Unknown; CPPUNIT_ASSERT(IoHelper::setRights(_syncPal->localPath() / relativeFolderPath, true, true, true, ioError) && @@ -295,4 +383,9 @@ void TestWorkers::testConvertToPlaceholder() { CPPUNIT_ASSERT_EQUAL(ExitCause::Unknown, exitInfo.cause()); } } + +bool TestWorkers::startVfs() { + return _vfsPtr->startImpl(_vfsInstallationDone, _vfsActivationDone, _vfsConnectionDone); +} + } // namespace KDC diff --git a/test/server/workers/testworkers.h b/test/server/workers/testworkers.h index 0f5365555..7ae4d3c8f 100644 --- a/test/server/workers/testworkers.h +++ b/test/server/workers/testworkers.h @@ -17,6 +17,7 @@ */ #include "testincludes.h" +#include "socketapi.h" #if defined(__APPLE__) #include "server/vfs/mac/vfs_mac.h" @@ -26,13 +27,14 @@ #include "libcommonserver/vfs.h" #endif -#include "propagation/executor/executorworker.h" +#include "libsyncengine/propagation/executor/executorworker.h" #include "test_utility/localtemporarydirectory.h" namespace KDC { class TestWorkers : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestWorkers); + CPPUNIT_TEST(testStartVfs); CPPUNIT_TEST(testCreatePlaceholder); CPPUNIT_TEST(testConvertToPlaceholder); CPPUNIT_TEST_SUITE_END(); @@ -40,10 +42,12 @@ class TestWorkers : public CppUnit::TestFixture { public: void setUp(void) final; void tearDown() override; + void testStartVfs(); void testCreatePlaceholder(); void testConvertToPlaceholder(); protected: + static bool startVfs(); static bool createPlaceholder(int syncDbId, const SyncPath &relativeLocalPath, const SyncFileItem &item); static bool convertToPlaceholder(int syncDbId, const SyncPath &relativeLocalPath, const SyncFileItem &item); static bool setPinState(int syncDbId, const SyncPath &relativeLocalPath, PinState pinState); @@ -53,13 +57,19 @@ class TestWorkers : public CppUnit::TestFixture { Sync _sync; LocalTemporaryDirectory _localTempDir{"TestExecutorWorker"}; + std::unique_ptr _socketApi; + #if defined(__APPLE__) - static std::unique_ptr _vfsPtr; + static std::shared_ptr _vfsPtr; #elif defined(_WIN32) - static std::unique_ptr _vfsPtr; + static std::shared_ptr _vfsPtr; #else - static std::unique_ptr _vfsPtr; + static std::shared_ptr _vfsPtr; #endif + + static bool _vfsInstallationDone; + static bool _vfsActivationDone; + static bool _vfsConnectionDone; }; } // namespace KDC diff --git a/test/test.cpp b/test/test.cpp index 568aa3a60..80a6b8242 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -18,9 +18,9 @@ #include "testincludes.h" -#include "libcommonserver/log/log.h" #include "libcommon/log/sentry/sentryhandler.h" -#include "utility/utility.h" +#include "libcommonserver/log/log.h" +#include "libcommonserver/utility/utility.h" #include @@ -28,7 +28,6 @@ int runTestSuite(const std::string &logFileName) { /* initialize random seed: */ srand(static_cast(time(NULL))); - // Disable sentry KDC::SentryHandler::init(KDC::AppType::None); // Setup log4cplus diff --git a/test/test_utility/localtemporarydirectory.cpp b/test/test_utility/localtemporarydirectory.cpp index cf73e7b34..4a5c65155 100644 --- a/test/test_utility/localtemporarydirectory.cpp +++ b/test/test_utility/localtemporarydirectory.cpp @@ -17,8 +17,8 @@ */ #include "localtemporarydirectory.h" -#include "io/filestat.h" -#include "io/iohelper.h" +#include "libcommonserver/io/filestat.h" +#include "libcommonserver/io/iohelper.h" #include @@ -51,7 +51,12 @@ LocalTemporaryDirectory::LocalTemporaryDirectory(const std::string &testType) { } LocalTemporaryDirectory::~LocalTemporaryDirectory() { - std::filesystem::remove_all(_path); + std::error_code ec; + std::filesystem::remove_all(_path, ec); + if (ec) { + // Cannot remove directory + assert(false); + } } diff --git a/test/test_utility/localtemporarydirectory.h b/test/test_utility/localtemporarydirectory.h index 28ffba3be..3966dd32a 100644 --- a/test/test_utility/localtemporarydirectory.h +++ b/test/test_utility/localtemporarydirectory.h @@ -17,7 +17,7 @@ */ #pragma once -#include "utility/types.h" +#include "libcommon/utility/types.h" #include #include diff --git a/test/test_utility/testhelpers.cpp b/test/test_utility/testhelpers.cpp index 5ade485e7..e7541bc5c 100644 --- a/test/test_utility/testhelpers.cpp +++ b/test/test_utility/testhelpers.cpp @@ -15,10 +15,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include + #include "testhelpers.h" -#include "utility/utility.h" +#include "libcommon/utility/utility.h" + +#include + + +#ifdef _WIN32 +#include "libcommonserver/io/filestat.h" +#include "libcommonserver/io/iohelper.h" +#include +#include +#else +#include +#include +#endif namespace KDC::testhelpers { @@ -51,5 +64,32 @@ std::string loadEnvVariable(const std::string& key) { } return val; } +#ifdef _WIN32 +void setModificationDate(const SyncPath& path, const std::chrono::time_point& timePoint) { + struct _utimbuf timeBuffer; + const std::time_t timeInSeconds = std::chrono::system_clock::to_time_t(timePoint); + IoError ioError = IoError::Success; + FileStat fileStat; + ::KDC::IoHelper::getFileStat(path, &fileStat, ioError); + + timeBuffer.actime = fileStat.creationTime; + timeBuffer.modtime = timeInSeconds; + _wutime(path.wstring().c_str(), &timeBuffer); +} +#else +void setModificationDate(const SyncPath& path, const std::chrono::time_point& timePoint) { + struct stat fileStat; + struct utimbuf newTime; + + const auto fileNameStr = path.string(); + const auto fileName = fileNameStr.c_str(); + + stat(fileName, &fileStat); + + const std::time_t timeInSeconds = std::chrono::system_clock::to_time_t(timePoint); + newTime.modtime = timeInSeconds; + utime(fileName, &newTime); +} +#endif } // namespace KDC::testhelpers diff --git a/test/test_utility/testhelpers.h b/test/test_utility/testhelpers.h index f1cf87e03..24debe4cf 100644 --- a/test/test_utility/testhelpers.h +++ b/test/test_utility/testhelpers.h @@ -19,12 +19,14 @@ #pragma once #include "libcommon/utility/utility.h" -#include "utility/types.h" -#include "utility/utility.h" +#include "libcommon/utility/types.h" +#include "libcommonserver/utility/utility.h" #include "version.h" #include +#include + namespace KDC::testhelpers { const SyncPath localTestDirPath(KDC::Utility::s2ws(TEST_DIR) + L"/test_ci"); @@ -54,5 +56,6 @@ struct TestVariables { }; void generateOrEditTestFile(const SyncPath &path); +void setModificationDate(const SyncPath &path, const std::chrono::time_point &timePoint); } // namespace KDC::testhelpers diff --git a/test/test_utility/timechecker.h b/test/test_utility/timechecker.h new file mode 100644 index 000000000..d83b09981 --- /dev/null +++ b/test/test_utility/timechecker.h @@ -0,0 +1,48 @@ +/* + * Infomaniak kDrive - Desktop + * Copyright (C) 2023-2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +class TimeChecker { + public: + explicit TimeChecker(bool start = false) { + if (start) this->start(); + } + void start() { _time = std::chrono::steady_clock::now(); } + void stop() { + auto end = std::chrono::steady_clock::now(); + _diff = std::chrono::duration_cast(end - _time).count(); + } + bool lessOrEqualThan(long long value) { + if (_diff > value) std::cout << "TimeChecker::lessThan: " << _diff << " >= " << value << std::endl; + return _diff <= value; + } + bool greaterOrEqualThan(long long value) { + if (_diff < value) std::cout << "TimeChecker::greaterThan: " << _diff << " <= " << value << std::endl; + return _diff >= value; + } + bool between(long long min, long long max) { + if (_diff < min || _diff > max) + std::cout << "TimeChecker::between: " << _diff << " <= " << min << " || " << _diff << " >= " << max << std::endl; + return _diff >= min && _diff <= max; + } + + private: + std::chrono::steady_clock::time_point _time; + long long _diff{0}; +}; diff --git a/translations/client_de.ts b/translations/client_de.ts index ae75845dc..0a416bec0 100644 --- a/translations/client_de.ts +++ b/translations/client_de.ts @@ -288,12 +288,12 @@ KDC::AppServer - + kDrive application is already running! Die kDrive-Anwendung läuft bereits! - + %1 and %n other file(s) have been removed. %1 und %n andere Datei(en) wurde(n) gelöscht. @@ -301,13 +301,13 @@ - + %1 has been removed. %1 names a file. %1 wurde gelöscht. - + %1 and %n other file(s) have been added. %1 und %n andere Datei(en) wurde(n) hinzugefügt. @@ -315,13 +315,13 @@ - + %1 has been added. %1 names a file. %1 wurde hinzugefügt. - + %1 and %n other file(s) have been updated. %1 und %n andere Datei(en) wurde(n) aktualisiert. @@ -329,13 +329,13 @@ - + %1 has been updated. %1 names a file. %1 wurde aktualisiert. - + %1 has been moved to %2 and %n other file(s) have been moved. %1 wurde zu %2 verschoben und %n andere Datei(en) wurde(n) verschoben. @@ -343,17 +343,17 @@ - + %1 has been moved to %2. %1 wurde zu %2 verschoben. - + Sync Activity Synchronisierungsaktivität - + A new folder larger than %1 MB has been added in the drive %2, you must validate its synchronization: %3. Ein neuer Ordner mit einer Grösse von mehr als %1 MB wurde zum Laufwerk %2 hinzugefügt, Sie müssen die Synchronisierung überprüfen: %3. @@ -396,118 +396,118 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten: KDC::ClientGui - + Please sign in Bitte melden Sie sich an - + Folder %1: %2 Ordner %1: %2 - + There are no sync folders configured. Es wurden keine Synchronisierungsordner konfiguriert. - + Synthesis Synthese - + Preferences Einstellungen - + Quit Beenden - + Undefined State. Undefinierter Zustand. - + Waiting to start syncing. Synchronisierung ausstehend. - + Sync is running. Synchronisierung läuft. - + Sync was successful, unresolved conflicts. Synchronisierung war erfolgreich, ungelöste Konflikte. - + Last Sync was successful. Die letzte Synchronisierung war erfolgreich. - + User Abort. Benutzer-Abbruch. - + Sync is paused. Synchronisierung wurde angehalten. - + %1 (Sync is paused) %1 (Synchronisierung wurde angehalten) - + Do you really want to remove the synchronizations of the account <i>%1</i> ?<br><b>Note:</b> This will <b>not</b> delete any files. Möchten Sie die Synchronisierungen des Kontos <i>%1</i> wirklich entfernen?<br><b>Hinweis:</b> Dadurch werden <b>keine</b> Dateien gelöscht. - + REMOVE ALL SYNCHRONIZATIONS ALLE SYNC. ENTFERNEN - + CANCEL ABBRECHEN - + Failed to start synchronizations! Synchronisierungen konnten nicht gestartet werden! - + Unable to open folder path %1. Ordnerpfad %1 kann nicht geöffnet werden. - + Unable to initialize kDrive client Der kDrive-Client kann nicht initialisiert werden - + Failed to fix conflict(s) on %1 item(s) in sync folder: %2 Konflikt(e) bei %1 Element(en) im Synchronisierungsordner %2 konnten nicht behoben werden - + Synchronization is paused Synchronisierung wird angehalten - - + + The shared link has been copied to the clipboard. Der freigegebene Link wurde in die Zwischenablage kopiert. @@ -680,153 +680,153 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten: KDC::DebuggingDialog - + Info Info - + Debug Debuggen - + Warning Warnung - + Error Fehler - + Fatal Schwer - + Debugging settings Debugging-Einstellungen - + Save debugging information in a folder on my computer (Recommended) Debugging-Informationen in einem Ordner auf meinem Computer speichern (empfohlen) - + This information enables IT support to determine the origin of an incident. Mithilfe dieser Informationen kann der IT-Support den Ursprung eines Vorfalls ermitteln. - + <a style="%1" href="%2">Open debugging folder</a> <a style="%1" href="%2">Debugging-Ordner öffnen</a> - + Debug level Debug level - + The trace level lets you choose the extent of the debugging information recorded Mit der Trace-Ebene können Sie den Umfang der aufgezeichneten Debugging-Informationen auswählen - + The extended full log collects a detailed history that can be used for debugging. Enabling it can slow down the kDrive application. Das erweiterte vollständige Protokoll erfasst einen detaillierten Verlauf, der zum Debuggen verwendet werden kann. Die Aktivierung kann die kDrive-Anwendung verlangsamen. - + Extended Full Log Erweitertes vollständiges Protokoll - + Delete logs older than %1 days Protokolle löschen, die älter als %1 Tage sind - + Share the debug folder with Infomaniak support. Geben Sie den Debug-Ordner für den Infomaniak-Support frei. - + The last session is the periode since the last kDrive start. Die letzte Sitzung ist der Zeitraum seit dem letzten kDrive-Start. - + Share only the last kDrive session Teilen Sie nur die letzte kDrive-Sitzung - + Loading Wird geladen - + Cancel Abbrechen - + SAVE SPEICHERN - + CANCEL ABBRECHEN - + Failed to share Teilen fehlgeschlagen - + 1. Check that you are logged in <br>2. Check that you have configured at least one kDrive 1. Überprüfen Sie, ob Sie angemeldet sind <br>2. Überprüfen Sie, ob Sie mindestens ein kDrive konfiguriert haben - + (Connexion interrupted) (Verbindung unterbrochen) - + Share the folder with SwissTransfer <br> Teilen Sie den Ordner mit SwissTransfer <br> - + 1. We automatically compressed your log <a style="%1" href="%2">here</a>.<br> 1. Wir haben Ihr Protokoll <a style="%1" href="%2">hier</a> automatisch komprimiert.<br> - + 2. Transfer the archive with <a style="%1" href="%2">swisstransfer.com</a><br> 2. Übertragen Sie das Archiv mit <a style="%1" href="%2">swisstransfer.com</a><br> - + 3. Share the link with <a style="%1" href="%2"> support@infomaniak.com </a><br> 3. Teilen Sie den Link mit <a style="%1" href="%2"> support@infomaniak.com </a><br> - + Last upload the %1 Letzte Hochladung am %1 Letztes Hochladen von %1 - + Sharing has been cancelled Die Freigabe wurde abgebrochen @@ -836,40 +836,40 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten: Der gesamte Ordner ist groß (> 100 MB) und kann einige Zeit für die Freigabe benötigen. Um die Freigabezeit zu reduzieren, empfehlen wir, nur die letzte kDrive-Sitzung freizugeben. - + %1/%2/%3 at %4h%5m and %6s Date format for the last successful log upload. %1: month, %2: day, %3: year, %4: hour, %5: minute, %6: second ie: Letzte Hochladung am 29.01.24 um 13:04:06 Uhr %2.%1.%3 um %4:%5:%6 Uhr - + Do you want to save your modifications? Wollen Sie Ihre Änderungen speichern? - - + + Unable to open folder %1. Ordner %1 kann nicht geöffnet werden. - + Share Teilen - + Sharing | step 1/2 %1% Teilen | Schritt 1/2 %1% - + Sharing | step 2/2 %1% Teilen | Schritt 2/2 %1% - + Canceling... Stornieren... @@ -1513,382 +1513,372 @@ Wählen Sie diejenigen aus, die Sie synchronisieren möchten: KDC::ParametersDialog - + Unable to open folder path %1. Ordnerpfad %1 kann nicht geöffnet werden. - + Transmission done!<br>Please refer to identifier <b>%1</b> in bug reports. Übertragung abgeschlossen!<br>Weitere Einzelheiten enthält der Identifier <b>%1</b> in den Fehlerberichten. - + Transmission failed! Please, use the following link to send the logs to the support: <a style="%1" href="%2">%2</a> Übertragung fehlgeschlagen! Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu senden: <a style="%1" href="%2">%2</a> - + No kDrive configured! Kein kDrive eingerichtet! - - - - - - - - - - - + + + + + + + + + + + A technical error has occurred (error %1).<br>Please empty the history and if the error persists, contact our support team. Ein technischer Fehler ist aufgetreten (Fehler %1).<br>Bitte leeren Sie den Verlauf und kontaktieren Sie unser Support-Team, falls der Fehler weiterhin besteht. - + It seems that your network connection is configured with too low a timeout for the application to work correctly (error %1).<br>Please check your network configuration. Es scheint, dass Ihre Netzwerkverbindung mit einem zu niedrigen Timeout konfiguriert ist, als dass die Anwendung ordnungsgemäß funktionieren könnte (Fehler %1).<br>Bitte überprüfen Sie Ihre Netzwerkkonfiguration. - - + + Cannot connect to kDrive server (error %1).<br>Attempting reconnection. Please check your Internet connection and your firewall. Es kann keine Verbindung zum kDrive-Server hergestellt werden (Fehler %1).<br>Es wird versucht, die Verbindung wiederherzustellen. Bitte überprüfen Sie Ihre Internetverbindung und Ihre Firewall. - + A login problem has occurred (error %1).<br>Please log in again and if the error persists, contact our support team. Es ist ein Anmeldeproblem aufgetreten (Fehler %1).<br>Bitte melden Sie sich erneut an. Wenn der Fehler weiterhin besteht, wenden Sie sich an unser Support-Team. - + Old synchronisation database doesn't exist or is not accessible.<br>Old blacklist data haven't been migrated. Alte Synchronisationsdatenbank besteht nicht oder ist nicht zugänglich. <br/>Alte Blacklist-Daten wurden nicht migriert. - + A new version of the application is available.<br>Please update the application to continue using it. Eine neue Version der Anwendung ist verfügbar.<br>Bitte aktualisieren Sie die Anwendung, um sie weiterhin verwenden zu können. - + The log upload failed (error %1).<br>Please try again later. Das Hochladen des Protokolls ist fehlgeschlagen (Fehler %1).<br>Bitte versuchen Sie es später erneut. - - The item misses search permission (error %1).<br>Please check that you have search/exec access to the parent folder. - Für das Element fehlt die Suchberechtigung (Fehler %1).<br>Bitte überprüfen Sie, ob Sie Such-/Ausführungszugriff auf den übergeordneten Ordner haben. - - - + The synchronization folder is no longer accessible (error %1).<br>Synchronization will resume as soon as the folder is accessible. Auf den Synchronisierungsordner kann nicht mehr zugegriffen werden (Fehler %1).<br>Die Synchronisierung wird fortgesetzt, sobald auf den Ordner zugegriffen werden kann. - - The synchronization folder is inaccessible (error %1).<br>Please check that you have read access to this folder. - Auf den Synchronisierungsordner kann nicht zugegriffen werden (Fehler %1).<br>Bitte überprüfen Sie, ob Sie Lesezugriff auf diesen Ordner haben. - - - - The synchronization folder is inaccessible (error %1).<br>Please check that you have write access to this folder. - Der Synchronisierungsordner ist nicht zugänglich (Fehler %1). <br/>Bitte überprüfen Sie, ob Sie Schreibrechte für diesen Ordner haben. - - - + There is not enough space left on your disk.<br>The synchronization has been stopped. Es ist nicht mehr genug Speicherplatz auf Ihrer Festplatte vorhanden. <br/>Die Synchronisierung wurde angehalten. - + There is not enough memory left on your machine.<br>The synchronization has been stopped. Auf Ihrem Computer ist nicht mehr genügend Speicher vorhanden.<br>Die Synchronisierung wurde gestoppt. - + Unable to start synchronization (error %1).<br>You must allow:<br>- kDrive in System Settings >> Privacy & Security >> Security<br>- kDrive LiteSync Extension in System Settings >> Privacy & Security >> Full Disk Access. Die Synchronisierung konnte nicht gestartet werden (Fehler %1).<br>Sie müssen Folgendes zulassen:<br>- kDrive in den Systemeinstellungen >> Datenschutz und Sicherheit >> Sicherheit<br>- kDrive LiteSync-Erweiterung in den Systemeinstellungen >> Datenschutz und Sicherheit >> Vollständiger Festplattenzugriff. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension is installed and Windows Search service is enabled.<br>Please empty the history, restart and if the error persists, contact our support team. Das Lite Sync-Plugin kann nicht gestartet werden (Fehler %1).<br>Überprüfen Sie, ob die Lite Sync-Erweiterung installiert und der Windows-Suchdienst aktiviert ist.<br>Bitte leeren Sie den Verlauf, starten Sie neu und wenden Sie sich an unser Support-Team, wenn der Fehler weiterhin besteht. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension has the correct permissions and is running.<br>Please empty the history, restart and if the error persists, contact our support team. Das Lite Sync-Plugin konnte nicht gestartet werden (Fehler %1).<br>Überprüfen Sie, ob die Lite Sync-Erweiterung über die richtigen Berechtigungen verfügt und ausgeführt wird.<br>Bitte leeren Sie den Verlauf, starten Sie neu und wenn der Fehler weiterhin besteht, wenden Sie sich an unser Support-Team. - + Unable to start Lite Sync plugin (error %1).<br>Please empty the history, restart and if the error persists, contact our support team. Das Lite Sync-Plugin konnte nicht gestartet werden (Fehler %1).<br>Bitte leeren Sie den Verlauf, starten Sie neu und kontaktieren Sie unser Support-Team, wenn der Fehler weiterhin besteht. - + The kDrive is in maintenance mode.<br>Synchronization will begin again as soon as possible. Please contact our support team if the error persists. Der kDrive befindet sich im Wartungsmodus. Die <br/>Synchronisierung wird schnellstmöglich wieder aufgenommen. Bitte wenden Sie sich an unseren Support, sofern der Fehler weiterhin auftritt. - + The kDrive is blocked.<br>Please renew kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Der kDrive ist gesperrt. <br/>Bitte verlängern Sie kDrive. Wenn keine Massnahmen ergriffen werden, werden die Daten dauerhaft gelöscht und können nicht wiederhergestellt werden. - + The kDrive is blocked.<br>Please contact an administrator to renew the kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Der kDrive ist gesperrt. <br/>Bitte wenden Sie sich an einen Administrator, um den kDrive zu verlängern. Wenn keine Massnahmen ergriffen werden, werden die Daten dauerhaft gelöscht und können nicht wiederhergestellt werden. - + You are not authorised to access this kDrive.<br>Synchronization has been paused. Please contact an administrator. Sie sind nicht berechtigt, auf dieses kDrive zuzugreifen.<br>Die Synchronisierung wurde angehalten. Bitte wenden Sie sich an einen Administrator. - + A technical error has occurred (error %1).<br>Synchronization will resume as soon as possible. Please contact our support team if the error persists. Es ist ein technischer Fehler aufgetreten (Fehler %1).<br>Die Synchronisierung wird so bald wie möglich fortgesetzt. Bitte wenden Sie sich an unser Support-Team, wenn der Fehler weiterhin besteht. - + The network connections have been dropped by the kernel (error %1).<br>Please empty the history and if the error persists, contact our support team. Die Netzwerkverbindungen wurden vom Kernel unterbrochen (Fehler %1).<br>Bitte leeren Sie den Verlauf. Wenn der Fehler weiterhin besteht, wenden Sie sich an unser Support-Team. - + Unfortunately your old configuration could not be migrated.<br>The application will use a blank configuration. Leider konnte Ihre alte Konfiguration nicht migriert werden. <br/>Das Programm verwendet eine leere Konfiguration. - + Unfortunately your old proxy configuration could not be migrated, SOCKS5 proxies are not supported at this time.<br>The application will use system proxy settings instead. Leider konnte Ihre alte Proxy-Konfiguration nicht migriert werden, SOCKS5-Proxies werden derzeit nicht unterstützt. Stattdessen werden <br/>die System-Proxyeinstellungen verwendet. - + A technical error has occurred (error %1).<br>Synchronization has been restarted. Please empty the history and if the error persists, please contact our support team. Ein technischer Fehler (Fehler %1) ist aufgetreten. Die <br/>Synchronisierung wurde neu gestartet. Bitte leeren Sie den Verlauf; sollte der Fehler weiterhin auftreten, dann wenden Sie sich an unseren Support. - + An error accessing the synchronization database has happened (error %1).<br>Synchronization has been stopped. Beim Zugriff auf die Synchronisierungsdatenbank ist ein Fehler aufgetreten (Fehler %1). Die <br/>Synchronisierung wurde angehalten. - + A login problem has occurred (error %1).<br>Token invalid or revoked. Es ist ein Anmeldeproblem aufgetreten (Fehler %1).<br>Token ungültig oder widerrufen. - + Nested synchronizations are prohibited (error %1).<br>You should only keep synchronizations whose folders are not nested. Verschachtelte Synchronisierungen sind verboten (Fehler %1).<br>Sie sollten nur Synchronisierungen behalten, deren Ordner nicht verschachtelt sind. - - The app does not have write rights to the synchronization folder.<br>The synchronization has been stopped. - Die App hat keine Schreibrechte für den Synchronisierungsordner.<br>Die Synchronisierung wurde gestoppt. - - - + File name parsing error (error %1).<br>Special characters such as double quotes, backslashes or line returns can cause parsing failures. Fehler beim Parsen des Dateinamens (Fehler %1).<br>Sonderzeichen wie doppelte Anführungszeichen, umgekehrte Schrägstriche oder Zeilenumbrüche können zu Parsingfehlern führen. - + An element was moved to a deleted folder.<br>The move has been canceled. Ein Element wurde in einen gelöschten Ordner verschoben. <br/>Die Verschiebung wurde abgebrochen. - + This element was moved by another user.<br>The deletion has been canceled. Dieses Element wurde von einem anderen Benutzer verschoben. <br/>Das Löschen wurde abgebrochen. - + An element was created in this folder while it was being deleted.<br>The delete operation has been propagated anyway. Während des Löschens wurde in diesem Ordner ein Element erstellt.<br>Der Löschvorgang wurde trotzdem weitergegeben. - + This element has been moved somewhere else.<br>The local operation has been canceled. Dieses Element wurde an einen anderen Ort verschoben. <br/>Der lokale Vorgang wurde abgebrochen. - - + + An element with the same name already exists in this location.<br>The local element has been renamed. An diesem Ort besteht bereits ein Element mit dem gleichen Namen. <br/>Das lokale Element wurde umbenannt. - + An element with the same name already exists in this location.<br>The local operation has been canceled. An diesem Ort besteht bereits ein Element mit dem gleichen Namen. <br/>Der lokale Vorgang wurde abgebrochen. - + The content of the file was modified while it was being deleted.<br>The deletion has been canceled. Der Inhalt der Datei wurde während des Löschens verändert. <br/>Das Löschen wurde abgebrochen. - + The content of a synchronized element was modified while a parent folder was being deleted (e.g. the folder containing the current folder).<br>The file has been moved to the root of your kDrive. Der Inhalt eines synchronisierten Elements wurde geändert, während ein übergeordneter Ordner gelöscht wurde (z.B. der Ordner mit dem aktuellen Ordner). <br/>Die Datei wurde in das Stammverzeichnis Ihres kDrive verschoben. - + The content of an already synchronized file has been modified while this one or one of its parent folders has been deleted.<br> Der Inhalt einer bereits synchronisierten Datei wurde geändert, während diese oder eines ihrer übergeordneten Verzeichnisse gelöscht wurde.<br> - + The file was modified at the same time by another user.<br>Your modifications have been saved in a copy. Die Datei wurde zur gleichen Zeit von einem anderen Benutzer geändert. <br/>Ihre Änderungen wurden in einer Kopie gespeichert. - + Another user has moved a parent folder of the destination.<br>The local operation has been canceled. Ein anderer Benutzer hat einen übergeordneten Ordner des Ziels verschoben. <br/>Der lokale Vorgang wurde abgebrochen. - + An existing file/directory has an identical name with the same case options (same upper and lower case letters).<br>The file/directory has been temporarily blacklisted. Eine vorhandene Datei/ein vorhandenes Verzeichnis hat einen identischen Namen mit denselben Groß- und Kleinschreibungsoptionen (gleiche Groß- und Kleinbuchstaben).<br>Die Datei/das Verzeichnis wurde vorübergehend auf die schwarze Liste gesetzt. - + The file/directory name contains an unsupported character.<br>The file/directory has been temporarily blacklisted. Der Datei-/Verzeichnisname enthält ein nicht unterstütztes Zeichen.<br>Die Datei/das Verzeichnis wurde vorübergehend auf die schwarze Liste gesetzt. - + This file/directory name is reserved by your operating system.<br>The file/directory has been temporarily blacklisted. Dieser Datei-/Verzeichnisname ist von Ihrem Betriebssystem reserviert.<br>Die Datei/das Verzeichnis wurde vorübergehend auf die schwarze Liste gesetzt. - + The file/directory name is too long.<br>The file/directory has been temporarily blacklisted. Der Datei-/Verzeichnisname ist zu lang.<br>Die Datei/das Verzeichnis wurde vorübergehend auf die schwarze Liste gesetzt. - + The file/directory path is too long.<br>The file/directory is ignored. Der Datei-/Verzeichnispfad ist zu lang.<br>Die Datei/das Verzeichnis wird ignoriert. - + The file/directory name contains a recent UNICODE character not yet supported by your filesystem.<br>The parent directory has been excluded from synchronization. Der Datei-/Verzeichnisname enthält ein aktuelles UNICODE-Zeichen, das von Ihrem Dateisystem noch nicht unterstützt wird.<br>Das übergeordnete Verzeichnis wurde von der Synchronisierung ausgeschlossen. - + The file/directory name coincides with the name of another item in the same directory.<br>This item is temporarily blacklisted. Consider removing duplicate items. Der Datei-/Verzeichnisname stimmt mit dem Namen eines anderen Elements im selben Verzeichnis überein.<br>Dieses Element steht vorübergehend auf der schwarzen Liste. Ziehen Sie in Erwägung, doppelte Elemente zu entfernen. - + Either you are not allowed to create an item, or another item already exists with the same name.<br>The item has been excluded from synchronization. Entweder ist es Ihnen nicht erlaubt, einen Eintrag zu erstellen, oder es existiert bereits ein anderer Eintrag mit demselben Namen.<br>Der Eintrag wurde von der Synchronisierung ausgeschlossen. - + You are not allowed to edit item.<br>The file containing your modifications has been renamed and excluded from synchronization. Sie dürfen keine Elemente bearbeiten. <br/>Die Datei mit Ihren Änderungen wurde umbenannt und von der Synchronisierung ausgeschlossen. - + You are not allowed to rename item.<br>It will be restored with its original name. Sie dürfen das Element nicht umbenennen. <br/>Es wird mit seinem ursprünglichen Namen wiederhergestellt. - + You are not allowed to move item to "%1".<br>It will be restored to its original location. Sie dürfen das Element nicht zu "%1" verschieben.<br>Es wird an seinem ursprünglichen Ort wiederhergestellt. - + You are not allowed to delete item.<br>It will be restored to its original location. Sie dürfen das Element nicht löschen. <br/>Es wird an seinem ursprünglichen Ort wiederhergestellt. - + Failed to move this item to trash, it has been blacklisted. Dieses Objekt konnte nicht in den Papierkorb verschoben werden, es wurde auf die schwarze Liste gesetzt. - + This item has been excluded from sync because it is an hard link Dieses Element wurde von der Synchronisierung ausgeschlossen, da es sich um einen Hardlink handelt. - + The operation performed on item is forbidden.<br>The item has been temporarily blacklisted. Der Vorgang, der mit dem Artikel durchgeführt wurde, ist verboten.<br>Der Artikel wurde vorübergehend auf die schwarze Liste gesetzt. - + The operation performed on this item failed.<br>The item has been temporarily blacklisted. Der für diesen Artikel durchgeführte Vorgang ist fehlgeschlagen.<br>Der Artikel wurde vorübergehend auf die schwarze Liste gesetzt. - - Can't access item.<br>Please fix the write permissions and restart the synchronization. - Zugriff auf Element nicht möglich.<br>Bitte korrigieren Sie die Schreibberechtigungen und starten Sie die Synchronisation neu. - - - + Move to trash failed. Verschieben in den Papierkorb fehlgeschlagen. - + The file is too large to be uploaded. It has been temporarily blacklisted. Die Datei ist zu groß, um hochgeladen zu werden. Sie wurde vorübergehend auf die schwarze Liste gesetzt. - + Impossible to download the file. Es ist nicht möglich, die Datei herunterzuladen. - + You have exceeded your quota. Increase your space quota to re-enable file upload. Sie haben Ihr Kontingent überschritten. Erhöhen Sie Ihr Speicherplatzkontingent, um den Datei-Upload wieder zu aktivieren. - + + The synchronization folder is inaccessible (error %1).<br>Please check that you have read and write access to this folder. + Auf den Synchronisationsordner kann nicht zugegriffen werden (Fehler %1).<br>Bitte überprüfen Sie, ob Sie Lese- und Schreibrechte für diesen Ordner haben. + + + This item already exists on remote kDrive. It is not synced because it has been blacklisted. Dieser Artikel existiert bereits auf dem entfernten kDrive. Er wird nicht synchronisiert, da er auf der Blacklist steht. - + This item already exists on local file system. It is not synced. Dieser Artikel existiert bereits im lokalen Dateisystem. Er wird nicht synchronisiert. - + Failed to synchronize this item. It has been temporarily blacklisted.<br>Another attempt to sync it will be done in one hour or on next application startup. Synchronisierung dieses Elements fehlgeschlagen. Es wurde vorübergehend auf die Blacklist gesetzt.<br>Ein weiterer Versuch zur Synchronisierung wird in einer Stunde oder beim nächsten Start der Anwendung durchgeführt. - + This item has been excluded from sync by a custom template.<br>You can disable this type of notification from the Preferences Dieser Artikel wurde durch eine benutzerdefinierte Vorlage vom Sync ausgeschlossen. Sie können diese Art von Benachrichtigung in den Einstellungen deaktivieren - - + + Synchronization error. Synchronisierungsfehler. - + + Can't access item.<br>Please fix the read and write permissions. + Zugriff auf Element nicht möglich.<br>Bitte korrigieren Sie die Lese- und Schreibberechtigungen. + + + + System error. + Systemfehler. + + + A technical error has occurred.<br>Please empty the history and if the error persists, contact our support team. Es ist ein technischer Fehler aufgetreten.<br>Bitte leeren Sie den Verlauf. Wenn der Fehler weiterhin besteht, wenden Sie sich an unser Support-Team. @@ -2218,35 +2208,35 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send KDC::SocketApi - - - + + + Copy private share link Link zur privaten Freigabe kopieren - - + + Resharing this file is not allowed Die erneute Freigabe dieser Datei ist nicht erlaubt - - + + Resharing this folder is not allowed Die erneute Freigabe dieses Ordners ist nicht erlaubt - - - - + + + + Copy public share link Öffentlichen Freigabelink kopieren - - + + Open in browser Im Browser öffnen @@ -2322,202 +2312,202 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send KDC::SynthesisPopover - - + + Never Nie - + During 1 hour 1 Stunde lang - - + + Until tomorrow 8:00AM Bis morgen 8 Uhr - + During 3 days 3 Tage lang - + During 1 week 1 Woche lang - - + + Always Immer - + For 1 more hour Für 1 weitere Stunde - + For 3 more days Für weitere 3 Tage - + For 1 more week Für 1 weitere Woche - + Update kDrive App Aktualisieren Sie die kDrive-App - + This kDrive app version is not supported anymore. To access the latest features and enhancements, please update. Diese kDrive-App-Version wird nicht mehr unterstützt. Um auf die neuesten Funktionen und Verbesserungen zuzugreifen, aktualisieren Sie bitte. - - + + Update Aktualisieren - + Please download the latest version on the website. Bitte laden Sie die neueste Version auf der Website herunter. - + Update download in progress Update-Download läuft - + Looking for update... Auf der Suche nach Updates... - + Manual update Manuelles Update - + Unavailable Nicht verfügbar - + Quit kDrive kDrive beenden - + Show errors and informations Fehler und Informationen anzeigen - + Show informations Informationen anzeigen - + You can synchronize files <a style="%1" href="%2">from your computer</a> or on <a style="%1" href="%3">kdrive.infomaniak.com</a>. Sie können Dateien <a style="%1" href="%2">von Ihrem Computer</a> oder von <a style="%1" href="%3">kdrive.infomaniak.com</a> synchronisieren. - + Open in folder Im Ordner öffnen - + More actions Weitere Aktionen - + Synchronized Synchronisiert - + Favorites Favoriten - + Activity Aktivität - - + + Not implemented! Nicht implementiert! - + No synchronized folder for this Drive! Kein synchronisierter Ordner für diesen kDrive! - + No kDrive configured! Kein kDrive eingerichtet! - + Unable to access web site %1. Zugang zu Website %1 nicht möglich. - + Open %1 web version Webversion %1 öffnen - + Notifications disabled until %1 Benachrichtigungen deaktiviert bis %1 - + Disable Notifications Benachrichtigungen deaktivieren - + Need help Hilfe benötigt - + Unable to open link %1. Link %1 kann nicht geöffnet werden. - + Invalid link %1. Ungültiger Link %1. - + Unable to open folder url %1. Ordner-URL %1 kann nicht geöffnet werden. - + Drive parameters Drive-Einstellungen - + Application preferences Anwendungseinstellungen @@ -2647,47 +2637,47 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send Einstellungen können nicht gespeichert werden! - + The parent folder is a sync folder or contained in one Der übergeordnete Ordner ist ein Sync-Ordner oder befindet sich in einem / einer - + Can't find a valid path Kann keinen gültigen Pfad finden - + No valid folder selected! Kein gültiger Ordner ausgewählt! - + The selected path does not exist! Der ausgewählte Pfad existiert nicht! - + The selected path is not a folder! Der ausgewählte Pfad ist kein Ordner! - + You have no permission to write to the selected folder! Sie haben keine Schreibberechtigung für den ausgewählten Ordner! - + The local folder %1 contains a folder already synced. Please pick another one! Der lokale Ordner %1 enthält einen bereits synchronisierten Ordner. Bitte wählen Sie einen anderen! - + The local folder %1 is contained in a folder already synced. Please pick another one! Der lokale Ordner %1 befindet sich in einem bereits synchronisierten Ordner. Bitte wählen Sie einen anderen! - + The local folder %1 is already synced on the same drive. Please pick another one! Der lokale Ordner %1 ist bereits auf demselben Drive synchronisiert. Bitte wählen Sie einen anderen! @@ -2807,23 +2797,23 @@ Bitte verwenden Sie den folgenden Link, um die Protokolle an den Support zu send utility - + Cancel free up local space Lokalen Speicherplatz freigeben abbrechen - - + + Cancel make available locally Lokal verfügbar machen abbrechen - + Free up local space Lokalen Speicherplatz freigeben - + Always make available locally Immer lokal verfügbar machen diff --git a/translations/client_es.ts b/translations/client_es.ts index 5174b3c89..7fafeb01f 100644 --- a/translations/client_es.ts +++ b/translations/client_es.ts @@ -288,12 +288,12 @@ KDC::AppServer - + kDrive application is already running! ¡La aplicación kDrive ya se está ejecutando! - + %1 and %n other file(s) have been removed. %1 y otros %n archivo(s) han sido borrados. @@ -301,13 +301,13 @@ - + %1 has been removed. %1 names a file. %1 ha sido eliminado. - + %1 and %n other file(s) have been added. %1 y % otros archivo(s) han sido añadidos. @@ -315,13 +315,13 @@ - + %1 has been added. %1 names a file. %1 ha sido añadido. - + %1 and %n other file(s) have been updated. %1 y otros %n archivo(s) han sido actualizados. @@ -329,13 +329,13 @@ - + %1 has been updated. %1 names a file. %1 ha sido actualizado. - + %1 has been moved to %2 and %n other file(s) have been moved. %1 ha sido movido a %2 y otros %n archivo(s) han sido movidos. @@ -343,17 +343,17 @@ - + %1 has been moved to %2. %1 ha sido movido a %2. - + Sync Activity Actividad de sincronización - + A new folder larger than %1 MB has been added in the drive %2, you must validate its synchronization: %3. Se ha añadido una nueva carpeta mayor a %1 MB en el drive %2, debes validar su sincronización: %3. @@ -396,118 +396,118 @@ Selecciona los que quieras sincronizar: KDC::ClientGui - + Please sign in Inicia sesión - + Folder %1: %2 Carpeta %1: %2 - + There are no sync folders configured. No hay carpetas de sincronización configuradas. - + Synthesis Síntesis - + Preferences Preferencias - + Quit Salir - + Undefined State. Estado no definido. - + Waiting to start syncing. Esperando para comenzar la sincronización. - + Sync is running. Ejecutando sincronización. - + Sync was successful, unresolved conflicts. La sincronización se ha realizado, pero hay conflictos sin resolver. - + Last Sync was successful. La última sincronización se ha realizado correctamente. - + User Abort. Interrupción por el usuario. - + Sync is paused. La sincronización está en pausa. - + %1 (Sync is paused) %1 (sincronización en pausa) - + Do you really want to remove the synchronizations of the account <i>%1</i> ?<br><b>Note:</b> This will <b>not</b> delete any files. ¿Realmente desea eliminar las sincronizaciones de la cuenta <i>%1</i>?<br><b>Nota:</b> Esto <b>no</b> eliminará ningún archivo. - + REMOVE ALL SYNCHRONIZATIONS ELIMINAR TODAS LAS SINC. - + CANCEL CANCELAR - + Failed to start synchronizations! ¡Error al iniciar las sincronizaciones! - + Unable to open folder path %1. No es posible abrir la ruta de la carpeta %1. - + Unable to initialize kDrive client No se puede inicializar el cliente de kDrive - + Failed to fix conflict(s) on %1 item(s) in sync folder: %2 No se pudieron solucionar los conflictos en %1 elementos en la carpeta de sincronización: %2 - + Synchronization is paused La sincronización está en pausa - - + + The shared link has been copied to the clipboard. El enlace compartido se ha copiado en el portapapeles. @@ -680,152 +680,152 @@ Selecciona los que quieras sincronizar: KDC::DebuggingDialog - + Info Información - + Debug Depurar - + Warning Advertencia - + Error Error - + Fatal Fatal - + Debugging settings Configuración de depuración - + Save debugging information in a folder on my computer (Recommended) Guardar información de depuración en una carpeta de mi computadora (recomendado) - + This information enables IT support to determine the origin of an incident. Esta información permite al soporte de TI determinar el origen de un incidente. - + <a style="%1" href="%2">Open debugging folder</a> <a style="%1" href="%2">Abrir carpeta de depuración</a> - + Debug level Nivel de depuración - + The trace level lets you choose the extent of the debugging information recorded El nivel de seguimiento le permite elegir el alcance de la información de depuración registrada - + The extended full log collects a detailed history that can be used for debugging. Enabling it can slow down the kDrive application. El registro completo extendido recopila un historial detallado que se puede utilizar para la depuración. Habilitarlo puede ralentizar la aplicación kDrive. - + Extended Full Log Registro completo extendido - + Delete logs older than %1 days Eliminar registros de más de %1 días - + Share the debug folder with Infomaniak support. Comparte la carpeta de depuración con el soporte de Infomaniak. - + The last session is the periode since the last kDrive start. La última sesión es el período desde el último inicio de kDrive. - + Share only the last kDrive session Comparte solo la última sesión de kDrive - + Loading Cargando - + Cancel Cancelar - + SAVE GUARDAR - + CANCEL CANCELAR - + Failed to share No se pudo compartir - + 1. Check that you are logged in <br>2. Check that you have configured at least one kDrive 1. Verifique que haya iniciado sesión <br>2. Comprueba que tienes configurado al menos un kDrive - + (Connexion interrupted) (Conexión interrumpida) - + Share the folder with SwissTransfer <br> Compartir la carpeta con SwissTransfer <br> - + 1. We automatically compressed your log <a style="%1" href="%2">here</a>.<br> 1. Comprimimos automáticamente su registro <a style="%1" href="%2">aquí</a>.<br> - + 2. Transfer the archive with <a style="%1" href="%2">swisstransfer.com</a><br> 2. Transfiera el archivo con <a style="%1" href="%2">swisstransfer.com</a><br> - + 3. Share the link with <a style="%1" href="%2"> support@infomaniak.com </a><br> 3. Comparte el enlace con <a style="%1" href="%2"> support@infomaniak.com </a><br> - + Last upload the %1 Última carga el %1 - + Sharing has been cancelled Compartir ha sido cancelado @@ -835,39 +835,39 @@ Selecciona los que quieras sincronizar: La carpeta completa es grande (> 100 MB) y puede tardar algún tiempo en compartirse. Para reducir el tiempo de compartición, le recomendamos que comparta sólo la última sesión de kDrive. - + %1/%2/%3 at %4h%5m and %6s Date format for the last successful log upload. %1: month, %2: day, %3: year, %4: hour, %5: minute, %6: second %2/%1/%3 a las %4:%5 y %6s - + Do you want to save your modifications? ¿Deseas guardar tus cambios? - - + + Unable to open folder %1. No se puede abrir la carpeta %1. - + Share Compartir - + Sharing | step 1/2 %1% Compartir | paso 1/2 %1% - + Sharing | step 2/2 %1% Compartir | paso 2/2 %1% - + Canceling... Cancelado... @@ -1511,383 +1511,372 @@ Selecciona los que quieras sincronizar: KDC::ParametersDialog - + Unable to open folder path %1. No es posible abrir la ruta de la carpeta %1. - + Transmission done!<br>Please refer to identifier <b>%1</b> in bug reports. ¡Transmisión realizada!<br>Consulta el identificador <b>%1</b> en informes de fallos. - + Transmission failed! Please, use the following link to send the logs to the support: <a style="%1" href="%2">%2</a> ¡La transmisión falló! Por favor, utilice el siguiente enlace para enviar los registros al soporte: <a style="%1" href="%2">%2</a> - + No kDrive configured! ¡No hay ningún kDrive configurado! - - - - - - - - - - - + + + + + + + + + + + A technical error has occurred (error %1).<br>Please empty the history and if the error persists, contact our support team. Se ha producido un error técnico (error %1).<br>Vacíe el historial y, si el error persiste, póngase en contacto con nuestro equipo de soporte. - + It seems that your network connection is configured with too low a timeout for the application to work correctly (error %1).<br>Please check your network configuration. Parece que su conexión de red está configurada con un tiempo de espera demasiado bajo para que la aplicación funcione correctamente (error %1).<br>Compruebe su configuración de red. - - + + Cannot connect to kDrive server (error %1).<br>Attempting reconnection. Please check your Internet connection and your firewall. No se puede conectar al servidor kDrive (error %1).<br>Intentando reconexión. Por favor verifique su conexión a Internet y su firewall. - + A login problem has occurred (error %1).<br>Please log in again and if the error persists, contact our support team. Se ha producido un problema de inicio de sesión (error %1).<br>Inicie sesión nuevamente y, si el error persiste, comuníquese con nuestro equipo de soporte. - + Old synchronisation database doesn't exist or is not accessible.<br>Old blacklist data haven't been migrated. La antigua base de datos de sincronización no existe o no es accesible.<br>Los datos de la lista negra antigua no se han migrado. - + A new version of the application is available.<br>Please update the application to continue using it. Hay una nueva versión de la aplicación disponible.<br>Actualice la aplicación para seguir usándola. - + The log upload failed (error %1).<br>Please try again later. Error al cargar el registro (error %1).<br>Inténtelo de nuevo más tarde. - - The item misses search permission (error %1).<br>Please check that you have search/exec access to the parent folder. - El elemento no tiene permiso de búsqueda (error %1).<br>Compruebe que tiene acceso de búsqueda/ejecución a la carpeta principal. - - - + The synchronization folder is no longer accessible (error %1).<br>Synchronization will resume as soon as the folder is accessible. Ya no se puede acceder a la carpeta de sincronización (error %1).<br>La sincronización se reanudará tan pronto como se pueda acceder a la carpeta. - - The synchronization folder is inaccessible (error %1).<br>Please check that you have read access to this folder. - No se puede acceder a la carpeta de sincronización (error %1).<br>Compruebe que tiene acceso de lectura a esta carpeta. - - - - The synchronization folder is inaccessible (error %1).<br>Please check that you have write access to this folder. - La carpeta de sincronización es inaccesible (error %1). Comprueba que tienes acceso de escritura a esta carpeta. - - - + There is not enough space left on your disk.<br>The synchronization has been stopped. No queda suficiente espacio en tu disco.<br>La sincronización se ha detenido. - + There is not enough memory left on your machine.<br>The synchronization has been stopped. No queda suficiente memoria en su máquina.<br>La sincronización se ha detenido. - + Unable to start synchronization (error %1).<br>You must allow:<br>- kDrive in System Settings >> Privacy & Security >> Security<br>- kDrive LiteSync Extension in System Settings >> Privacy & Security >> Full Disk Access. No se puede iniciar la sincronización (error %1).<br>Debe permitir:<br>- kDrive en Configuración del sistema >> Privacidad y seguridad >> Seguridad<br>- Extensión kDrive LiteSync en Configuración del sistema >> Privacidad y seguridad >> Acceso total al disco. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension is installed and Windows Search service is enabled.<br>Please empty the history, restart and if the error persists, contact our support team. No se puede iniciar el complemento Lite Sync (error %1).<br>Compruebe que la extensión Lite Sync esté instalada y que el servicio de búsqueda de Windows esté habilitado.<br>Vacia el historial, reinicia y, si el error persiste, contacta a nuestro equipo de soporte. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension has the correct permissions and is running.<br>Please empty the history, restart and if the error persists, contact our support team. No se puede iniciar el complemento Lite Sync (error %1).<br>Compruebe que la extensión Lite Sync tenga los permisos correctos y se esté ejecutando.<br>Vacia el historial, reinicia y, si el error persiste, contacta a nuestro equipo de soporte. - + Unable to start Lite Sync plugin (error %1).<br>Please empty the history, restart and if the error persists, contact our support team. No se puede iniciar el complemento Lite Sync (error %1).<br>Vacia el historial, reinicia y, si el error persiste, contacta a nuestro equipo de soporte. - + The kDrive is in maintenance mode.<br>Synchronization will begin again as soon as possible. Please contact our support team if the error persists. kDrive está en modo de mantenimiento.<br>La sincronización comenzará de nuevo lo antes posible. Ponte en contacto con nuestro equipo de asistencia si el error persiste. - + The kDrive is blocked.<br>Please renew kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. El kDrive está bloqueado.<br>Renueva kDrive. Si no se toman medidas, los datos se eliminarán permanentemente y será imposible recuperarlos. - + The kDrive is blocked.<br>Please contact an administrator to renew the kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. El kDrive está bloqueado.<br>Ponte en contacto con un administrador para renovar el kDrive. Si no se toman medidas, los datos se eliminarán permanentemente y será imposible recuperarlos. - + You are not authorised to access this kDrive.<br>Synchronization has been paused. Please contact an administrator. No estás autorizado a acceder a este kDrive.<br>La sincronización se ha pausado. Por favor contacte a un administrador. - + A technical error has occurred (error %1).<br>Synchronization will resume as soon as possible. Please contact our support team if the error persists. Se ha producido un error técnico (error %1).<br>La sincronización se reanudará lo antes posible. Comuníquese con nuestro equipo de soporte si el error persiste. - + The network connections have been dropped by the kernel (error %1).<br>Please empty the history and if the error persists, contact our support team. El kernel ha interrumpido las conexiones de red (error %1).<br>Vacia el historial y, si el error persiste, ponte en contacto con nuestro equipo de soporte. - + Unfortunately your old configuration could not be migrated.<br>The application will use a blank configuration. Desafortunadamente no se ha podido migrar tu configuración anterior.<br>La aplicación usará una configuración en blanco. - + Unfortunately your old proxy configuration could not be migrated, SOCKS5 proxies are not supported at this time.<br>The application will use system proxy settings instead. Desafortunadamente no se ha podido migrar tu antigua configuración de proxy. Los proxies SOCKS5 no son compatibles en este momento.<br>La aplicación usará la configuración del proxy del sistema en su lugar. - + A technical error has occurred (error %1).<br>Synchronization has been restarted. Please empty the history and if the error persists, please contact our support team. Se ha producido un error técnico (error %1).<br>Se ha reiniciado la sincronización. Vacía el historial y si el error persiste, contacta con nuestro equipo de asistencia. - + An error accessing the synchronization database has happened (error %1).<br>Synchronization has been stopped. Se ha producido un error al acceder a la base de datos de sincronización (error %1).<br>La sincronización se ha detenido. - + A login problem has occurred (error %1).<br>Token invalid or revoked. Se ha producido un problema de inicio de sesión (error %1).<br>Token no válido o revocado. - + Nested synchronizations are prohibited (error %1).<br>You should only keep synchronizations whose folders are not nested. Las sincronizaciones anidadas están prohibidas (error %1).<br>Solo debes conservar las sincronizaciones cuyas carpetas no estén anidadas. - - The app does not have write rights to the synchronization folder.<br>The synchronization has been stopped. - La aplicación no tiene derechos de escritura en la carpeta de sincronización.<br>La sincronización se ha detenido. - - - + File name parsing error (error %1).<br>Special characters such as double quotes, backslashes or line returns can cause parsing failures. Error de análisis del nombre de archivo (error %1).<br>Los caracteres especiales como comillas dobles, barras invertidas o retornos de línea pueden provocar errores de análisis. - + An element was moved to a deleted folder.<br>The move has been canceled. Se ha movido un elemento a una carpeta eliminada.<br>El movimiento ha sido cancelado. - + This element was moved by another user.<br>The deletion has been canceled. Este elemento ha sido movido por otro usuario.<br>La eliminación ha sido cancelada. - + An element was created in this folder while it was being deleted.<br>The delete operation has been propagated anyway. Se creó un elemento en esta carpeta mientras se eliminaba.<br>La operación de eliminación se propagó de todos modos. - + This element has been moved somewhere else.<br>The local operation has been canceled. Este elemento se ha movido a otro lugar.<br>La operación local ha sido cancelada. - - + + An element with the same name already exists in this location.<br>The local element has been renamed. Ya existe un elemento con el mismo nombre en esta ubicación.<br>Se ha cambiado el nombre del elemento local. - + An element with the same name already exists in this location.<br>The local operation has been canceled. Ya existe un elemento con el mismo nombre en esta ubicación.<br>La operación local ha sido cancelada. - + The content of the file was modified while it was being deleted.<br>The deletion has been canceled. El contenido del archivo se modificó mientras se estaba borrando.<br>La eliminación ha sido cancelada. - + The content of a synchronized element was modified while a parent folder was being deleted (e.g. the folder containing the current folder).<br>The file has been moved to the root of your kDrive. El contenido de un elemento sincronizado se modificó mientras se estaba borrando una carpeta principal (p. ej., la carpeta que contiene la carpeta actual).<br>El archivo se ha movido a la raíz de tu kDrive. - + The content of an already synchronized file has been modified while this one or one of its parent folders has been deleted.<br> El contenido de un archivo ya sincronizado se cambió cuando el archivo o un directorio principal se había eliminado.<br> - + The file was modified at the same time by another user.<br>Your modifications have been saved in a copy. El archivo fue modificado al mismo tiempo por otro usuario.<br>Tus modificaciones se han guardado en una copia. - + Another user has moved a parent folder of the destination.<br>The local operation has been canceled. Otro usuario ha movido una carpeta principal del destino.<br>La operación local ha sido cancelada. - + An existing file/directory has an identical name with the same case options (same upper and lower case letters).<br>The file/directory has been temporarily blacklisted. Un archivo/directorio existente tiene un nombre idéntico con las mismas opciones de mayúsculas y minúsculas (mismas letras mayúsculas y minúsculas).<br>El archivo/directorio se ha incluido temporalmente en la lista negra. - + The file/directory name contains an unsupported character.<br>The file/directory has been temporarily blacklisted. El nombre del archivo/directorio contiene un carácter no admitido.<br>El archivo/directorio se ha incluido temporalmente en la lista negra. - + This file/directory name is reserved by your operating system.<br>The file/directory has been temporarily blacklisted. Este nombre de archivo/directorio está reservado por su sistema operativo.<br>El archivo/directorio se ha incluido temporalmente en la lista negra. - + The file/directory name is too long.<br>The file/directory has been temporarily blacklisted. El nombre del archivo/directorio es demasiado largo.<br>El archivo/directorio se ha incluido temporalmente en la lista negra. - + The file/directory path is too long.<br>The file/directory is ignored. La ruta del archivo/directorio es demasiado larga.<br>El archivo/directorio se ignora. - + The file/directory name contains a recent UNICODE character not yet supported by your filesystem.<br>The parent directory has been excluded from synchronization. El nombre del archivo/directorio contiene un carácter UNICODE reciente que aún no es compatible con su sistema de archivos.<br>El directorio principal se ha excluido de la sincronización. - + The file/directory name coincides with the name of another item in the same directory.<br>This item is temporarily blacklisted. Consider removing duplicate items. El nombre del archivo/directorio coincide con el nombre de otro elemento en el mismo directorio.<br>Este elemento está temporalmente en la lista negra. Considere eliminar elementos duplicados. - + Either you are not allowed to create an item, or another item already exists with the same name.<br>The item has been excluded from synchronization. O bien no se le permite crear un elemento, o bien ya existe otro elemento con el mismo nombre.<br>El elemento se ha excluido de la sincronización. - + You are not allowed to edit item.<br>The file containing your modifications has been renamed and excluded from synchronization. No tienes permiso para editar elementos.<br>Se ha cambiado el nombre del archivo que contiene tus modificaciones y se le ha excluido de la sincronización. - + You are not allowed to rename item.<br>It will be restored with its original name. No tienes permiso para cambiar el nombre del elemento.<br>Se restaurará con su nombre original. - + You are not allowed to move item to "%1".<br>It will be restored to its original location. No tienes permiso para mover el elemento a "%1".<br>Se restaurará a su ubicación original. - + You are not allowed to delete item.<br>It will be restored to its original location. No tienes permiso para eliminar el elemento.<br>Se restaurará a su ubicación original. - + Failed to move this item to trash, it has been blacklisted. No se ha podido mover este elemento a la papelera, se ha incluido en la lista negra. - + This item has been excluded from sync because it is an hard link Este elemento se ha excluido de la sincronización porque es un enlace duro - + The operation performed on item is forbidden.<br>The item has been temporarily blacklisted. La operación realizada en el artículo está prohibida.<br>El artículo ha sido incluido temporalmente en la lista negra. - + The operation performed on this item failed.<br>The item has been temporarily blacklisted. La operación realizada en este artículo ha fallado.<br>El artículo ha sido incluido temporalmente en la lista negra. - - Can't access item.<br>Please fix the write permissions and restart the synchronization. - Mover a la papelera fallido. - No se puede acceder al elemento.<br>Corrija los permisos de escritura y reinicie la sincronización. - - - + Move to trash failed. - + The file is too large to be uploaded. It has been temporarily blacklisted. El archivo es demasiado grande para ser cargado. Ha sido temporalmente bloqueado. - + Impossible to download the file. Imposible descargar el archivo. - + You have exceeded your quota. Increase your space quota to re-enable file upload. Has excedido tu cuota. Aumente su cuota de espacio para volver a habilitar la carga de archivos. - + + The synchronization folder is inaccessible (error %1).<br>Please check that you have read and write access to this folder. + La carpeta de sincronización es inaccesible (error %1).<br>Por favor, compruebe que tiene acceso de lectura y escritura a esta carpeta. + + + This item already exists on remote kDrive. It is not synced because it has been blacklisted. Este artículo ya existe en el kDrive remoto. No se sincroniza porque está en la lista negra. - + This item already exists on local file system. It is not synced. Este artículo ya existe en el sistema de archivos local. No está sincronizado. - + Failed to synchronize this item. It has been temporarily blacklisted.<br>Another attempt to sync it will be done in one hour or on next application startup. No se pudo sincronizar este elemento. Se ha incluido temporalmente en la lista negra.<br>Se realizará otro intento de sincronización en una hora o en el próximo inicio de la aplicación. - + This item has been excluded from sync by a custom template.<br>You can disable this type of notification from the Preferences Este artículo ha sido excluido del sincronizado por una plantilla personalizada. Puede desactivar este tipo de notificación desde las preferencias - - + + Synchronization error. Error de sincronización. - + + Can't access item.<br>Please fix the read and write permissions. + No se puede acceder al elemento.<br>Por favor, corrija los permisos de lectura y escritura. + + + + System error. + Error del sistema. + + + A technical error has occurred.<br>Please empty the history and if the error persists, contact our support team. Se ha producido un error técnico.<br>Vacíe el historial y, si el error persiste, póngase en contacto con nuestro equipo de soporte. @@ -2217,35 +2206,35 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: < KDC::SocketApi - - - + + + Copy private share link Copiar enlace para compartir privado - - + + Resharing this file is not allowed No está permitido volver a compartir este archivo - - + + Resharing this folder is not allowed No está permitido volver a compartir esta carpeta - - - - + + + + Copy public share link Copiar enlace para compartir públicamente - - + + Open in browser Abrir en navegador @@ -2321,202 +2310,202 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: < KDC::SynthesisPopover - - + + Never Nunca - + During 1 hour Durante 1 hora - - + + Until tomorrow 8:00AM Hasta mañana a las 8:00AM - + During 3 days Durante 3 días - + During 1 week Durante 1 semana - - + + Always Siempre - + For 1 more hour Por 1 hora más - + For 3 more days Por 3 días más - + For 1 more week Por 1 semana más - + Update kDrive App Actualizar la aplicación kDrive - + This kDrive app version is not supported anymore. To access the latest features and enhancements, please update. Esta versión de la aplicación kDrive ya no es compatible. Para acceder a las últimas funciones y mejoras, actualice. - - + + Update Actualizar - + Please download the latest version on the website. Descargue la última versión del sitio web. - + Update download in progress Descarga de actualización en curso - + Looking for update... Buscando actualización... - + Manual update Actualización manual - + Unavailable Indisponible - + Quit kDrive Salir de kDrive - + Show errors and informations Mostrar errores e informaciones - + Show informations Mostrar información - + You can synchronize files <a style="%1" href="%2">from your computer</a> or on <a style="%1" href="%3">kdrive.infomaniak.com</a>. Puedes sincronizar archivos <a style="%1" href="%2">desde tu ordenador</a> o en <a style="%1" href="%3">kdrive.infomaniak.com</a>. - + Open in folder Abrir en carpeta - + More actions Más acciones - + Synchronized Sincronizado - + Favorites Favoritos - + Activity Actividad - - + + Not implemented! ¡No implementado! - + No synchronized folder for this Drive! ¡No hay ninguna carpeta sincronizada para este Drive! - + No kDrive configured! ¡No hay ningún kDrive configurado! - + Unable to access web site %1. No es posible acceder al sitio web %1. - + Open %1 web version Abrir la versión web de %1 - + Notifications disabled until %1 Notificaciones deshabilitadas hasta %1 - + Disable Notifications Deshabilitar Notificaciones - + Need help Se necesita ayuda - + Unable to open link %1. No se puede abrir el enlace %1. - + Invalid link %1. Enlace %1 no válido. - + Unable to open folder url %1. No es posible abrir la url de carpeta %1. - + Drive parameters Parámetros de drive - + Application preferences Preferencias de aplicación @@ -2646,47 +2635,47 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: < ¡No se pueden guardar los parámetros! - + The parent folder is a sync folder or contained in one La carpeta principal es una carpeta de sincronización o está contenida en una - + Can't find a valid path No se puede encontrar una ruta válida - + No valid folder selected! ¡No se ha seleccionado una carpeta válida! - + The selected path does not exist! ¡La ruta seleccionada no existe! - + The selected path is not a folder! ¡La ruta seleccionada no es una carpeta! - + You have no permission to write to the selected folder! ¡No tienes permiso para escribir en la carpeta seleccionada! - + The local folder %1 contains a folder already synced. Please pick another one! La carpeta local %1 contiene una carpeta ya sincronizada.¡Elige otra! - + The local folder %1 is contained in a folder already synced. Please pick another one! La carpeta local %1 está contenida en una carpeta ya sincronizada.¡Elige otra! - + The local folder %1 is already synced on the same drive. Please pick another one! La carpeta local %1 ya está sincronizada en el mismo drive.¡Elige otra! @@ -2806,23 +2795,23 @@ Por favor, utilice el siguiente enlace para enviar los registros al soporte: < utility - + Cancel free up local space Cancelar liberar espacio local - - + + Cancel make available locally Cancelar disponible localmente - + Free up local space Liberar espacio local - + Always make available locally Siempre disponible en local diff --git a/translations/client_fr.ts b/translations/client_fr.ts index 738097edb..b7b45dc1a 100644 --- a/translations/client_fr.ts +++ b/translations/client_fr.ts @@ -288,12 +288,12 @@ KDC::AppServer - + kDrive application is already running! L'application kDrive est déjà en cours d'exécution! - + %1 and %n other file(s) have been removed. %1 a été supprimé. @@ -301,13 +301,13 @@ - + %1 has been removed. %1 names a file. %1 a été supprimé. - + %1 and %n other file(s) have been added. %1 et %n autre(s) fichier(s) ont été ajouté(s). @@ -315,13 +315,13 @@ - + %1 has been added. %1 names a file. %1 a été ajouté. - + %1 and %n other file(s) have been updated. %1 a été mis à jour. @@ -329,13 +329,13 @@ - + %1 has been updated. %1 names a file. %1 a été mis à jour. - + %1 has been moved to %2 and %n other file(s) have been moved. %1 a été déplacé vers %2. @@ -343,17 +343,17 @@ - + %1 has been moved to %2. %1 a été déplacé vers %2. - + Sync Activity Activité de synchronisation - + A new folder larger than %1 MB has been added in the drive %2, you must validate its synchronization: %3. Un nouveau dossier supérieur à %1 Mo a été ajouté dans le lecteur %2, vous devez valider sa synchronisation: %3. @@ -395,119 +395,119 @@ Sélectionnez ceux que vous souhaitez synchroniser: KDC::ClientGui - + Failed to fix conflict(s) on %1 item(s) in sync folder: %2 Échec de la résolution des conflits sur %1 élément(s) dans le dossier de synchronisation : %2 - + Please sign in Veuillez vous connecter - + Folder %1: %2 Dossier %1 : %2 - + There are no sync folders configured. Aucun dossier à synchroniser n'est configuré. - + Synthesis Synthèse - + Preferences Préférences Préférences - + Quit Quitter - + Undefined State. Statut indéfini. - + Waiting to start syncing. En attente de synchronisation. - + Sync is running. Synchronisation en cours. - + Sync was successful, unresolved conflicts. Synchronisation réussie, conflits non résolus. - + Last Sync was successful. Synchronisation terminée avec succès. - + User Abort. Abandon par l'utilisateur. - + Sync is paused. La synchronisation est en pause. - + %1 (Sync is paused) %1 (Synchronisation en pause) - + Do you really want to remove the synchronizations of the account <i>%1</i> ?<br><b>Note:</b> This will <b>not</b> delete any files. Voulez-vous vraiment supprimer les synchronisations du compte <i>%1</i> ?<br><b>Remarque :</b> Cela <b>ne</b> supprimera aucun fichier. - + REMOVE ALL SYNCHRONIZATIONS SUPPRIMER TOUTES LES SYNC - + CANCEL ANNULER - + Failed to start synchronizations! Échec du démarrage des synchronisations ! - + Unable to open folder path %1. Impossible d’ouvrir le dossier de débogage %1. - + Unable to initialize kDrive client Impossible d'initialiser le client kDrive - + Synchronization is paused La synchronisation est en pause - - + + The shared link has been copied to the clipboard. Le lien partagé a été copié dans le presse-papier. @@ -680,152 +680,152 @@ Sélectionnez ceux que vous souhaitez synchroniser: KDC::DebuggingDialog - + Info Information - + Debug Débogage - + Warning Avertissement - + Error Erreur - + Fatal Fatal - + Debugging settings Paramètres de débogage - + Save debugging information in a folder on my computer (Recommended) Enregistrer les informations de débogage dans un dossier sur mon ordinateur (recommandé) - + This information enables IT support to determine the origin of an incident. Ces informations permettent au support informatique de déterminer l’origine d’un incident. - + <a style="%1" href="%2">Open debugging folder</a> <a style="%1" href="%2">Ouvrir le dossier de débogage</a> - + Debug level Niveau de débogage - + The trace level lets you choose the extent of the debugging information recorded Le niveau de trace vous permet de choisir l'étendue des informations de débogage enregistrées - + The extended full log collects a detailed history that can be used for debugging. Enabling it can slow down the kDrive application. Le journal complet étendu collecte un historique détaillé qui peut être utilisé pour le débogage. L'activer peut ralentir l'application kDrive. - + Extended Full Log Journal complet étendu - + Delete logs older than %1 days Supprimer les journaux datant de plus de %1 jours - + Share the debug folder with Infomaniak support. Partagez le dossier debug avec le support Infomaniak. - + The last session is the periode since the last kDrive start. La dernière session est la période depuis le dernier démarrage de kDrive. - + Share only the last kDrive session Partager uniquement la dernière session kDrive - + Loading Chargement - + Cancel Annuler - + SAVE ENREGISTRER - + CANCEL ANNULER - + Failed to share Échec du partage - + 1. Check that you are logged in <br>2. Check that you have configured at least one kDrive 1. Vérifiez que vous êtes connecté <br>2. Vérifiez que vous avez configuré au moins un kDrive - + (Connexion interrupted) (Connexion interrompue) - + Share the folder with SwissTransfer <br> Partager le dossier avec SwissTransfer <br> - + 1. We automatically compressed your log <a style="%1" href="%2">here</a>.<br> 1. Nous avons automatiquement compressé votre journal <a style="%1" href="%2">ici</a>.<br> - + 2. Transfer the archive with <a style="%1" href="%2">swisstransfer.com</a><br> 2. Transférez l'archive avec <a style="%1" href="%2">swisstransfer.com</a><br> - + 3. Share the link with <a style="%1" href="%2"> support@infomaniak.com </a><br> 3. Partagez le lien avec <a style="%1" href="%2"> support@infomaniak.com </a><br> - + Last upload the %1 Dernier envoi le %1 - + Sharing has been cancelled Le partage a été annulé @@ -835,39 +835,39 @@ Sélectionnez ceux que vous souhaitez synchroniser: Le dossier entier est volumineux (> 100 Mo) et son partage peut prendre un certain temps. Pour réduire le temps de partage, nous vous recommandons de ne partager que la dernière session kDrive. - + %1/%2/%3 at %4h%5m and %6s Date format for the last successful log upload. %1: month, %2: day, %3: year, %4: hour, %5: minute, %6: second %2/%1/%3 à %4h%5 et %6s - + Do you want to save your modifications? Voulez-vous enregistrer vos modifications ? - - + + Unable to open folder %1. Impossible d'ouvrir le dossier %1. - + Share Partager - + Sharing | step 1/2 %1% Partage | étape 1/2 %1% - + Sharing | step 2/2 %1% Partage | étape 2/2 %1% - + Canceling... Annulation... @@ -1511,382 +1511,372 @@ Sélectionnez ceux que vous souhaitez synchroniser: KDC::ParametersDialog - + Unable to open folder path %1. Impossible d’ouvrir le dossier de débogage %1. - + Transmission done!<br>Please refer to identifier <b>%1</b> in bug reports. Transmission effectuée !<br>Veuillez vous référer à l'identifiant <b>%1</b> dans vos rapports de bugs. - + No kDrive configured! Aucun kDrive configuré ! - + Transmission failed! Please, use the following link to send the logs to the support: <a style="%1" href="%2">%2</a> Échec de la transmission ! Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style="%1" href="%2">%2</a> - - - - - - - - - - - + + + + + + + + + + + A technical error has occurred (error %1).<br>Please empty the history and if the error persists, contact our support team. Une erreur technique s'est produite (erreur %1).<br>Veuillez vider l'historique et si l'erreur persiste, contactez notre équipe d'assistance. - + It seems that your network connection is configured with too low a timeout for the application to work correctly (error %1).<br>Please check your network configuration. Il semble que votre connexion réseau soit configurée avec un délai d'expiration trop court pour que l'application fonctionne correctement (erreur %1).<br>Veuillez vérifier votre configuration réseau. - - + + Cannot connect to kDrive server (error %1).<br>Attempting reconnection. Please check your Internet connection and your firewall. Impossible de se connecter au serveur kDrive (erreur %1).<br>Tentative de reconnexion. Veuillez vérifier votre connexion Internet et votre pare-feu. - + A login problem has occurred (error %1).<br>Please log in again and if the error persists, contact our support team. Un problème de connexion est survenu (erreur %1).<br>Veuillez vous reconnecter et si l'erreur persiste, contactez notre équipe d'assistance. - + Old synchronisation database doesn't exist or is not accessible.<br>Old blacklist data haven't been migrated. L'ancienne base de données de synchronisation n'existe pas ou n'est pas accessible.<br>Les données de l'ancienne liste noire n'ont pas été migrées. - + A new version of the application is available.<br>Please update the application to continue using it. Une nouvelle version de l'application est disponible.<br>Veuillez mettre à jour l'application pour continuer à l'utiliser. - + The log upload failed (error %1).<br>Please try again later. Le téléchargement du journal a échoué (erreur %1).<br>Veuillez réessayer plus tard. - - The item misses search permission (error %1).<br>Please check that you have search/exec access to the parent folder. - L'élément ne dispose pas de l'autorisation de recherche (erreur %1).<br>Veuillez vérifier que vous disposez d'un accès de recherche/exécution au dossier parent. - - - + The synchronization folder is no longer accessible (error %1).<br>Synchronization will resume as soon as the folder is accessible. Le dossier de synchronisation n'est plus accessible (erreur %1).<br>La synchronisation reprendra dès que le dossier sera accessible. - - The synchronization folder is inaccessible (error %1).<br>Please check that you have read access to this folder. - Le dossier de synchronisation est inaccessible (erreur %1).<br>Veuillez vérifier que vous disposez d'un accès en lecture à ce dossier. - - - - The synchronization folder is inaccessible (error %1).<br>Please check that you have write access to this folder. - Le dossier de synchronisation est inaccessible (erreur %1).<br>Veuillez vérifier que vous disposez d'un accès en écriture à ce dossier. - - - + There is not enough space left on your disk.<br>The synchronization has been stopped. Il ne reste plus assez d'espace sur votre disque.<br>La synchronisation a été arrêtée. - + There is not enough memory left on your machine.<br>The synchronization has been stopped. Il ne reste plus assez de mémoire sur votre machine.<br>La synchronisation a été arrêtée. - + Unable to start synchronization (error %1).<br>You must allow:<br>- kDrive in System Settings >> Privacy & Security >> Security<br>- kDrive LiteSync Extension in System Settings >> Privacy & Security >> Full Disk Access. Impossible de démarrer la synchronisation (erreur %1).<br>Vous devez autoriser :<br>- kDrive dans les paramètres système >> Confidentialité et sécurité >> Sécurité<br>- l'extension kDrive LiteSync dans les paramètres système >> Confidentialité et sécurité >> Accès complet au disque. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension is installed and Windows Search service is enabled.<br>Please empty the history, restart and if the error persists, contact our support team. Impossible de démarrer le plug-in Lite Sync (erreur %1).<br>Vérifiez que l'extension Lite Sync est installée et que le service Windows Search est activé.<br>Veuillez vider l'historique, redémarrer et si l'erreur persiste, contactez notre équipe d'assistance. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension has the correct permissions and is running.<br>Please empty the history, restart and if the error persists, contact our support team. Impossible de démarrer le plugin Lite Sync (erreur %1).<br>Vérifiez que l'extension Lite Sync dispose des autorisations appropriées et est en cours d'exécution.<br>Veuillez vider l'historique, redémarrer et si l'erreur persiste, contactez notre équipe d'assistance. - + Unable to start Lite Sync plugin (error %1).<br>Please empty the history, restart and if the error persists, contact our support team. Impossible de démarrer le plugin Lite Sync (erreur %1).<br>Veuillez vider l'historique, redémarrer et si l'erreur persiste, contactez notre équipe d'assistance. - + The kDrive is in maintenance mode.<br>Synchronization will begin again as soon as possible. Please contact our support team if the error persists. Le kDrive est en mode maintenance.<br>La synchronisation recommencera dès que possible. Veuillez contacter notre équipe d'assistance si l'erreur persiste. - + The kDrive is blocked.<br>Please renew kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Le kDrive est bloqué.<br>Veuillez renouveler le kDrive. Si aucune action n'est entreprise, les données seront définitivement supprimées et il sera impossible de les récupérer. - + The kDrive is blocked.<br>Please contact an administrator to renew the kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Le kDrive est bloqué.<br>Veuillez contacter un administrateur pour renouveler le kDrive. Si aucune action n'est entreprise, les données seront définitivement supprimées et il sera impossible de les récupérer. - + You are not authorised to access this kDrive.<br>Synchronization has been paused. Please contact an administrator. Vous n'êtes pas autorisé à accéder à ce kDrive.<br>La synchronisation a été suspendue. Veuillez contacter un administrateur. - + A technical error has occurred (error %1).<br>Synchronization will resume as soon as possible. Please contact our support team if the error persists. Une erreur technique s'est produite (erreur %1).<br>La synchronisation reprendra dès que possible. Veuillez contacter notre équipe d'assistance si l'erreur persiste. - + The network connections have been dropped by the kernel (error %1).<br>Please empty the history and if the error persists, contact our support team. Les connexions réseau ont été interrompues par le noyau (erreur %1).<br>Veuillez vider l'historique et si l'erreur persiste, contactez notre équipe d'assistance. - + Unfortunately your old configuration could not be migrated.<br>The application will use a blank configuration. Malheureusement, votre ancienne configuration n'a pas pu être migrée.<br>L'application utilisera une configuration vierge. - + Unfortunately your old proxy configuration could not be migrated, SOCKS5 proxies are not supported at this time.<br>The application will use system proxy settings instead. Malheureusement, votre ancienne configuration de proxy n'a pas pu être migrée, les proxys SOCKS5 ne sont pas pris en charge pour le moment.<br>L'application utilisera à la place les paramètres de proxy du système. - + A technical error has occurred (error %1).<br>Synchronization has been restarted. Please empty the history and if the error persists, please contact our support team. Une erreur technique s'est produite (erreur %1).<br>La synchronisation a été redémarrée. Veuillez vider l'historique et si l'erreur persiste, veuillez contacter notre équipe d'assistance. - + An error accessing the synchronization database has happened (error %1).<br>Synchronization has been stopped. Une erreur d'accès à la base de données de synchronisation s'est produite (erreur %1).<br>La synchronisation a été arrêtée. - + A login problem has occurred (error %1).<br>Token invalid or revoked. Un problème de connexion est survenu (erreur %1).<br>Jeton invalide ou révoqué. - + Nested synchronizations are prohibited (error %1).<br>You should only keep synchronizations whose folders are not nested. Les synchronisations imbriquées sont interdites (erreur %1).<br>Vous ne devez conserver que les synchronisations dont les dossiers ne sont pas imbriqués. - - The app does not have write rights to the synchronization folder.<br>The synchronization has been stopped. - L'application ne dispose pas de droits d'écriture sur le dossier de synchronisation.<br>La synchronisation a été arrêtée. - - - + File name parsing error (error %1).<br>Special characters such as double quotes, backslashes or line returns can cause parsing failures. Erreur d'analyse du nom de fichier (erreur %1).<br>Les caractères spéciaux tels que les guillemets doubles, les barres obliques inverses ou les retours de ligne peuvent provoquer des échecs d'analyse. - + An element was moved to a deleted folder.<br>The move has been canceled. Un élément a été déplacé vers un dossier supprimé.<br>Le déplacement a été annulé. - + This element was moved by another user.<br>The deletion has been canceled. Cet élément a été déplacé par un autre utilisateur.<br>La suppression a été annulée. - + An element was created in this folder while it was being deleted.<br>The delete operation has been propagated anyway. Un élément a été créé dans ce dossier alors qu'il était en cours de suppression.<br>L'opération de suppression a quand même été propagée. - + This element has been moved somewhere else.<br>The local operation has been canceled. Cet élément a été déplacé ailleurs.<br>L'opération locale a été annulée. - - + + An element with the same name already exists in this location.<br>The local element has been renamed. Un élément portant le même nom existe déjà à cet emplacement.<br>L'élément local a été renommé. - + An element with the same name already exists in this location.<br>The local operation has been canceled. Un élément portant le même nom existe déjà à cet emplacement.<br>L'opération locale a été annulée. - + The content of the file was modified while it was being deleted.<br>The deletion has been canceled. Le contenu du fichier a été modifié lors de sa suppression.<br>La suppression a été annulée. - + The content of a synchronized element was modified while a parent folder was being deleted (e.g. the folder containing the current folder).<br>The file has been moved to the root of your kDrive. Le contenu d'un élément synchronisé a été modifié alors qu'un dossier parent était en cours de suppression (par exemple le dossier contenant le dossier en cours).<br>Le fichier a été déplacé à la racine de votre kDrive. - + The content of an already synchronized file has been modified while this one or one of its parent folders has been deleted.<br> Le contenu d'un fichier déjà synchronisé a été modifié alors qu'un répertoire parent ou celui-ci a été supprimé.<br> - + The file was modified at the same time by another user.<br>Your modifications have been saved in a copy. Le fichier a été modifié au même moment par un autre utilisateur.<br>Vos modifications ont été enregistrées dans une copie. - + Another user has moved a parent folder of the destination.<br>The local operation has been canceled. Un autre utilisateur a déplacé un dossier parent de la destination.<br>L'opération locale a été annulée. - + An existing file/directory has an identical name with the same case options (same upper and lower case letters).<br>The file/directory has been temporarily blacklisted. Un fichier/répertoire existant a un nom identique avec les mêmes options de casse (mêmes lettres majuscules et minuscules).<br>Le fichier/répertoire a été temporairement mis sur liste noire. - + The file/directory name contains an unsupported character.<br>The file/directory has been temporarily blacklisted. Le nom du fichier/répertoire contient un caractère non pris en charge.<br>Le fichier/répertoire a été temporairement mis sur liste noire. - + This file/directory name is reserved by your operating system.<br>The file/directory has been temporarily blacklisted. Ce nom de fichier/répertoire est réservé par votre système d'exploitation.<br>Le fichier/répertoire a été temporairement mis sur liste noire. - + The file/directory name is too long.<br>The file/directory has been temporarily blacklisted. Le nom du fichier/répertoire est trop long.<br>Le fichier/répertoire a été temporairement mis sur liste noire. - + The file/directory path is too long.<br>The file/directory is ignored. Le chemin du fichier/répertoire est trop long.<br>Le fichier/répertoire est ignoré. - + The file/directory name contains a recent UNICODE character not yet supported by your filesystem.<br>The parent directory has been excluded from synchronization. Le nom du fichier/répertoire contient un caractère UNICODE récent qui n'est pas encore pris en charge par votre système de fichiers.<br>Le répertoire parent a été exclu de la synchronisation. - + The file/directory name coincides with the name of another item in the same directory.<br>This item is temporarily blacklisted. Consider removing duplicate items. Le nom du fichier/répertoire coïncide avec le nom d'un autre élément dans le même répertoire.<br>Cet élément est temporairement mis sur liste noire. Envisagez de supprimer les éléments en double. - + Either you are not allowed to create an item, or another item already exists with the same name.<br>The item has been excluded from synchronization. Soit vous n'êtes pas autorisé à créer un élément, soit un autre élément existe déjà avec le même nom.<br>L'élément a été exclu de la synchronisation. - + You are not allowed to edit item.<br>The file containing your modifications has been renamed and excluded from synchronization. Vous n'êtes pas autorisé à modifier l'élément.<br>Le fichier contenant vos modifications a été renommé et exclu de la synchronisation. - + You are not allowed to rename item.<br>It will be restored with its original name. Vous n'êtes pas autorisé à renommer l'élément.<br>Il sera restauré avec son nom d'origine. - + You are not allowed to move item to "%1".<br>It will be restored to its original location. Vous n'êtes pas autorisé à déplacer l'élément vers "%1".<br>Il sera restauré à son emplacement d'origine. - + You are not allowed to delete item.<br>It will be restored to its original location. Vous n'êtes pas autorisé à supprimer l'élément.<br>Il sera restauré à son emplacement d'origine. - + Failed to move this item to trash, it has been blacklisted. Échec du déplacement de cet élément vers la corbeille, il a été mis sur liste noire. - + This item has been excluded from sync because it is an hard link Cet élément a été exclu de la synchronisation parce qu'il s'agit d'un lien dur. - + The operation performed on item is forbidden.<br>The item has been temporarily blacklisted. L'opération effectuée sur l'élément est interdite.<br>L'article a été temporairement mis sur liste noire. - + The operation performed on this item failed.<br>The item has been temporarily blacklisted. L'opération effectuée sur cet élément a échoué.<br>L'article a été temporairement mis sur liste noire. - - Can't access item.<br>Please fix the write permissions and restart the synchronization. - Impossible d'accéder à l'élément.<br>Veuillez corriger les autorisations d'écriture et redémarrer la synchronisation. - - - + Move to trash failed. Le déplacement vers la corbeille a échoué. - + The file is too large to be uploaded. It has been temporarily blacklisted. Le fichier est trop volumineux pour être téléchargé. Il a été temporairement mis sur liste noire. - + Impossible to download the file. Impossible de télécharger le fichier. - + You have exceeded your quota. Increase your space quota to re-enable file upload. Vous avez dépassé votre quota. Augmentez votre quota d'espace pour réactiver le téléchargement de fichiers. - + + The synchronization folder is inaccessible (error %1).<br>Please check that you have read and write access to this folder. + Le dossier de synchronisation est inaccessible (erreur %1).<br>Veuillez vérifier que vous avez accès en lecture et en écriture à ce dossier. + + + This item already exists on remote kDrive. It is not synced because it has been blacklisted. Cet élément existe déjà sur le kDrive distant. Il n'est pas synchronisé car il a été mis sur liste noire. - + This item already exists on local file system. It is not synced. Cet élément existe déjà sur le système de fichiers local. Il n'est pas synchronisé. - + Failed to synchronize this item. It has been temporarily blacklisted.<br>Another attempt to sync it will be done in one hour or on next application startup. Échec de la synchronisation de cet élément. Il a été temporairement mis sur liste noire.<br>Une autre tentative de synchronisation sera effectuée dans une heure ou au prochain démarrage de l'application. - + This item has been excluded from sync by a custom template.<br>You can disable this type of notification from the Preferences Cet élément a été exclu de la synchronisation par un modèle personnalisé. Vous pouvez désactiver ce type de notification dans les préférences - - + + Synchronization error. Erreur de synchronisation. - + + Can't access item.<br>Please fix the read and write permissions. + Impossible d'accéder à l'élément.<br>Veuillez corriger les autorisations de lecture et d'écriture. + + + + System error. + Erreur système. + + + A technical error has occurred.<br>Please empty the history and if the error persists, contact our support team. Une erreur technique s'est produite.<br>Veuillez vider l'historique et si l'erreur persiste, contactez notre équipe d'assistance. @@ -2216,35 +2206,35 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style= KDC::SocketApi - - - + + + Copy private share link Copier le lien de partage privé - - + + Resharing this file is not allowed Le partage de ce fichier n'est pas autorisé - - + + Resharing this folder is not allowed Le partage de ce dossier n'est pas autorisé - - - - + + + + Copy public share link Copier le lien de partage public - - + + Open in browser Ouvrir dans le navigateur web @@ -2320,202 +2310,202 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style= KDC::SynthesisPopover - - + + Never Jamais - + During 1 hour Pendant 1 heure - - + + Until tomorrow 8:00AM Jusqu’à demain 8h00 - + During 3 days Pendant 3 jours - + During 1 week Pendant 1 semaine - - + + Always Toujours - + For 1 more hour Pour 1 heure de plus - + For 3 more days Pour 3 heures de plus - + For 1 more week Pour 1 semaine de plus - + Update kDrive App Mettre à jour l'application kDrive - + This kDrive app version is not supported anymore. To access the latest features and enhancements, please update. Cette version de l'application kDrive n'est plus prise en charge. Pour accéder aux dernières fonctionnalités et améliorations, veuillez mettre à jour. - - + + Update Mise à jour - + Please download the latest version on the website. Veuillez télécharger la dernière version sur le site Web. - + Update download in progress Téléchargement de la mise à jour en cours - + Looking for update... Recherche d'une mise à jour... - + Manual update Mise à jour manuelle - + Unavailable Indisponible - + Quit kDrive Quitter kDrive - + Show errors and informations Afficher les erreurs et les informations - + Show informations Afficher les informations - + You can synchronize files <a style="%1" href="%2">from your computer</a> or on <a style="%1" href="%3">kdrive.infomaniak.com</a>. Vous pouvez synchroniser les fichiers <a style="%1" href="%2">depuis votre ordinateur</a> ou sur <a style="%1" href="%3">kdrive.infomaniak.com</ un>. - + Open in folder Ouvrir dans le dossier - + More actions Autres actions - + Synchronized Synchronisé - + Favorites Favoris - + Activity Activité - - + + Not implemented! Non implémenté ! - + No synchronized folder for this Drive! Aucun fichier synchronisé pour ce kDrive ! - + No kDrive configured! Aucun kDrive configuré ! - + Unable to access web site %1. Impossible d’accéder au site %1. - + Open %1 web version Ouvrir la version Web de %1 - + Notifications disabled until %1 Notifications désactivées jusqu’à %1 - + Disable Notifications Désactiver les notifications - + Need help Besoin d’aide - + Unable to open link %1. Impossible d’ouvrir le lien %1. - + Invalid link %1. Lien %1 invalide. - + Unable to open folder url %1. Impossible d’ouvrir l’URL du dossier %1. - + Drive parameters Paramètres du kDrive - + Application preferences Préférences de l’application @@ -2645,47 +2635,47 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style= Impossible d'enregistrer les paramètres ! - + The parent folder is a sync folder or contained in one Le dossier parent est un dossier de synchronisation ou contenu dans un - + Can't find a valid path Impossible de trouver un chemin valide - + No valid folder selected! Aucun dossier valable sélectionné! - + The selected path does not exist! Le chemin sélectionné n’existe pas! - + The selected path is not a folder! Le chemin sélectionné n'est pas un dossier! - + You have no permission to write to the selected folder! Vous n'avez pas la permission d'écrire dans le dossier sélectionné! - + The local folder %1 contains a folder already synced. Please pick another one! Le dossier local %1 contient un dossier déjà synchronisé. Veuillez en choisir un autre ! - + The local folder %1 is contained in a folder already synced. Please pick another one! Le dossier local %1 est contenu dans un dossier déjà synchronisé. Veuillez en choisir un autre ! - + The local folder %1 is already synced on the same drive. Please pick another one! Le dossier local %1 est déjà synchronisé sur le même lecteur. Veuillez en choisir un autre ! @@ -2870,18 +2860,18 @@ Veuillez utiliser le lien suivant pour envoyer les logs au support: <a style= Démarrage de la synchronisation - + Free up local space Libérer de l’espace local - + Cancel free up local space Annuler libérer l'espace local - - + + Cancel make available locally Annuler rendre disponible localement @@ -2920,7 +2910,7 @@ You can add one from the kDrive settings. Ajoutez-en un depuis la configuration du kDrive. - + Always make available locally Rendre toujours disponible localement diff --git a/translations/client_it.ts b/translations/client_it.ts index 66e7743d7..900104430 100644 --- a/translations/client_it.ts +++ b/translations/client_it.ts @@ -288,12 +288,12 @@ KDC::AppServer - + kDrive application is already running! L'applicazione kDrive è già in esecuzione! - + %1 and %n other file(s) have been removed. %1 e %n altri file sono stati rimossi. @@ -301,13 +301,13 @@ - + %1 has been removed. %1 names a file. %1 è stato rimosso. - + %1 and %n other file(s) have been added. %1 e %n altri file sono stati aggiunti. @@ -315,13 +315,13 @@ - + %1 has been added. %1 names a file. %1 è stato aggiunto. - + %1 and %n other file(s) have been updated. %1 e %n altri file sono stati aggiornati. @@ -329,13 +329,13 @@ - + %1 has been updated. %1 names a file. %1 è stato aggiornato. - + %1 has been moved to %2 and %n other file(s) have been moved. %1 è stato spostato in %2 e %n altri file sono stati spostati. @@ -343,17 +343,17 @@ - + %1 has been moved to %2. %1 è stato spostato in %2. - + Sync Activity Sincronizza attività - + A new folder larger than %1 MB has been added in the drive %2, you must validate its synchronization: %3. È stata aggiunta una nuova cartella più grande di %1 MB nell'unità %2, devi convalidarne la sincronizzazione: %3. @@ -396,118 +396,118 @@ Seleziona quelli che desideri sincronizzare: KDC::ClientGui - + Please sign in Accedi - + Folder %1: %2 Cartella %1: %2 - + There are no sync folders configured. Non è stata configurata alcuna cartella per la sincronizzazione. - + Synthesis Sintesi - + Preferences Preferenze - + Quit Esci - + Undefined State. Stato non definito. - + Waiting to start syncing. In attesa di iniziare la sincronizzazione. - + Sync is running. Sincronizzazione in corso. - + Sync was successful, unresolved conflicts. La sincronizzazione è stata completata correttamente, sono presenti conflitti non risolti. - + Last Sync was successful. L'ultima sincronizzazione è stata completata correttamente. - + User Abort. Interrotto dall'utente. - + Sync is paused. Sincronizzazione sospesa. - + %1 (Sync is paused) %1 (Sincronizzazione sospesa) - + Do you really want to remove the synchronizations of the account <i>%1</i> ?<br><b>Note:</b> This will <b>not</b> delete any files. Vuoi davvero rimuovere le sincronizzazioni dell'account <i>%1</i> ?<br><b>Nota:</b> questo <b>non</b> eliminerà alcun file. - + REMOVE ALL SYNCHRONIZATIONS RIMUOVERE TUTTE LE SINC - + CANCEL ANNULLA - + Failed to start synchronizations! Impossibile avviare la sincronizzazione! - + Unable to open folder path %1. Impossibile aprire il percorso della cartella %1. - + Unable to initialize kDrive client Impossibile inizializzare il client kDrive - + Failed to fix conflict(s) on %1 item(s) in sync folder: %2 Impossibile risolvere i conflitti su %1 elemento/i nella cartella di sincronizzazione: %2 - + Synchronization is paused Sincronizzazione sospesa - - + + The shared link has been copied to the clipboard. Il link condiviso è stato copiato negli appunti. @@ -680,152 +680,152 @@ Seleziona quelli che desideri sincronizzare: KDC::DebuggingDialog - + Info Info - + Debug Debug - + Warning Avviso - + Error Errore - + Fatal Irreversibile - + Debugging settings Impostazioni di debug - + Save debugging information in a folder on my computer (Recommended) Salva le informazioni di debug in una cartella sul mio computer (consigliato) - + This information enables IT support to determine the origin of an incident. Queste informazioni consentono al supporto IT di determinare l'origine di un incidente. - + <a style="%1" href="%2">Open debugging folder</a> <a style="%1" href="%2">Apri cartella di debug</a> - + Debug level Livello di debug - + The trace level lets you choose the extent of the debugging information recorded Il livello di traccia consente di scegliere l'entità delle informazioni di debug registrate - + The extended full log collects a detailed history that can be used for debugging. Enabling it can slow down the kDrive application. Il registro completo esteso raccoglie una cronologia dettagliata che può essere utilizzata per il debug. Abilitarla può rallentare l'applicazione kDrive. - + Extended Full Log Log completo esteso - + Delete logs older than %1 days Elimina i registri più vecchi di %1 giorni - + Share the debug folder with Infomaniak support. Condividi la cartella debug con il supporto di Infomaniak. - + The last session is the periode since the last kDrive start. L'ultima sessione è il periodo trascorso dall'ultimo avvio di kDrive. - + Share only the last kDrive session Condividi solo l'ultima sessione di kDrive - + Loading Caricamento - + Cancel Annulla - + SAVE SALVA - + CANCEL ANNULLA - + Failed to share Impossibile condividere - + 1. Check that you are logged in <br>2. Check that you have configured at least one kDrive 1. Verifica di aver effettuato l'accesso<br>2. Verifica di aver configurato almeno un kDrive - + (Connexion interrupted) (Connessione interrotta) - + Share the folder with SwissTransfer <br> Condividi la cartella con SwissTransfer <br> - + 1. We automatically compressed your log <a style="%1" href="%2">here</a>.<br> 1. Abbiamo compresso automaticamente il tuo registro <a style="%1" href="%2">qui</a>.<br> - + 2. Transfer the archive with <a style="%1" href="%2">swisstransfer.com</a><br> 2. Trasferisci l'archivio con <a style="%1" href="%2">swisstransfer.com</a><br> - + 3. Share the link with <a style="%1" href="%2"> support@infomaniak.com </a><br> 3. Condividi il link con <a style="%1" href="%2"> support@infomaniak.com </a><br> - + Last upload the %1 Ultimo caricamento il %1 - + Sharing has been cancelled La condivisione è stata annullata @@ -835,39 +835,39 @@ Seleziona quelli che desideri sincronizzare: L'intera cartella è di grandi dimensioni (> 100 MB) e potrebbe richiedere del tempo per essere condivisa. Per ridurre il tempo di condivisione, si consiglia di condividere solo l'ultima sessione di kDrive. - + %1/%2/%3 at %4h%5m and %6s Date format for the last successful log upload. %1: month, %2: day, %3: year, %4: hour, %5: minute, %6: second %2/%1/%3 alle %4:%5:%6 - + Do you want to save your modifications? Salvare le modifiche? - - + + Unable to open folder %1. Impossibile aprire la cartella %1. - + Share Condividere - + Sharing | step 1/2 %1% Condivisione | passaggio 1/2 %1% - + Sharing | step 2/2 %1% Condivisione | passaggio 2/2 %1% - + Canceling... Annullamento... @@ -1511,382 +1511,372 @@ Seleziona quelli che desideri sincronizzare: KDC::ParametersDialog - + Unable to open folder path %1. Impossibile aprire il percorso della cartella %1. - + Transmission done!<br>Please refer to identifier <b>%1</b> in bug reports. Invio completato!<br>Fai riferimento all'identificatore <b>%1</b> nelle segnalazioni di bug. - + Transmission failed! Please, use the following link to send the logs to the support: <a style="%1" href="%2">%2</a> Trasmissione fallita! Per favore, utilizza il seguente link per inviare i log al supporto: <a style="%1" href="%2">%2</a> - + No kDrive configured! Nessun kDrive configurato! - - - - - - - - - - - + + + + + + + + + + + A technical error has occurred (error %1).<br>Please empty the history and if the error persists, contact our support team. Si è verificato un errore tecnico (errore %1).<br>Svuota la cronologia e, se l'errore persiste, contatta il nostro team di supporto. - + It seems that your network connection is configured with too low a timeout for the application to work correctly (error %1).<br>Please check your network configuration. Sembra che la tua connessione di rete sia configurata con un timeout troppo basso perché l'applicazione funzioni correttamente (errore %1).<br>Verifica la configurazione di rete. - - + + Cannot connect to kDrive server (error %1).<br>Attempting reconnection. Please check your Internet connection and your firewall. Impossibile connettersi al server kDrive (errore %1).<br>Tentativo di riconnessione. Controlla la tua connessione Internet e il tuo firewall. - + A login problem has occurred (error %1).<br>Please log in again and if the error persists, contact our support team. Si è verificato un problema di accesso (errore %1).<br>Accedi nuovamente e se l'errore persiste, contatta il nostro team di supporto. - + Old synchronisation database doesn't exist or is not accessible.<br>Old blacklist data haven't been migrated. Il vecchio database di sincronizzazione non esiste o non è accessibile. I <br>vecchi dati della blacklist non sono stati migrati. - + A new version of the application is available.<br>Please update the application to continue using it. È disponibile una nuova versione dell'applicazione.<br>Aggiorna l'applicazione per continuare a utilizzarla. - + The log upload failed (error %1).<br>Please try again later. Il caricamento del registro non è riuscito (errore %1).<br>Riprova più tardi. - - The item misses search permission (error %1).<br>Please check that you have search/exec access to the parent folder. - L'elemento non dispone dell'autorizzazione di ricerca (errore %1).<br>Verifica di disporre dell'accesso di ricerca/esecuzione alla cartella principale. - - - + The synchronization folder is no longer accessible (error %1).<br>Synchronization will resume as soon as the folder is accessible. La cartella di sincronizzazione non è più accessibile (errore %1).<br>La sincronizzazione riprenderà non appena la cartella sarà accessibile. - - The synchronization folder is inaccessible (error %1).<br>Please check that you have read access to this folder. - La cartella di sincronizzazione non è accessibile (errore %1).<br>Verifica di avere accesso in lettura a questa cartella. - - - - The synchronization folder is inaccessible (error %1).<br>Please check that you have write access to this folder. - La cartella di sincronizzazione è inaccessibile (errore %1).<br>Controlla di avere accesso in scrittura a questa cartella. - - - + There is not enough space left on your disk.<br>The synchronization has been stopped. Non c'è spazio sufficiente sul disco.<br>La sincronizzazione è stata interrotta. - + There is not enough memory left on your machine.<br>The synchronization has been stopped. Memoria insufficiente sul computer.<br>La sincronizzazione è stata interrotta. - + Unable to start synchronization (error %1).<br>You must allow:<br>- kDrive in System Settings >> Privacy & Security >> Security<br>- kDrive LiteSync Extension in System Settings >> Privacy & Security >> Full Disk Access. Impossibile avviare la sincronizzazione (errore %1).<br>Devi consentire:<br>- kDrive in Impostazioni di sistema >> Privacy e sicurezza >> Sicurezza<br>- Estensione kDrive LiteSync in Impostazioni di sistema >> Privacy e sicurezza >> Accesso completo al disco. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension is installed and Windows Search service is enabled.<br>Please empty the history, restart and if the error persists, contact our support team. Impossibile avviare il plug-in Lite Sync (errore %1).<br>Verifica che l'estensione Lite Sync sia installata e che il servizio di ricerca di Windows sia abilitato.<br>Svuota la cronologia, riavvia e se l'errore persiste, contatta il nostro team di supporto. - + Unable to start Lite Sync plugin (error %1).<br>Check that the Lite Sync extension has the correct permissions and is running.<br>Please empty the history, restart and if the error persists, contact our support team. Impossibile avviare il plug-in Lite Sync (errore %1).<br>Verifica che l'estensione Lite Sync disponga delle autorizzazioni corrette e sia in esecuzione.<br>Svuota la cronologia, riavvia e se l'errore persiste, contatta il nostro team di supporto. - + Unable to start Lite Sync plugin (error %1).<br>Please empty the history, restart and if the error persists, contact our support team. Impossibile avviare il plug-in Lite Sync (errore %1).<br>Svuota la cronologia, riavvia e se l'errore persiste, contatta il nostro team di supporto. - + The kDrive is in maintenance mode.<br>Synchronization will begin again as soon as possible. Please contact our support team if the error persists. Il kDrive è in modalità di manutenzione.<br>La sincronizzazione riprenderà appena possibile. Contatta il nostro team di assistenza se l'errore persiste. - + The kDrive is blocked.<br>Please renew kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Il kDrive è bloccato.<br>Rinnovare kDrive. Se non si interviene, i dati verranno cancellati in modo definitivo e sarà impossibile recuperarli. - + The kDrive is blocked.<br>Please contact an administrator to renew the kDrive. If no action is taken, the data will be permanently deleted and it will be impossible to recover them. Il kDrive è bloccato.<br>Contatta un amministratore per rinnovare il kDrive. Se non si interviene, i dati verranno cancellati in modo definitivo e sarà impossibile recuperarli. - + You are not authorised to access this kDrive.<br>Synchronization has been paused. Please contact an administrator. Non sei autorizzato ad accedere a questo kDrive.<br>La sincronizzazione è stata sospesa. Contatta un amministratore. - + A technical error has occurred (error %1).<br>Synchronization will resume as soon as possible. Please contact our support team if the error persists. Si è verificato un errore tecnico (errore %1).<br>La sincronizzazione riprenderà il prima possibile. Contatta il nostro team di supporto se l'errore persiste. - + The network connections have been dropped by the kernel (error %1).<br>Please empty the history and if the error persists, contact our support team. Le connessioni di rete sono state interrotte dal kernel (errore %1).<br>Svuota la cronologia e se l'errore persiste, contatta il nostro team di supporto. - + Unfortunately your old configuration could not be migrated.<br>The application will use a blank configuration. Purtroppo la tua vecchia configurazione non può essere migrata. <br>L'applicazione userà una configurazione vuota. - + Unfortunately your old proxy configuration could not be migrated, SOCKS5 proxies are not supported at this time.<br>The application will use system proxy settings instead. Purtroppo la tua vecchia configurazione proxy non può essere migrata, i proxy SOCKS5 non sono supportati al momento. <br>L'applicazione userà invece le impostazioni del proxy di sistema. - + A technical error has occurred (error %1).<br>Synchronization has been restarted. Please empty the history and if the error persists, please contact our support team. Si è verificato un errore tecnico (errore %1).<br>La sincronizzazione è stata riavviata. Svuota la cronologia e se l'errore persiste, contatta il nostro team di assistenza. - + An error accessing the synchronization database has happened (error %1).<br>Synchronization has been stopped. Si è verificato un errore nell'accedere al database di sincronizzazione (errore %1). <br>La sincronizzazione è stata interrotta. - + A login problem has occurred (error %1).<br>Token invalid or revoked. Si è verificato un problema di accesso (errore %1).<br>Token non valido o revocato. - + Nested synchronizations are prohibited (error %1).<br>You should only keep synchronizations whose folders are not nested. Le sincronizzazioni nidificate sono vietate (errore %1).<br>Conservare solo le sincronizzazioni le cui cartelle non sono nidificate. - - The app does not have write rights to the synchronization folder.<br>The synchronization has been stopped. - L'app non dispone dei diritti di scrittura sulla cartella di sincronizzazione.<br>La sincronizzazione è stata interrotta. - - - + File name parsing error (error %1).<br>Special characters such as double quotes, backslashes or line returns can cause parsing failures. Errore durante l'analisi del nome file (errore %1).<br>Caratteri speciali come virgolette doppie, barre rovesciate o ritorni a capo possono causare errori di analisi. - + An element was moved to a deleted folder.<br>The move has been canceled. Un elemento è stato spostato in una cartella eliminata.<br>L'operazione è stata annullata. - + This element was moved by another user.<br>The deletion has been canceled. Questo elemento è stato spostato da un altro utente.<br>L'eliminazione è stata annullata. - + An element was created in this folder while it was being deleted.<br>The delete operation has been propagated anyway. Un elemento è stato creato in questa cartella durante l'eliminazione.<br>L'operazione di eliminazione è stata comunque propagata. - + This element has been moved somewhere else.<br>The local operation has been canceled. Questo elemento è stato spostato altrove.<br>L'operazione locale è stata annullata. - - + + An element with the same name already exists in this location.<br>The local element has been renamed. In questa posizione esiste già un elemento con lo stesso nome.<br>L'elemento locale è stato rinominato. - + An element with the same name already exists in this location.<br>The local operation has been canceled. Un elemento con lo stesso nome esiste già in questa posizione.<br>L'operazione locale è stata annullata. - + The content of the file was modified while it was being deleted.<br>The deletion has been canceled. Il contenuto del file è stato modificato mentre veniva eliminato.<br>L'eliminazione è stata annullata. - + The content of a synchronized element was modified while a parent folder was being deleted (e.g. the folder containing the current folder).<br>The file has been moved to the root of your kDrive. Il contenuto di un elemento sincronizzato è stato modificato mentre veniva eliminata una cartella padre (ad es. la cartella contenente la cartella corrente).<br>Il file è stato spostato nella radice del tuo kDrive. - + The content of an already synchronized file has been modified while this one or one of its parent folders has been deleted.<br> Il contenuto di un file già sincronizzato è stato modificato mentre esso o una sua directory padre è stata eliminata.<br> - + The file was modified at the same time by another user.<br>Your modifications have been saved in a copy. Il file è stato modificato contemporaneamente da un altro utente.<br>Le tue modifiche sono state salvate in una copia. - + Another user has moved a parent folder of the destination.<br>The local operation has been canceled. Un altro utente ha spostato una cartella padre della destinazione.<br>L'operazione locale è stata annullata. - + An existing file/directory has an identical name with the same case options (same upper and lower case letters).<br>The file/directory has been temporarily blacklisted. Un file/directory esistente ha un nome identico con le stesse opzioni maiuscole e minuscole (stesse lettere maiuscole e minuscole).<br>Il file/directory è stato temporaneamente inserito nella lista nera. - + The file/directory name contains an unsupported character.<br>The file/directory has been temporarily blacklisted. Il nome del file/della directory contiene un carattere non supportato.<br>Il file/la directory è stato temporaneamente inserito nella lista nera. - + This file/directory name is reserved by your operating system.<br>The file/directory has been temporarily blacklisted. Questo nome di file/directory è riservato dal tuo sistema operativo.<br>Il file/directory è stato temporaneamente inserito nella blacklist. - + The file/directory name is too long.<br>The file/directory has been temporarily blacklisted. Il nome file/directory è troppo lungo.<br>Il file/directory è stato temporaneamente inserito nella blacklist. - + The file/directory path is too long.<br>The file/directory is ignored. Il percorso del file/directory è troppo lungo.<br>Il file/directory viene ignorato. - + The file/directory name contains a recent UNICODE character not yet supported by your filesystem.<br>The parent directory has been excluded from synchronization. Il nome del file/directory contiene un carattere UNICODE recente non ancora supportato dal file system.<br>La directory principale è stata esclusa dalla sincronizzazione. - + The file/directory name coincides with the name of another item in the same directory.<br>This item is temporarily blacklisted. Consider removing duplicate items. Il nome del file/directory coincide con il nome di un altro elemento nella stessa directory.<br>Questo elemento è temporaneamente inserito nella lista nera. Valuta la possibilità di rimuovere gli elementi duplicati. - + Either you are not allowed to create an item, or another item already exists with the same name.<br>The item has been excluded from synchronization. O non si è autorizzati a creare un elemento, oppure esiste già un altro elemento con lo stesso nome.<br>L'elemento è stato escluso dalla sincronizzazione. - + You are not allowed to edit item.<br>The file containing your modifications has been renamed and excluded from synchronization. Non disponi dell'autorizzazione per modificare l'elemento.<br>Il file contenente le tue modifiche è stato rinominato ed escluso dalla sincronizzazione. - + You are not allowed to rename item.<br>It will be restored with its original name. Non disponi dell'autorizzazione per rinominare l'elemento.<br>Verrà ripristinato con il suo nome originale. - + You are not allowed to move item to "%1".<br>It will be restored to its original location. Non disponi dell'autorizzazione per spostare l'elemento in "%1".<br>Verrà ripristinato alla sua posizione originale. - + You are not allowed to delete item.<br>It will be restored to its original location. Non disponi dell'autorizzazione per eliminare l'elemento.<br>Verrà ripristinato alla sua posizione originale. - + Failed to move this item to trash, it has been blacklisted. Impossibile spostare questo elemento nel cestino, è stato inserito nella lista nera. - + This item has been excluded from sync because it is an hard link Questo elemento è stato escluso dalla sincronizzazione perché si tratta di un collegamento rigido. - + The operation performed on item is forbidden.<br>The item has been temporarily blacklisted. L'operazione eseguita sull'articolo è vietata.<br>L'articolo è stato temporaneamente inserito nella lista nera. - + The operation performed on this item failed.<br>The item has been temporarily blacklisted. L'operazione eseguita su questo articolo non è riuscita.<br>L'articolo è stato temporaneamente inserito nella lista nera. - - Can't access item.<br>Please fix the write permissions and restart the synchronization. - Impossibile accedere all'elemento.<br>Correggere i permessi di scrittura e riavviare la sincronizzazione. - - - + Move to trash failed. Spostamento nel cestino non riuscito. - + The file is too large to be uploaded. It has been temporarily blacklisted. Il file è troppo grande per essere caricato. È stato temporaneamente inserito nella lista nera. - + Impossible to download the file. Impossibile scaricare il file. - + You have exceeded your quota. Increase your space quota to re-enable file upload. Hai superato la tua quota. Aumenta la tua quota di spazio per riattivare il caricamento dei file. - + + The synchronization folder is inaccessible (error %1).<br>Please check that you have read and write access to this folder. + La cartella di sincronizzazione è inaccessibile (errore %1).<br>Verificare di avere accesso in lettura e scrittura a questa cartella. + + + This item already exists on remote kDrive. It is not synced because it has been blacklisted. Questo elemento esiste già su kDrive remoto. Non è sincronizzato perché è stato inserito nella lista nera. - + This item already exists on local file system. It is not synced. Questo elemento esiste già nel sistema di file locale. Non è sincronizzato. - + Failed to synchronize this item. It has been temporarily blacklisted.<br>Another attempt to sync it will be done in one hour or on next application startup. La sincronizzazione di questo elemento è fallita. È stato temporaneamente inserito nella lista nera.<br>Un altro tentativo di sincronizzazione verrà effettuato tra un'ora o al successivo avvio dell'applicazione. - + This item has been excluded from sync by a custom template.<br>You can disable this type of notification from the Preferences Questo elemento è stato escluso dalla sincronizzazione da un modello personalizzato. È possibile disattivare questo tipo di notifica dalle preferenze - - + + Synchronization error. Errore di sincronizzazione. - + + Can't access item.<br>Please fix the read and write permissions. + Impossibile accedere all'elemento.<br>Si prega di correggere i permessi di lettura e scrittura. + + + + System error. + Errore di sistema. + + + A technical error has occurred.<br>Please empty the history and if the error persists, contact our support team. Si è verificato un errore tecnico.<br>Svuota la cronologia e, se l'errore persiste, contatta il nostro team di assistenza. @@ -2216,35 +2206,35 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style KDC::SocketApi - - - + + + Copy private share link Copia il collegamento di condivisione privata - - + + Resharing this file is not allowed Ricondivisione di questo file non consentita - - + + Resharing this folder is not allowed Ricondivisione di questa cartella non consentita - - - - + + + + Copy public share link Copia il collegamento di condivisione pubblica - - + + Open in browser Apri nel browser @@ -2320,202 +2310,202 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style KDC::SynthesisPopover - - + + Never Mai - + During 1 hour Per 1 ora - - + + Until tomorrow 8:00AM Fino alle 8:00 di domani - + During 3 days Per 3 giorni - + During 1 week Per 1 settimana - - + + Always Sempre - + For 1 more hour Per 1 altra ora - + For 3 more days Per altri 3 giorni - + For 1 more week Per 1 altra settimana - + Update kDrive App Aggiorna l'app kDrive - + This kDrive app version is not supported anymore. To access the latest features and enhancements, please update. Questa versione dell'app kDrive non è più supportata. Per accedere alle funzionalità e ai miglioramenti più recenti, aggiornare. - - + + Update Aggiornamento - + Please download the latest version on the website. Si prega di scaricare l'ultima versione dal sito web. - + Update download in progress Download dell'aggiornamento in corso - + Looking for update... In cerca di aggiornamento... - + Manual update Aggiornamento manuale - + Unavailable Non disponibile - + Quit kDrive Esci da kDrive - + Show errors and informations Mostra errori e informazioni - + Show informations Mostra informazioni - + You can synchronize files <a style="%1" href="%2">from your computer</a> or on <a style="%1" href="%3">kdrive.infomaniak.com</a>. Puoi sincronizzare i file <a style="%1" href="%2">dal tuo computer</a> o su <a style="%1" href="%3">kdrive.infomaniak.com</a>. - + Open in folder Apri nella cartella - + More actions Più azioni - + Synchronized Sincronizzata - + Favorites Preferiti - + Activity Attività - - + + Not implemented! Non implementato! - + No synchronized folder for this Drive! Nessuna cartella sincronizzata per questo kDrive! - + No kDrive configured! Nessun kDrive configurato! - + Unable to access web site %1. Impossibile accedere al sito web %1. - + Open %1 web version Apri la versione web %1 - + Notifications disabled until %1 Notifiche disabilitate fino al %1 - + Disable Notifications Disabilita notifiche - + Need help Hai bisogno di aiuto - + Unable to open link %1. Impossibile aprire il link %1. - + Invalid link %1. Link %1 non valido. - + Unable to open folder url %1. Impossibile aprire l'URL della cartella %1. - + Drive parameters Parametri unità - + Application preferences Preferenze dell'applicazione @@ -2645,47 +2635,47 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style Impossibile salvare i parametri! - + The parent folder is a sync folder or contained in one La cartella padre è una cartella di sincronizzazione o contenuta in un - + Can't find a valid path Impossibile trovare un percorso valido - + No valid folder selected! Nessuna cartella valida selezionata! - + The selected path does not exist! Il percorso selezionato non esiste! - + The selected path is not a folder! Il percorso selezionato non è una cartella! - + You have no permission to write to the selected folder! Non disponi dell'autorizzazione di scrittura per la cartella selezionata! - + The local folder %1 contains a folder already synced. Please pick another one! La cartella locale %1 contiene una cartella già sincronizzata. Scegline un'altra! - + The local folder %1 is contained in a folder already synced. Please pick another one! La cartella locale %1 è contenuta in una cartella già sincronizzata. Scegline un'altra! - + The local folder %1 is already synced on the same drive. Please pick another one! La cartella locale %1 è già sincronizzata sulla stessa unità. Scegline un'altra! @@ -2806,23 +2796,23 @@ Per favore, utilizza il seguente link per inviare i log al supporto: <a style utility - + Free up local space Libera spazio locale - + Cancel free up local space Annulla liberazione di spazio locale - - + + Cancel make available locally Annulla rendere disponibile localmente - + Always make available locally Rendi sempre disponibile localmente