2017-10-04 14:49:42 +03:00
|
|
|
/*
|
|
|
|
* Copyright (C) by Jocelyn Turcotte <jturcotte@woboq.com>
|
|
|
|
*
|
|
|
|
* 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 2 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "navigationpanehelper.h"
|
|
|
|
#include "accountmanager.h"
|
2017-10-19 17:37:51 +03:00
|
|
|
#include "configfile.h"
|
2017-10-04 14:49:42 +03:00
|
|
|
#include "folderman.h"
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
|
|
namespace OCC {
|
|
|
|
|
2017-12-28 22:33:10 +03:00
|
|
|
Q_LOGGING_CATEGORY(lcNavPane, "nextcloud.gui.folder.navigationpane", QtInfoMsg)
|
2017-10-04 14:49:42 +03:00
|
|
|
|
|
|
|
NavigationPaneHelper::NavigationPaneHelper(FolderMan *folderMan)
|
|
|
|
: _folderMan(folderMan)
|
|
|
|
{
|
2017-10-19 17:37:51 +03:00
|
|
|
ConfigFile cfg;
|
|
|
|
_showInExplorerNavigationPane = cfg.showInExplorerNavigationPane();
|
|
|
|
|
2017-10-04 14:49:42 +03:00
|
|
|
_updateCloudStorageRegistryTimer.setSingleShot(true);
|
|
|
|
connect(&_updateCloudStorageRegistryTimer, &QTimer::timeout, this, &NavigationPaneHelper::updateCloudStorageRegistry);
|
2020-01-13 10:58:02 +03:00
|
|
|
|
|
|
|
// Ensure that the folder integration stays persistent in Explorer,
|
|
|
|
// the uninstaller removes the folder upon updating the client.
|
|
|
|
_showInExplorerNavigationPane = !_showInExplorerNavigationPane;
|
|
|
|
setShowInExplorerNavigationPane(!_showInExplorerNavigationPane);
|
2017-10-04 14:49:42 +03:00
|
|
|
}
|
|
|
|
|
2017-10-19 17:37:51 +03:00
|
|
|
void NavigationPaneHelper::setShowInExplorerNavigationPane(bool show)
|
|
|
|
{
|
|
|
|
if (_showInExplorerNavigationPane == show)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_showInExplorerNavigationPane = show;
|
|
|
|
// Re-generate a new CLSID when enabling, possibly throwing away the old one.
|
|
|
|
// updateCloudStorageRegistry will take care of removing any unknown CLSID our application owns from the registry.
|
|
|
|
foreach (Folder *folder, _folderMan->map())
|
|
|
|
folder->setNavigationPaneClsid(show ? QUuid::createUuid() : QUuid());
|
|
|
|
|
|
|
|
scheduleUpdateCloudStorageRegistry();
|
|
|
|
}
|
|
|
|
|
2017-10-04 14:49:42 +03:00
|
|
|
void NavigationPaneHelper::scheduleUpdateCloudStorageRegistry()
|
|
|
|
{
|
|
|
|
// Schedule the update to happen a bit later to avoid doing the update multiple times in a row.
|
|
|
|
if (!_updateCloudStorageRegistryTimer.isActive())
|
|
|
|
_updateCloudStorageRegistryTimer.start(500);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NavigationPaneHelper::updateCloudStorageRegistry()
|
|
|
|
{
|
|
|
|
// Start by looking at every registered namespace extension for the sidebar, and look for an "ApplicationName" value
|
|
|
|
// that matches ours when we saved.
|
|
|
|
QVector<QUuid> entriesToRemove;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
Utility::registryWalkSubKeys(
|
|
|
|
HKEY_CURRENT_USER,
|
|
|
|
QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace"),
|
|
|
|
[&entriesToRemove](HKEY key, const QString &subKey) {
|
|
|
|
QVariant appName = Utility::registryGetKeyValue(key, subKey, QStringLiteral("ApplicationName"));
|
|
|
|
if (appName.toString() == QLatin1String(APPLICATION_NAME)) {
|
|
|
|
QUuid clsid{ subKey };
|
|
|
|
Q_ASSERT(!clsid.isNull());
|
|
|
|
entriesToRemove.append(clsid);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Then re-save every folder that has a valid navigationPaneClsid to the registry.
|
|
|
|
// We currently don't distinguish between new and existing CLSIDs, if it's there we just
|
|
|
|
// save over it. We at least need to update the tile in case we are suddently using multiple accounts.
|
|
|
|
foreach (Folder *folder, _folderMan->map()) {
|
|
|
|
if (!folder->navigationPaneClsid().isNull()) {
|
|
|
|
// If it already exists, unmark it for removal, this is a valid sync root.
|
|
|
|
entriesToRemove.removeOne(folder->navigationPaneClsid());
|
|
|
|
|
|
|
|
QString clsidStr = folder->navigationPaneClsid().toString();
|
|
|
|
QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
|
2018-04-19 12:35:03 +03:00
|
|
|
QString clsidPathWow64 = QString() % "Software\\Classes\\Wow6432Node\\CLSID\\" % clsidStr;
|
2017-10-04 14:49:42 +03:00
|
|
|
QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
|
|
|
|
|
|
|
|
QString title = folder->shortGuiRemotePathOrAppName();
|
|
|
|
// Write the account name in the sidebar only when using more than one account.
|
|
|
|
if (AccountManager::instance()->accounts().size() > 1)
|
|
|
|
title = title % " - " % folder->accountState()->account()->displayName();
|
|
|
|
QString iconPath = QDir::toNativeSeparators(qApp->applicationFilePath());
|
|
|
|
QString targetFolderPath = QDir::toNativeSeparators(folder->cleanPath());
|
|
|
|
|
|
|
|
qCInfo(lcNavPane) << "Explorer Cloud storage provider: saving path" << targetFolderPath << "to CLSID" << clsidStr;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
// Steps taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/dn889934%28v=vs.85%29.aspx
|
|
|
|
// Step 1: Add your CLSID and name your extension
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QString(), REG_SZ, title);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QString(), REG_SZ, title);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 2: Set the image for your icon
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\DefaultIcon"), QString(), REG_SZ, iconPath);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 3: Add your extension to the Navigation Pane and make it visible
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("System.IsPinnedToNameSpaceTree"), REG_DWORD, 0x1);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 4: Set the location for your extension in the Navigation Pane
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64, QStringLiteral("SortOrderIndex"), REG_DWORD, 0x41);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 5: Provide the dll that hosts your extension.
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\InProcServer32"), QString(), REG_EXPAND_SZ, QStringLiteral("%systemroot%\\system32\\shell32.dll"));
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 6: Define the instance object
|
|
|
|
// Indicate that your namespace extension should function like other file folder structures in File Explorer.
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance"), QStringLiteral("CLSID"), REG_SZ, QStringLiteral("{0E5AAE11-A475-4c5b-AB00-C66DE400274E}"));
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 7: Provide the file system attributes of the target folder
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("Attributes"), REG_DWORD, 0x11);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 8: Set the path for the sync root
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\Instance\\InitPropertyBag"), QStringLiteral("TargetFolderPath"), REG_SZ, targetFolderPath);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 9: Set appropriate shell flags
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("FolderValueFlags"), REG_DWORD, 0x28);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 10: Set the appropriate flags to control your shell behavior
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPath + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, clsidPathWow64 + QStringLiteral("\\ShellFolder"), QStringLiteral("Attributes"), REG_DWORD, 0xF080004D);
|
2017-10-04 14:49:42 +03:00
|
|
|
// Step 11: Register your extension in the namespace root
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QString(), REG_SZ, title);
|
|
|
|
// Step 12: Hide your extension from the Desktop
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr, REG_DWORD, 0x1);
|
|
|
|
|
|
|
|
// For us, to later be able to iterate and find our own namespace entries and associated CLSID.
|
|
|
|
// Use the macro instead of the theme to make sure it matches with the uninstaller.
|
|
|
|
Utility::registrySetKeyValue(HKEY_CURRENT_USER, namespacePath, QStringLiteral("ApplicationName"), REG_SZ, QLatin1String(APPLICATION_NAME));
|
|
|
|
#else
|
|
|
|
// This code path should only occur on Windows (the config will be false, and the checkbox invisible on other platforms).
|
|
|
|
// Add runtime checks rather than #ifdefing out the whole code to help catch breakages when developing on other platforms.
|
2020-01-13 10:58:02 +03:00
|
|
|
|
|
|
|
// Don't crash, by any means!
|
|
|
|
// Q_ASSERT(false);
|
2017-10-04 14:49:42 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then remove anything that isn't in our folder list anymore.
|
|
|
|
foreach (auto &clsid, entriesToRemove) {
|
|
|
|
QString clsidStr = clsid.toString();
|
|
|
|
QString clsidPath = QString() % "Software\\Classes\\CLSID\\" % clsidStr;
|
2018-04-19 12:35:03 +03:00
|
|
|
QString clsidPathWow64 = QString() % "Software\\Classes\\Wow6432Node\\CLSID\\" % clsidStr;
|
2017-10-04 14:49:42 +03:00
|
|
|
QString namespacePath = QString() % "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\NameSpace\\" % clsidStr;
|
|
|
|
|
|
|
|
qCInfo(lcNavPane) << "Explorer Cloud storage provider: now unused, removing own CLSID" << clsidStr;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
Utility::registryDeleteKeyTree(HKEY_CURRENT_USER, clsidPath);
|
2018-04-19 12:35:03 +03:00
|
|
|
Utility::registryDeleteKeyTree(HKEY_CURRENT_USER, clsidPathWow64);
|
2017-10-04 14:49:42 +03:00
|
|
|
Utility::registryDeleteKeyTree(HKEY_CURRENT_USER, namespacePath);
|
|
|
|
Utility::registryDeleteKeyValue(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel"), clsidStr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace OCC
|