|
|
@@ -33,6 +33,8 @@
|
|
|
#include <comdef.h>
|
|
|
#include <ntstatus.h>
|
|
|
|
|
|
+#include "config.h"
|
|
|
+
|
|
|
Q_LOGGING_CATEGORY(lcCfApiWrapper, "nextcloud.sync.vfs.cfapi.wrapper", QtInfoMsg)
|
|
|
|
|
|
#define FIELD_SIZE( type, field ) ( sizeof( ( (type*)0 )->field ) )
|
|
|
@@ -44,6 +46,8 @@ namespace {
|
|
|
constexpr auto syncRootFlagsFull = 34;
|
|
|
constexpr auto syncRootFlagsNoCfApiContextMenu = 2;
|
|
|
|
|
|
+constexpr auto syncRootManagerRegKey = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager)";
|
|
|
+
|
|
|
void cfApiSendTransferInfo(const CF_CONNECTION_KEY &connectionKey, const CF_TRANSFER_KEY &transferKey, NTSTATUS status, void *buffer, qint64 offset, qint64 currentBlockLength, qint64 totalLength)
|
|
|
{
|
|
|
|
|
|
@@ -407,7 +411,7 @@ QString retrieveWindowsSid()
|
|
|
return {};
|
|
|
}
|
|
|
|
|
|
-bool createSyncRootRegistryKeys(const QString &providerName, const QString &folderAlias, const QString &displayName, const QString &accountDisplayName, const QString &syncRootPath)
|
|
|
+bool createSyncRootRegistryKeys(const QString &providerName, const QString &folderAlias, const QString &navigationPaneClsid, const QString &displayName, const QString &accountDisplayName, const QString &syncRootPath)
|
|
|
{
|
|
|
// We must set specific Registry keys to make the progress bar refresh correctly and also add status icons into Windows Explorer
|
|
|
// More about this here: https://docs.microsoft.com/en-us/windows/win32/shell/integrate-cloud-storage
|
|
|
@@ -422,7 +426,7 @@ bool createSyncRootRegistryKeys(const QString &providerName, const QString &fold
|
|
|
// folder registry keys go like: Nextcloud!S-1-5-21-2096452760-2617351404-2281157308-1001!user@nextcloud.lan:8080!0, Nextcloud!S-1-5-21-2096452760-2617351404-2281157308-1001!user@nextcloud.lan:8080!1, etc. for each sync folder
|
|
|
const auto syncRootId = QString("%1!%2!%3!%4").arg(providerName).arg(windowsSid).arg(accountDisplayName).arg(folderAlias);
|
|
|
|
|
|
- const QString providerSyncRootIdRegistryKey = QStringLiteral(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager\)") + syncRootId;
|
|
|
+ const QString providerSyncRootIdRegistryKey = syncRootManagerRegKey + QStringLiteral("\\") + syncRootId;
|
|
|
const QString providerSyncRootIdUserSyncRootsRegistryKey = providerSyncRootIdRegistryKey + QStringLiteral(R"(\UserSyncRoots\)");
|
|
|
|
|
|
struct RegistryKeyInfo {
|
|
|
@@ -438,7 +442,9 @@ bool createSyncRootRegistryKeys(const QString &providerName, const QString &fold
|
|
|
{ providerSyncRootIdRegistryKey, QStringLiteral("Flags"), REG_DWORD, flags },
|
|
|
{ providerSyncRootIdRegistryKey, QStringLiteral("DisplayNameResource"), REG_EXPAND_SZ, displayName },
|
|
|
{ providerSyncRootIdRegistryKey, QStringLiteral("IconResource"), REG_EXPAND_SZ, QString(QDir::toNativeSeparators(qApp->applicationFilePath()) + QStringLiteral(",0")) },
|
|
|
- { providerSyncRootIdUserSyncRootsRegistryKey, windowsSid, REG_SZ, syncRootPath }
|
|
|
+ { providerSyncRootIdUserSyncRootsRegistryKey, windowsSid, REG_SZ, syncRootPath},
|
|
|
+ { providerSyncRootIdRegistryKey, QStringLiteral("ThumbnailProvider"), REG_SZ, CFAPI_SHELLEXT_THUMBNAIL_HANDLER_CLASS_ID_REG},
|
|
|
+ { providerSyncRootIdRegistryKey, QStringLiteral("NamespaceCLSID"), REG_SZ, QString(navigationPaneClsid)}
|
|
|
};
|
|
|
|
|
|
for (const auto ®istryKeyToSet : qAsConst(registryKeysToSet)) {
|
|
|
@@ -457,9 +463,7 @@ bool createSyncRootRegistryKeys(const QString &providerName, const QString &fold
|
|
|
|
|
|
bool deleteSyncRootRegistryKey(const QString &syncRootPath, const QString &providerName, const QString &accountDisplayName)
|
|
|
{
|
|
|
- const auto syncRootManagerRegistryKey = QStringLiteral(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager\)");
|
|
|
-
|
|
|
- if (OCC::Utility::registryKeyExists(HKEY_LOCAL_MACHINE, syncRootManagerRegistryKey)) {
|
|
|
+ if (OCC::Utility::registryKeyExists(HKEY_LOCAL_MACHINE, syncRootManagerRegKey)) {
|
|
|
const auto windowsSid = retrieveWindowsSid();
|
|
|
Q_ASSERT(!windowsSid.isEmpty());
|
|
|
if (windowsSid.isEmpty()) {
|
|
|
@@ -472,13 +476,13 @@ bool deleteSyncRootRegistryKey(const QString &syncRootPath, const QString &provi
|
|
|
bool result = true;
|
|
|
|
|
|
// walk through each registered syncRootId
|
|
|
- OCC::Utility::registryWalkSubKeys(HKEY_LOCAL_MACHINE, syncRootManagerRegistryKey, [&](HKEY, const QString &syncRootId) {
|
|
|
+ OCC::Utility::registryWalkSubKeys(HKEY_LOCAL_MACHINE, syncRootManagerRegKey, [&](HKEY, const QString &syncRootId) {
|
|
|
// make sure we have matching syncRootId(providerName!windowsSid!accountDisplayName)
|
|
|
if (syncRootId.startsWith(currentUserSyncRootIdPattern)) {
|
|
|
- const QString syncRootIdUserSyncRootsRegistryKey = syncRootManagerRegistryKey + syncRootId + QStringLiteral(R"(\UserSyncRoots\)");
|
|
|
+ const QString syncRootIdUserSyncRootsRegistryKey = syncRootManagerRegKey + QStringLiteral("\\") + syncRootId + QStringLiteral(R"(\UserSyncRoots\)");
|
|
|
// check if there is a 'windowsSid' Registry value under \UserSyncRoots and it matches the sync folder path we are removing
|
|
|
if (OCC::Utility::registryGetKeyValue(HKEY_LOCAL_MACHINE, syncRootIdUserSyncRootsRegistryKey, windowsSid).toString() == syncRootPath) {
|
|
|
- const QString syncRootIdToDelete = syncRootManagerRegistryKey + syncRootId;
|
|
|
+ const QString syncRootIdToDelete = syncRootManagerRegKey + QStringLiteral("\\") + syncRootId;
|
|
|
result = OCC::Utility::registryDeleteKeyTree(HKEY_LOCAL_MACHINE, syncRootIdToDelete);
|
|
|
}
|
|
|
}
|
|
|
@@ -488,10 +492,10 @@ bool deleteSyncRootRegistryKey(const QString &syncRootPath, const QString &provi
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-OCC::Result<void, QString> OCC::CfApiWrapper::registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion, const QString &folderAlias, const QString &displayName, const QString &accountDisplayName)
|
|
|
+OCC::Result<void, QString> OCC::CfApiWrapper::registerSyncRoot(const QString &path, const QString &providerName, const QString &providerVersion, const QString &folderAlias, const QString &navigationPaneClsid, const QString &displayName, const QString &accountDisplayName)
|
|
|
{
|
|
|
// even if we fail to register our sync root with shell, we can still proceed with using the VFS
|
|
|
- const auto createRegistryKeyResult = createSyncRootRegistryKeys(providerName, folderAlias, displayName, accountDisplayName, path);
|
|
|
+ const auto createRegistryKeyResult = createSyncRootRegistryKeys(providerName, folderAlias, navigationPaneClsid, displayName, accountDisplayName, path);
|
|
|
Q_ASSERT(createRegistryKeyResult);
|
|
|
|
|
|
if (!createRegistryKeyResult) {
|
|
|
@@ -532,6 +536,24 @@ OCC::Result<void, QString> OCC::CfApiWrapper::registerSyncRoot(const QString &pa
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void unregisterSyncRootShellExtensions(const QString &providerName, const QString &folderAlias, const QString &accountDisplayName)
|
|
|
+{
|
|
|
+ const auto windowsSid = retrieveWindowsSid();
|
|
|
+ Q_ASSERT(!windowsSid.isEmpty());
|
|
|
+ if (windowsSid.isEmpty()) {
|
|
|
+ qCWarning(lcCfApiWrapper) << "Failed to unregister SyncRoot Shell Extensions!";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto syncRootId = QString("%1!%2!%3!%4").arg(providerName).arg(windowsSid).arg(accountDisplayName).arg(folderAlias);
|
|
|
+
|
|
|
+ const QString providerSyncRootIdRegistryKey = syncRootManagerRegKey + QStringLiteral("\\") + syncRootId;
|
|
|
+
|
|
|
+ OCC::Utility::registryDeleteKeyValue(HKEY_LOCAL_MACHINE, providerSyncRootIdRegistryKey, QStringLiteral("ThumbnailProvider"));
|
|
|
+
|
|
|
+ qCInfo(lcCfApiWrapper) << "Successfully unregistered SyncRoot Shell Extensions!";
|
|
|
+}
|
|
|
+
|
|
|
OCC::Result<void, QString> OCC::CfApiWrapper::unregisterSyncRoot(const QString &path, const QString &providerName, const QString &accountDisplayName)
|
|
|
{
|
|
|
const auto deleteRegistryKeyResult = deleteSyncRootRegistryKey(path, providerName, accountDisplayName);
|
|
|
@@ -579,6 +601,31 @@ OCC::Result<void, QString> OCC::CfApiWrapper::disconnectSyncRoot(ConnectionKey &
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+bool OCC::CfApiWrapper::isAnySyncRoot(const QString &providerName, const QString &accountDisplayName)
|
|
|
+{
|
|
|
+ const auto windowsSid = retrieveWindowsSid();
|
|
|
+ Q_ASSERT(!windowsSid.isEmpty());
|
|
|
+ if (windowsSid.isEmpty()) {
|
|
|
+ qCWarning(lcCfApiWrapper) << "Could not retrieve Windows Sid.";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto syncRootPrefix = QString("%1!%2!%3!").arg(providerName).arg(windowsSid).arg(accountDisplayName);
|
|
|
+
|
|
|
+ if (Utility::registryKeyExists(HKEY_LOCAL_MACHINE, syncRootManagerRegKey)) {
|
|
|
+ bool foundSyncRoots = false;
|
|
|
+ Utility::registryWalkSubKeys(HKEY_LOCAL_MACHINE, syncRootManagerRegKey,
|
|
|
+ [&foundSyncRoots, &syncRootPrefix](HKEY key, const QString &subKey) {
|
|
|
+ if (subKey.startsWith(syncRootPrefix)) {
|
|
|
+ foundSyncRoots = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return foundSyncRoots;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
bool OCC::CfApiWrapper::isSparseFile(const QString &path)
|
|
|
{
|
|
|
const auto p = path.toStdWString();
|