From 9ce13486eff46f3466bcff62b9590b82b8ec41bf Mon Sep 17 00:00:00 2001 From: ArceDanielShok Date: Mon, 31 Mar 2025 14:25:51 -0300 Subject: [PATCH 1/5] Add GetRegisteredSyncRoots functionality to SyncRoot and expose it through wrappers --- include/sync_root_interface/SyncRoot.h | 13 +++- include/virtual_drive/Wrappers.h | 1 + native-src/main.cpp | 18 +++++ native-src/sync_root_interface/SyncRoot.cpp | 43 +++++++++++- native-src/virtual_drive/Wrappers.cpp | 78 +++++++++++++++++++++ src/addon-wrapper.ts | 5 ++ src/addon.ts | 1 + src/addon/addon-zod.ts | 8 +++ src/virtual-drive.ts | 4 ++ 9 files changed, 168 insertions(+), 3 deletions(-) diff --git a/include/sync_root_interface/SyncRoot.h b/include/sync_root_interface/SyncRoot.h index 6d74f738..af4860fd 100644 --- a/include/sync_root_interface/SyncRoot.h +++ b/include/sync_root_interface/SyncRoot.h @@ -3,6 +3,16 @@ #include #include #include "stdafx.h" +#include +#include + +struct SyncRoots +{ + std::wstring id; + std::wstring path; + std::wstring displayName; + std::wstring version; +}; struct ItemInfo { @@ -15,6 +25,7 @@ class SyncRoot { public: static HRESULT RegisterSyncRoot(const wchar_t *syncRootPath, const wchar_t *providerName, const wchar_t *providerVersion, const GUID &providerId, const wchar_t *logoPath); + static std::vector GetRegisteredSyncRoots(); static HRESULT ConnectSyncRoot(const wchar_t *syncRootPath, InputSyncCallbacks syncCallbacks, napi_env env, CF_CONNECTION_KEY *connectionKey); static HRESULT DisconnectSyncRoot(const wchar_t *syncRootPath); static HRESULT UnregisterSyncRoot(const GUID &providerId); @@ -25,4 +36,4 @@ class SyncRoot private: CF_CONNECTION_KEY connectionKey; -}; \ No newline at end of file +}; diff --git a/include/virtual_drive/Wrappers.h b/include/virtual_drive/Wrappers.h index 49248a1b..00461098 100644 --- a/include/virtual_drive/Wrappers.h +++ b/include/virtual_drive/Wrappers.h @@ -5,6 +5,7 @@ napi_value CreatePlaceholderFile(napi_env env, napi_callback_info args); napi_value UnregisterSyncRootWrapper(napi_env env, napi_callback_info args); napi_value RegisterSyncRootWrapper(napi_env env, napi_callback_info args); +napi_value GetRegisteredSyncRootsWrapper(napi_env env, napi_callback_info args); napi_value ConnectSyncRootWrapper(napi_env env, napi_callback_info args); napi_value CreateEntryWrapper(napi_env env, napi_callback_info args); napi_value DisconnectSyncRootWrapper(napi_env env, napi_callback_info args); diff --git a/native-src/main.cpp b/native-src/main.cpp index ac64dfbb..40223357 100644 --- a/native-src/main.cpp +++ b/native-src/main.cpp @@ -57,6 +57,24 @@ napi_value init(napi_env env, napi_value exports) return nullptr; } + // GetRegisteredSyncRootsWrapper + napi_property_descriptor getRegisteredSyncRootsRootDesc = { + "getRegisteredSyncRoots", + nullptr, + GetRegisteredSyncRootsWrapper, + nullptr, + nullptr, + nullptr, + napi_default, + nullptr}; + + napi_status defineGetRegisteredSyncRootsRootDescStatus = napi_define_properties(env, exports, 1, &getRegisteredSyncRootsRootDesc); + if (defineGetRegisteredSyncRootsRootDescStatus != napi_ok) + { + napi_throw_error(env, nullptr, "Failed to define getRegisteredSyncRoots function"); + return nullptr; + } + // ConnectSyncRootWrapper napi_property_descriptor connectSyncRootDesc = { "connectSyncRoot", diff --git a/native-src/sync_root_interface/SyncRoot.cpp b/native-src/sync_root_interface/SyncRoot.cpp index 3cb6c7b6..11fa53ad 100644 --- a/native-src/sync_root_interface/SyncRoot.cpp +++ b/native-src/sync_root_interface/SyncRoot.cpp @@ -1,10 +1,10 @@ #include "Callbacks.h" #include "SyncRoot.h" #include "stdafx.h" -#include -#include #include #include "Logger.h" +#include +#include namespace fs = std::filesystem; // variable to disconect @@ -170,6 +170,45 @@ HRESULT SyncRoot::RegisterSyncRoot(const wchar_t *syncRootPath, const wchar_t *p } } +std::vector SyncRoot::GetRegisteredSyncRoots() +{ + std::vector syncRootList; + try + { + auto syncRoots = winrt::StorageProviderSyncRootManager::GetCurrentSyncRoots(); + + printf("Sync roots count: %d\n", syncRoots.Size()); + + for (auto const &info : syncRoots) + { + auto contextBuffer = info.Context(); + std::wstring contextString; + if (contextBuffer) + { + contextString = winrt::CryptographicBuffer::ConvertBinaryToString( + winrt::BinaryStringEncoding::Utf8, + contextBuffer) + .c_str(); + } + + if (contextString.find(L"->") != std::wstring::npos) + { + SyncRoots sr; + sr.id = info.Id(); + sr.path = info.Path().Path(); + sr.displayName = info.DisplayNameResource(); + sr.version = info.Version(); + syncRootList.push_back(sr); + } + } + } + catch (...) + { + Logger::getInstance().log("ERROR: getting sync root", LogLevel::INFO); + } + return syncRootList; +} + HRESULT SyncRoot::UnregisterSyncRoot(const GUID &providerId) { try diff --git a/native-src/virtual_drive/Wrappers.cpp b/native-src/virtual_drive/Wrappers.cpp index 572d99b6..c572c6bd 100644 --- a/native-src/virtual_drive/Wrappers.cpp +++ b/native-src/virtual_drive/Wrappers.cpp @@ -5,6 +5,15 @@ #include "LoggerPath.h" #include #include +#include +#include +#include + +std::string WStringToUTF8(const std::wstring &wstr) +{ + std::wstring_convert> conv; + return conv.to_bytes(wstr); +} napi_value CreatePlaceholderFile(napi_env env, napi_callback_info args) { @@ -202,6 +211,75 @@ napi_value RegisterSyncRootWrapper(napi_env env, napi_callback_info args) return napiResult; } +napi_value GetRegisteredSyncRootsWrapper(napi_env env, napi_callback_info args) +{ + try + { + std::vector roots = SyncRoot::GetRegisteredSyncRoots(); + + napi_value jsArray; + napi_status status = napi_create_array_with_length(env, roots.size(), &jsArray); + if (status != napi_ok) + throw std::runtime_error("Error creando el array"); + + for (size_t i = 0; i < roots.size(); i++) + { + napi_value jsObj; + status = napi_create_object(env, &jsObj); + if (status != napi_ok) + throw std::runtime_error("Error creando el objeto"); + + std::string id = WStringToUTF8(roots[i].id); + napi_value napiId; + status = napi_create_string_utf8(env, id.c_str(), id.size(), &napiId); + if (status != napi_ok) + throw std::runtime_error("Error creando la cadena id"); + napi_set_named_property(env, jsObj, "id", napiId); + + // Propiedad "path" + std::string path = WStringToUTF8(roots[i].path); + napi_value napiPath; + status = napi_create_string_utf8(env, path.c_str(), path.size(), &napiPath); + if (status != napi_ok) + throw std::runtime_error("Error creando la cadena path"); + napi_set_named_property(env, jsObj, "path", napiPath); + + // Propiedad "displayName" + std::string displayName = WStringToUTF8(roots[i].displayName); + napi_value napiDisplayName; + status = napi_create_string_utf8(env, displayName.c_str(), displayName.size(), &napiDisplayName); + if (status != napi_ok) + throw std::runtime_error("Error creando la cadena displayName"); + napi_set_named_property(env, jsObj, "displayName", napiDisplayName); + + // Propiedad "version" + std::string version = WStringToUTF8(roots[i].version); + napi_value napiVersion; + status = napi_create_string_utf8(env, version.c_str(), version.size(), &napiVersion); + if (status != napi_ok) + throw std::runtime_error("Error creando la cadena version"); + napi_set_named_property(env, jsObj, "version", napiVersion); + + // Insertamos el objeto en el array + status = napi_set_element(env, jsArray, i, jsObj); + if (status != napi_ok) + throw std::runtime_error("Error insertando el elemento en el array"); + } + + return jsArray; + } + catch (const std::exception &ex) + { + napi_throw_error(env, nullptr, ex.what()); + return nullptr; + } + catch (...) + { + napi_throw_error(env, nullptr, "An unknown error occurred in GetRegisteredSyncRootsWrapper"); + return nullptr; + } +} + napi_value ConnectSyncRootWrapper(napi_env env, napi_callback_info args) { try diff --git a/src/addon-wrapper.ts b/src/addon-wrapper.ts index 08438dfa..967216cf 100644 --- a/src/addon-wrapper.ts +++ b/src/addon-wrapper.ts @@ -27,6 +27,11 @@ export class Addon { return this.parseAddonZod("registerSyncRoot", result); } + getRegisteredSyncRoots() { + const result = addon.getRegisteredSyncRoots(); + return this.parseAddonZod("getRegisteredSyncRoots", result); + } + connectSyncRoot({ callbacks }: { callbacks: Callbacks }) { const result = addon.connectSyncRoot(this.syncRootPath, callbacks); return this.parseAddonZod("connectSyncRoot", result); diff --git a/src/addon.ts b/src/addon.ts index 0faf0632..cb20fbbd 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -53,4 +53,5 @@ export type TAddon = { * TODO: Returns a type in c++ that is not initialized */ updateFileIdentity(itemPath: string, id: string, isDirectory: boolean): any; + getRegisteredSyncRoots(): z.infer; }; diff --git a/src/addon/addon-zod.ts b/src/addon/addon-zod.ts index f8ffbbc9..c55e8b14 100644 --- a/src/addon/addon-zod.ts +++ b/src/addon/addon-zod.ts @@ -14,4 +14,12 @@ export const addonZod = { registerSyncRoot: z.literal(0), updateSyncStatus: z.boolean(), unregisterSyncRoot: z.number(), + getRegisteredSyncRoots: z.array( + z.object({ + id: z.string(), + path: z.string(), + displayName: z.string(), + version: z.string(), + }), + ), }; diff --git a/src/virtual-drive.ts b/src/virtual-drive.ts index b5855a89..933d434a 100644 --- a/src/virtual-drive.ts +++ b/src/virtual-drive.ts @@ -160,6 +160,10 @@ class VirtualDrive { }); } + static async getRegisteredSyncRoots() { + return await DependencyInjectionAddonProvider.get().getRegisteredSyncRoots(); + } + unregisterSyncRoot() { return this.addon.unregisterSyncRoot({ providerId: this.providerId }); } From ab48bd0f47708baa727e2c9c2cde929baeb45c48 Mon Sep 17 00:00:00 2001 From: ArceDanielShok Date: Mon, 31 Mar 2025 14:55:29 -0300 Subject: [PATCH 2/5] Refactor error messages in GetRegisteredSyncRootsWrapper for clarity and consistency --- native-src/virtual_drive/Wrappers.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/native-src/virtual_drive/Wrappers.cpp b/native-src/virtual_drive/Wrappers.cpp index c572c6bd..1c4906f7 100644 --- a/native-src/virtual_drive/Wrappers.cpp +++ b/native-src/virtual_drive/Wrappers.cpp @@ -220,50 +220,46 @@ napi_value GetRegisteredSyncRootsWrapper(napi_env env, napi_callback_info args) napi_value jsArray; napi_status status = napi_create_array_with_length(env, roots.size(), &jsArray); if (status != napi_ok) - throw std::runtime_error("Error creando el array"); + throw std::runtime_error("Error creating the array"); for (size_t i = 0; i < roots.size(); i++) { napi_value jsObj; status = napi_create_object(env, &jsObj); if (status != napi_ok) - throw std::runtime_error("Error creando el objeto"); + throw std::runtime_error("Error creating the object"); std::string id = WStringToUTF8(roots[i].id); napi_value napiId; status = napi_create_string_utf8(env, id.c_str(), id.size(), &napiId); if (status != napi_ok) - throw std::runtime_error("Error creando la cadena id"); + throw std::runtime_error("Error creating the string id"); napi_set_named_property(env, jsObj, "id", napiId); - // Propiedad "path" std::string path = WStringToUTF8(roots[i].path); napi_value napiPath; status = napi_create_string_utf8(env, path.c_str(), path.size(), &napiPath); if (status != napi_ok) - throw std::runtime_error("Error creando la cadena path"); + throw std::runtime_error("Error creating the string path"); napi_set_named_property(env, jsObj, "path", napiPath); - // Propiedad "displayName" std::string displayName = WStringToUTF8(roots[i].displayName); napi_value napiDisplayName; status = napi_create_string_utf8(env, displayName.c_str(), displayName.size(), &napiDisplayName); if (status != napi_ok) - throw std::runtime_error("Error creando la cadena displayName"); + throw std::runtime_error("Error creating the string displayName"); napi_set_named_property(env, jsObj, "displayName", napiDisplayName); - // Propiedad "version" std::string version = WStringToUTF8(roots[i].version); napi_value napiVersion; status = napi_create_string_utf8(env, version.c_str(), version.size(), &napiVersion); if (status != napi_ok) - throw std::runtime_error("Error creando la cadena version"); + throw std::runtime_error("Error creating the string version"); napi_set_named_property(env, jsObj, "version", napiVersion); - // Insertamos el objeto en el array status = napi_set_element(env, jsArray, i, jsObj); if (status != napi_ok) - throw std::runtime_error("Error insertando el elemento en el array"); + throw std::runtime_error("Error setting the element in the array"); } return jsArray; From 59939caae5801db9446322c5f48b1218f9ea2092 Mon Sep 17 00:00:00 2001 From: ArceDanielShok Date: Tue, 1 Apr 2025 09:09:53 -0300 Subject: [PATCH 3/5] Update SyncRoot identity format and enhance filtering for registered sync roots --- native-src/sync_root_interface/SyncRoot.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/native-src/sync_root_interface/SyncRoot.cpp b/native-src/sync_root_interface/SyncRoot.cpp index 11fa53ad..29d06a8a 100644 --- a/native-src/sync_root_interface/SyncRoot.cpp +++ b/native-src/sync_root_interface/SyncRoot.cpp @@ -146,7 +146,7 @@ HRESULT SyncRoot::RegisterSyncRoot(const wchar_t *syncRootPath, const wchar_t *p // Context std::wstring syncRootIdentity(syncRootPath); - syncRootIdentity.append(L"->"); + syncRootIdentity.append(L"#intx#"); syncRootIdentity.append(providerName); winrt::IBuffer contextBuffer = winrt::CryptographicBuffer::ConvertStringToBinary(syncRootIdentity.data(), winrt::BinaryStringEncoding::Utf8); @@ -191,7 +191,13 @@ std::vector SyncRoot::GetRegisteredSyncRoots() .c_str(); } - if (contextString.find(L"->") != std::wstring::npos) + /** + * v2.5.1 Jonathan Arce + * Sync root records are now filtered using the characters '->' and '#inxt#' to identify our records. + * Currently, we only use '#inxt#' in the records, but to support previous versions, we are still + * including '->' in the filter. In future versions, the filtering by '->' should be removed. + */ + if (contextString.find(L"#inxt#") != std::wstring::npos || contextString.find(L"->") != std::wstring::npos) { SyncRoots sr; sr.id = info.Id(); From 50d1830f2824e6a026049923746dfcc2f86131aa Mon Sep 17 00:00:00 2001 From: ArceDanielShok Date: Tue, 1 Apr 2025 09:11:20 -0300 Subject: [PATCH 4/5] Update filtering logic in GetRegisteredSyncRoots to clarify sync root identification --- native-src/sync_root_interface/SyncRoot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native-src/sync_root_interface/SyncRoot.cpp b/native-src/sync_root_interface/SyncRoot.cpp index 29d06a8a..2ed55c83 100644 --- a/native-src/sync_root_interface/SyncRoot.cpp +++ b/native-src/sync_root_interface/SyncRoot.cpp @@ -193,8 +193,8 @@ std::vector SyncRoot::GetRegisteredSyncRoots() /** * v2.5.1 Jonathan Arce - * Sync root records are now filtered using the characters '->' and '#inxt#' to identify our records. - * Currently, we only use '#inxt#' in the records, but to support previous versions, we are still + * Sync root register are now filtered using the characters '->' and '#inxt#' to identify our register. + * Currently, we only use '#inxt#' in the register, but to support previous versions, we are still * including '->' in the filter. In future versions, the filtering by '->' should be removed. */ if (contextString.find(L"#inxt#") != std::wstring::npos || contextString.find(L"->") != std::wstring::npos) From 5644d22f045f90765deb67e89fd9f492cee8808c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Tue, 1 Apr 2025 15:12:03 +0200 Subject: [PATCH 5/5] Update virtual-drive.ts --- src/virtual-drive.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/virtual-drive.ts b/src/virtual-drive.ts index 933d434a..c16a91c3 100644 --- a/src/virtual-drive.ts +++ b/src/virtual-drive.ts @@ -160,8 +160,8 @@ class VirtualDrive { }); } - static async getRegisteredSyncRoots() { - return await DependencyInjectionAddonProvider.get().getRegisteredSyncRoots(); + static getRegisteredSyncRoots() { + return DependencyInjectionAddonProvider.get().getRegisteredSyncRoots(); } unregisterSyncRoot() {