2014-07-30 19:20:55 +04:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2014 ownCloud, Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU Lesser General Public License as published by the Free
|
|
|
|
* Software Foundation; version 2.1 of the License
|
|
|
|
*
|
|
|
|
* This library 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 Lesser General Public License for more
|
|
|
|
* details.
|
|
|
|
*/
|
|
|
|
|
2015-02-04 21:54:34 +03:00
|
|
|
#include "stdafx.h"
|
|
|
|
|
2014-07-30 19:20:55 +04:00
|
|
|
#include "CommunicationSocket.h"
|
|
|
|
|
|
|
|
#include "RemotePathChecker.h"
|
2014-08-05 21:23:40 +04:00
|
|
|
#include "StringUtil.h"
|
2014-07-30 19:20:55 +04:00
|
|
|
|
2014-11-03 22:45:38 +03:00
|
|
|
#include <shlobj.h>
|
|
|
|
|
2014-07-30 19:20:55 +04:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iterator>
|
2014-10-14 18:05:48 +04:00
|
|
|
#include <unordered_set>
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
#include <shlobj.h>
|
2014-07-30 19:20:55 +04:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
|
|
|
|
// This code is run in a thread
|
|
|
|
void RemotePathChecker::workerThreadLoop()
|
2014-07-30 19:20:55 +04:00
|
|
|
{
|
2014-10-15 17:13:04 +04:00
|
|
|
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
|
|
|
pipename += L"ownCloud";
|
|
|
|
|
|
|
|
bool connected = false;
|
2014-10-14 18:05:48 +04:00
|
|
|
CommunicationSocket socket;
|
|
|
|
std::unordered_set<std::wstring> asked;
|
|
|
|
|
|
|
|
while(!_stop) {
|
2014-10-15 17:57:15 +04:00
|
|
|
Sleep(50);
|
2014-10-15 17:13:04 +04:00
|
|
|
|
|
|
|
if (!connected) {
|
|
|
|
asked.clear();
|
2015-01-22 18:30:02 +03:00
|
|
|
if (!WaitNamedPipe(pipename.data(), 100)) {
|
2014-10-15 17:13:04 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!socket.Connect(pipename)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
connected = true;
|
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
_connected = true;
|
|
|
|
}
|
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
while (!_pending.empty() && !_stop) {
|
|
|
|
auto filePath = _pending.front();
|
|
|
|
_pending.pop();
|
|
|
|
|
|
|
|
lock.unlock();
|
|
|
|
if (!asked.count(filePath)) {
|
|
|
|
asked.insert(filePath);
|
|
|
|
socket.SendMsg(wstring(L"RETRIEVE_FILE_STATUS:" + filePath + L'\n').data());
|
|
|
|
}
|
|
|
|
lock.lock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring response;
|
|
|
|
while (!_stop && socket.ReadLine(&response)) {
|
2014-10-15 17:57:15 +04:00
|
|
|
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
|
|
|
wstring responsePath = response.substr(14); // length of REGISTER_PATH:
|
|
|
|
|
|
|
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
_watchedDirectories.push_back(responsePath);
|
|
|
|
}
|
2014-11-03 22:45:38 +03:00
|
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
2014-10-15 17:57:15 +04:00
|
|
|
} else if (StringUtil::begins_with(response, wstring(L"UNREGISTER_PATH:"))) {
|
2014-10-15 17:13:04 +04:00
|
|
|
wstring responsePath = response.substr(16); // length of UNREGISTER_PATH:
|
|
|
|
|
|
|
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
_watchedDirectories.erase(
|
|
|
|
std::remove(_watchedDirectories.begin(), _watchedDirectories.end(), responsePath),
|
|
|
|
_watchedDirectories.end());
|
|
|
|
|
|
|
|
// Remove any item from the cache
|
|
|
|
for (auto it = _cache.begin(); it != _cache.end() ; ) {
|
2014-10-15 17:57:15 +04:00
|
|
|
if (StringUtil::begins_with(it->first, responsePath)) {
|
2014-10-15 17:13:04 +04:00
|
|
|
it = _cache.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 12:24:01 +03:00
|
|
|
// Assume that we won't need this at this point, UNREGISTER_PATH is rare
|
|
|
|
_oldCache.clear();
|
2014-10-15 17:13:04 +04:00
|
|
|
}
|
2014-11-03 22:45:38 +03:00
|
|
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
2014-10-14 18:05:48 +04:00
|
|
|
} else if (StringUtil::begins_with(response, wstring(L"STATUS:")) ||
|
|
|
|
StringUtil::begins_with(response, wstring(L"BROADCAST:"))) {
|
|
|
|
|
|
|
|
auto statusBegin = response.find(L':', 0);
|
|
|
|
assert(statusBegin != std::wstring::npos);
|
|
|
|
|
|
|
|
auto statusEnd = response.find(L':', statusBegin + 1);
|
|
|
|
if (statusEnd == std::wstring::npos) {
|
|
|
|
// the command do not contains two colon?
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1);
|
|
|
|
auto responsePath = response.substr(statusEnd+1);
|
|
|
|
auto state = _StrToFileState(responseStatus);
|
2015-05-06 16:51:41 +03:00
|
|
|
bool wasAsked = asked.erase(responsePath) > 0;
|
2014-10-14 18:05:48 +04:00
|
|
|
|
2014-10-21 16:51:18 +04:00
|
|
|
bool changed = false;
|
2014-10-14 18:05:48 +04:00
|
|
|
{ std::unique_lock<std::mutex> lock(_mutex);
|
2015-05-06 16:51:41 +03:00
|
|
|
bool wasCached = _cache.find(responsePath) != _cache.end();
|
|
|
|
if (wasAsked || wasCached) {
|
|
|
|
auto &it = _cache[responsePath];
|
|
|
|
changed = (it != state);
|
|
|
|
it = state;
|
|
|
|
}
|
2014-10-21 16:51:18 +04:00
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
2014-10-14 18:05:48 +04:00
|
|
|
}
|
2014-11-03 22:45:38 +03:00
|
|
|
}
|
|
|
|
else if (StringUtil::begins_with(response, wstring(L"UPDATE_VIEW"))) {
|
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
2015-05-05 12:24:01 +03:00
|
|
|
// Keep the old states to continue having something to display while the new state is
|
|
|
|
// requested from the client, triggered by clearing _cache.
|
|
|
|
_oldCache.insert(_cache.cbegin(), _cache.cend());
|
|
|
|
|
|
|
|
// Swap to make a copy of the cache under the mutex and clear the one stored.
|
|
|
|
std::unordered_map<std::wstring, FileState> cache;
|
|
|
|
swap(cache, _cache);
|
2014-12-03 11:10:09 +03:00
|
|
|
lock.unlock();
|
2015-05-05 12:24:01 +03:00
|
|
|
// Let explorer know about the invalidated cache entries, it will re-request the ones it needs.
|
|
|
|
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
|
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, it->first.data(), NULL);
|
|
|
|
}
|
|
|
|
}
|
2014-11-03 22:45:38 +03:00
|
|
|
}
|
2014-10-14 18:05:48 +04:00
|
|
|
|
2014-10-15 17:57:15 +04:00
|
|
|
if (socket.Event() == INVALID_HANDLE_VALUE) {
|
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
_cache.clear();
|
2015-05-05 12:24:01 +03:00
|
|
|
_oldCache.clear();
|
2014-10-15 17:57:15 +04:00
|
|
|
_watchedDirectories.clear();
|
|
|
|
_connected = connected = false;
|
|
|
|
}
|
2014-10-15 18:09:35 +04:00
|
|
|
|
|
|
|
if (_stop) return;
|
|
|
|
|
|
|
|
HANDLE handles[2] = { _newQueries, socket.Event() };
|
|
|
|
WaitForMultipleObjects(2, handles, false, 0);
|
2014-10-14 18:05:48 +04:00
|
|
|
}
|
2014-07-30 19:20:55 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
|
|
|
|
|
|
|
|
RemotePathChecker::RemotePathChecker()
|
2014-10-15 17:57:15 +04:00
|
|
|
: _connected(false)
|
2014-10-14 18:05:48 +04:00
|
|
|
, _newQueries(CreateEvent(NULL, true, true, NULL))
|
2014-10-15 17:57:15 +04:00
|
|
|
, _thread([this]{ this->workerThreadLoop(); })
|
2014-08-05 21:23:40 +04:00
|
|
|
{
|
2014-10-14 18:05:48 +04:00
|
|
|
}
|
2014-08-05 21:23:40 +04:00
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
RemotePathChecker::~RemotePathChecker()
|
|
|
|
{
|
|
|
|
_stop = true;
|
|
|
|
//_newQueries.notify_all();
|
|
|
|
SetEvent(_newQueries);
|
|
|
|
_thread.join();
|
|
|
|
CloseHandle(_newQueries);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<wstring> RemotePathChecker::WatchedDirectories()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
return _watchedDirectories;
|
2014-08-05 21:23:40 +04:00
|
|
|
}
|
|
|
|
|
2014-08-04 17:40:08 +04:00
|
|
|
bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
2014-07-30 19:20:55 +04:00
|
|
|
{
|
2014-10-14 18:05:48 +04:00
|
|
|
assert(state); assert(filePath);
|
2014-07-30 19:20:55 +04:00
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
2014-10-15 17:13:04 +04:00
|
|
|
if (!_connected) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-07-30 19:20:55 +04:00
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
auto path = std::wstring(filePath);
|
|
|
|
|
|
|
|
auto it = _cache.find(path);
|
|
|
|
if (it != _cache.end()) {
|
|
|
|
*state = it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-05 12:24:01 +03:00
|
|
|
// Re-request the status while we display what we have in _oldCache
|
2014-10-14 18:05:48 +04:00
|
|
|
_pending.push(filePath);
|
2015-05-05 12:24:01 +03:00
|
|
|
|
|
|
|
it = _oldCache.find(path);
|
|
|
|
bool foundInOldCache = it != _oldCache.end();
|
|
|
|
if (foundInOldCache)
|
|
|
|
*state = it->second;
|
|
|
|
|
2014-12-03 11:10:09 +03:00
|
|
|
lock.unlock();
|
2014-10-14 18:05:48 +04:00
|
|
|
SetEvent(_newQueries);
|
2015-05-05 12:24:01 +03:00
|
|
|
return foundInOldCache;
|
2014-07-30 19:20:55 +04:00
|
|
|
}
|
|
|
|
|
2014-10-14 18:05:48 +04:00
|
|
|
RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstring &str)
|
2014-07-30 19:20:55 +04:00
|
|
|
{
|
|
|
|
if (str == L"NOP" || str == L"NONE") {
|
|
|
|
return StateNone;
|
|
|
|
} else if (str == L"SYNC" || str == L"NEW") {
|
|
|
|
return StateSync;
|
|
|
|
} else if (str == L"SYNC+SWM" || str == L"NEW+SWM") {
|
|
|
|
return StateSyncSWM;
|
|
|
|
} else if (str == L"OK") {
|
|
|
|
return StateOk;
|
|
|
|
} else if (str == L"OK+SWM") {
|
|
|
|
return StateOkSWM;
|
|
|
|
} else if (str == L"IGNORE") {
|
|
|
|
return StateWarning;
|
|
|
|
} else if (str == L"IGNORE+SWM") {
|
|
|
|
return StateWarningSWM;
|
|
|
|
} else if (str == L"ERROR") {
|
|
|
|
return StateError;
|
|
|
|
} else if (str == L"ERROR+SWM") {
|
|
|
|
return StateErrorSWM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return StateNone;
|
|
|
|
}
|