mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
Do not upload modified PDF file while it is open in Kofax PowerPDF on Windows. Prevents signature verification failure.
Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
parent
39611b9728
commit
b2aca219fc
@ -50,6 +50,18 @@ Q_DECLARE_LOGGING_CATEGORY(lcUtility)
|
||||
* @{
|
||||
*/
|
||||
namespace Utility {
|
||||
struct ProcessInfosForOpenFile {
|
||||
ulong processId;
|
||||
QString processName;
|
||||
};
|
||||
/**
|
||||
* @brief Queries the OS for processes that are keeping the file open(using it)
|
||||
*
|
||||
* @param filePath absolute file path
|
||||
* @return list of ProcessInfosForOpenFile
|
||||
*/
|
||||
OCSYNC_EXPORT QVector<ProcessInfosForOpenFile> queryProcessInfosKeepingFileOpen(const QString &filePath);
|
||||
|
||||
OCSYNC_EXPORT int rand();
|
||||
OCSYNC_EXPORT void sleep(int sec);
|
||||
OCSYNC_EXPORT void usleep(int usec);
|
||||
|
||||
@ -31,6 +31,12 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QVector<Utility::ProcessInfosForOpenFile> Utility::queryProcessInfosKeepingFileOpen(const QString &filePath)
|
||||
{
|
||||
Q_UNUSED(filePath)
|
||||
return {};
|
||||
}
|
||||
|
||||
void Utility::setupFavLink(const QString &folder)
|
||||
{
|
||||
// Finder: Place under "Places"/"Favorites" on the left sidebar
|
||||
|
||||
@ -31,6 +31,12 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QVector<Utility::ProcessInfosForOpenFile> Utility::queryProcessInfosKeepingFileOpen(const QString &filePath)
|
||||
{
|
||||
Q_UNUSED(filePath)
|
||||
return {};
|
||||
}
|
||||
|
||||
void Utility::setupFavLink(const QString &folder)
|
||||
{
|
||||
// Nautilus: add to ~/.gtk-bookmarks
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
|
||||
#include <comdef.h>
|
||||
#include <Lmcons.h>
|
||||
#include <psapi.h>
|
||||
#include <RestartManager.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#include <string>
|
||||
@ -43,6 +45,72 @@ static const char runPathC[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\C
|
||||
|
||||
namespace OCC {
|
||||
|
||||
QVector<Utility::ProcessInfosForOpenFile> Utility::queryProcessInfosKeepingFileOpen(const QString &filePath)
|
||||
{
|
||||
QVector<ProcessInfosForOpenFile> results;
|
||||
|
||||
DWORD restartManagerSession = 0;
|
||||
WCHAR restartManagerSessionKey[CCH_RM_SESSION_KEY + 1] = {0};
|
||||
auto errorStatus = RmStartSession(&restartManagerSession, 0, restartManagerSessionKey);
|
||||
if (errorStatus != ERROR_SUCCESS) {
|
||||
return results;
|
||||
}
|
||||
|
||||
LPCWSTR files[] = {reinterpret_cast<LPCWSTR>(filePath.utf16())};
|
||||
errorStatus = RmRegisterResources(restartManagerSession, 1, files, 0, NULL, 0, NULL);
|
||||
if (errorStatus != ERROR_SUCCESS) {
|
||||
RmEndSession(restartManagerSession);
|
||||
return results;
|
||||
}
|
||||
|
||||
DWORD rebootReasons = 0;
|
||||
UINT rmProcessInfosNeededCount = 0;
|
||||
std::vector<RM_PROCESS_INFO> rmProcessInfos;
|
||||
auto rmProcessInfosRequestedCount = static_cast<UINT>(rmProcessInfos.size());
|
||||
errorStatus = RmGetList(restartManagerSession, &rmProcessInfosNeededCount, &rmProcessInfosRequestedCount, rmProcessInfos.data(), &rebootReasons);
|
||||
|
||||
if (errorStatus == ERROR_MORE_DATA) {
|
||||
rmProcessInfos.resize(rmProcessInfosNeededCount, {});
|
||||
rmProcessInfosRequestedCount = static_cast<UINT>(rmProcessInfos.size());
|
||||
errorStatus = RmGetList(restartManagerSession, &rmProcessInfosNeededCount, &rmProcessInfosRequestedCount, rmProcessInfos.data(), &rebootReasons);
|
||||
}
|
||||
|
||||
if (errorStatus != ERROR_SUCCESS || rmProcessInfos.empty()) {
|
||||
RmEndSession(restartManagerSession);
|
||||
return results;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rmProcessInfos.size(); ++i) {
|
||||
const auto processHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, rmProcessInfos[i].Process.dwProcessId);
|
||||
if (!processHandle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FILETIME ftCreate, ftExit, ftKernel, ftUser;
|
||||
|
||||
if (!GetProcessTimes(processHandle, &ftCreate, &ftExit, &ftKernel, &ftUser)
|
||||
|| CompareFileTime(&rmProcessInfos[i].Process.ProcessStartTime, &ftCreate) != 0) {
|
||||
CloseHandle(processHandle);
|
||||
continue;
|
||||
}
|
||||
|
||||
WCHAR processFullPath[MAX_PATH];
|
||||
DWORD processFullPathLength = MAX_PATH;
|
||||
if (QueryFullProcessImageNameW(processHandle, 0, processFullPath, &processFullPathLength) && processFullPathLength <= MAX_PATH) {
|
||||
const auto processFullPathString = QDir::fromNativeSeparators(QString::fromWCharArray(processFullPath));
|
||||
const QFileInfo fileInfoForProcess(processFullPathString);
|
||||
const auto processName = fileInfoForProcess.fileName();
|
||||
if (!processName.isEmpty()) {
|
||||
results.push_back(Utility::ProcessInfosForOpenFile{rmProcessInfos[i].Process.dwProcessId, processName});
|
||||
}
|
||||
}
|
||||
CloseHandle(processHandle);
|
||||
}
|
||||
RmEndSession(restartManagerSession);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void Utility::setupFavLink(const QString &folder)
|
||||
{
|
||||
// First create a Desktop.ini so that the folder and favorite link show our application's icon.
|
||||
|
||||
@ -31,7 +31,7 @@ if (NOT LINUX)
|
||||
endif (NOT LINUX)
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} psapi kernel32)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} psapi kernel32 Rstrtmgr)
|
||||
endif()
|
||||
|
||||
check_function_exists(utimes HAVE_UTIMES)
|
||||
|
||||
@ -34,6 +34,12 @@
|
||||
#include "csync_exclude.h"
|
||||
#include "csync.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char *editorNamesForDelayedUpload[] = {"PowerPDF"};
|
||||
constexpr const char *fileExtensionsToCheckIfOpenForSigning[] = {".pdf"};
|
||||
constexpr auto delayIntervalForSyncRetryForOpenedForSigningFilesSeconds = 60;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@ -1022,6 +1028,19 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
|
||||
item->_status = SyncFileItem::Status::NormalError;
|
||||
}
|
||||
|
||||
{
|
||||
const auto foundEditorsKeepingFileBusy = queryEditorsKeepingFileBusy(item, path);
|
||||
if (!foundEditorsKeepingFileBusy.isEmpty()) {
|
||||
item->_instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
const auto editorsString = foundEditorsKeepingFileBusy.join(", ");
|
||||
qCInfo(lcDisco) << "Failed, because it is open in the editor." << item->_file << "direction" << item->_direction << editorsString;
|
||||
item->_errorString = tr("Could not upload file, because it is open in \"%1\".").arg(editorsString);
|
||||
item->_status = SyncFileItem::Status::SoftError;
|
||||
_discoveryData->_anotherSyncNeeded = true;
|
||||
_discoveryData->_filesNeedingScheduledSync.insert(path._original, delayIntervalForSyncRetryForOpenedForSigningFilesSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
if (dbEntry.isValid() && item->isDirectory()) {
|
||||
item->_e2eEncryptionStatus = EncryptionStatusEnums::fromDbEncryptionStatus(dbEntry._e2eEncryptionStatus);
|
||||
if (item->isEncrypted()) {
|
||||
@ -1839,6 +1858,43 @@ bool ProcessDirectoryJob::isRename(const QString &originalPath) const
|
||||
*/
|
||||
}
|
||||
|
||||
QStringList ProcessDirectoryJob::queryEditorsKeepingFileBusy(const SyncFileItemPtr &item, const PathTuple &path) const
|
||||
{
|
||||
QStringList matchingEditorsKeepingFileBusy;
|
||||
|
||||
if (item->isDirectory() || item->_direction != SyncFileItem::Up) {
|
||||
return matchingEditorsKeepingFileBusy;
|
||||
}
|
||||
|
||||
const auto isMatchingFileExtension = std::find_if(std::cbegin(fileExtensionsToCheckIfOpenForSigning), std::cend(fileExtensionsToCheckIfOpenForSigning),
|
||||
[path](const auto &matchingExtension) {
|
||||
return path._local.endsWith(matchingExtension, Qt::CaseInsensitive);
|
||||
}) != std::cend(fileExtensionsToCheckIfOpenForSigning);
|
||||
|
||||
if (!isMatchingFileExtension) {
|
||||
return matchingEditorsKeepingFileBusy;
|
||||
}
|
||||
|
||||
const QString fullLocalPath(_discoveryData->_localDir + path._local);
|
||||
const auto editorsKeepingFileBusy = Utility::queryProcessInfosKeepingFileOpen(fullLocalPath);
|
||||
|
||||
for (const auto &detectedEditorName : editorsKeepingFileBusy) {
|
||||
const auto isMatchingEditorFound = std::find_if(std::cbegin(editorNamesForDelayedUpload), std::cend(editorNamesForDelayedUpload),
|
||||
[detectedEditorName](const auto &matchingEditorName) {
|
||||
return detectedEditorName.processName.startsWith(matchingEditorName, Qt::CaseInsensitive);
|
||||
}) != std::cend(editorNamesForDelayedUpload);
|
||||
if (isMatchingEditorFound) {
|
||||
matchingEditorsKeepingFileBusy.push_back(detectedEditorName.processName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchingEditorsKeepingFileBusy.isEmpty()) {
|
||||
matchingEditorsKeepingFileBusy.push_back("PowerPDF.exe");
|
||||
}
|
||||
|
||||
return matchingEditorsKeepingFileBusy;
|
||||
}
|
||||
|
||||
auto ProcessDirectoryJob::checkMovePermissions(RemotePermissions srcPerm, const QString &srcPath,
|
||||
bool isDirectory)
|
||||
-> MovePermissionResult
|
||||
|
||||
@ -191,6 +191,8 @@ private:
|
||||
|
||||
[[nodiscard]] bool isRename(const QString &originalPath) const;
|
||||
|
||||
[[nodiscard]] QStringList queryEditorsKeepingFileBusy(const SyncFileItemPtr &item, const PathTuple &path) const;
|
||||
|
||||
struct MovePermissionResult
|
||||
{
|
||||
// whether moving/renaming the source is ok
|
||||
|
||||
Loading…
Reference in New Issue
Block a user