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..2ed55c83 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 @@ -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); @@ -170,6 +170,51 @@ 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(); + } + + /** + * v2.5.1 Jonathan Arce + * 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) + { + 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..1c4906f7 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,71 @@ 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 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 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 creating the string id"); + napi_set_named_property(env, jsObj, "id", napiId); + + 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 creating the string path"); + napi_set_named_property(env, jsObj, "path", napiPath); + + 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 creating the string displayName"); + napi_set_named_property(env, jsObj, "displayName", napiDisplayName); + + 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 creating the string version"); + napi_set_named_property(env, jsObj, "version", napiVersion); + + status = napi_set_element(env, jsArray, i, jsObj); + if (status != napi_ok) + throw std::runtime_error("Error setting the element in the 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..c16a91c3 100644 --- a/src/virtual-drive.ts +++ b/src/virtual-drive.ts @@ -160,6 +160,10 @@ class VirtualDrive { }); } + static getRegisteredSyncRoots() { + return DependencyInjectionAddonProvider.get().getRegisteredSyncRoots(); + } + unregisterSyncRoot() { return this.addon.unregisterSyncRoot({ providerId: this.providerId }); }