Comparar commits
55 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| e95b73dfac | |||
| c00e3e8c0a | |||
| 2a12610a46 | |||
| d466a05915 | |||
| e05d6bfcdc | |||
| b49dd02e3d | |||
| d6fdda8efa | |||
| faedaa5e09 | |||
| 661c7f0558 | |||
| a7efe144fc | |||
| b68a28de8d | |||
| 72a7b7ca42 | |||
| 166ef85a51 | |||
| 1f60c61f87 | |||
| 30ef794fa1 | |||
| 47fbfbc006 | |||
| e131c142ff | |||
| 1b99ff2e91 | |||
| 3e85d47a57 | |||
| e709d75d56 | |||
| cb5cfb8cf6 | |||
| ebd2a15711 | |||
| d3a0608bd5 | |||
| c25b599bbb | |||
| a5ff9e58e3 | |||
| 9b96899d75 | |||
| b046cca010 | |||
| edf0a99a8a | |||
| a4640b202f | |||
| 5ef2e88f00 | |||
| 352f168313 | |||
| 85d3de1589 | |||
| 588a88fb63 | |||
| cd9e88ad22 | |||
| 29b39acfbe | |||
| 7427be0062 | |||
| e7546903cc | |||
| 78e3a7e897 | |||
| 1a21b8698e | |||
| 280906eab9 | |||
| 9d7425b201 | |||
| 7eb43d7f7e | |||
| d76e0ec6d8 | |||
| 4a83f976e1 | |||
| 23572aaf77 | |||
| 48655ff1ec | |||
| ffbf34cb97 | |||
| fad690be11 | |||
| 355a644020 | |||
| 5e6e4e7464 | |||
| 76fde49282 | |||
| 4ef7d0410d | |||
| a1b4984d14 | |||
| adbe5ecf55 | |||
| ba32571039 |
+39
@@ -1,6 +1,45 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
version 2.3.0 (2017-0x-xx)
|
||||
* WiP!
|
||||
* WiP Switch Windows and OS X build to 5.6.2
|
||||
* WiP Performance improvements for exclude detection
|
||||
* Switch to new ownCloud server WebDAV endpoint
|
||||
* Chunking NG: New file upload chunking algorithmn for ownCloud server 9.2
|
||||
* Allow to sync a folder to multiple different servers (Filename change from .csync_journal.db to _sync_$HASH.db)
|
||||
* Conflicts: Use the local mtime for the conflict file name (#5273)
|
||||
* "Sync now" menu item
|
||||
* SSL Client certificate support improved (Show UI, Store keys in keychain)
|
||||
* Propagator: Upload more small files in parallel
|
||||
* Sync Engine: Read data-fingerprint property to detect backups (#2325)
|
||||
* GUI: Show link to ceate an app password/token for syncing
|
||||
* Share dialog: Add 'Mail link' button
|
||||
* Caja file manager plugin
|
||||
* Make "backup detected" message to not trigger in wrong cases
|
||||
* SyncEngine: Fix renaming of folder when file are changed (#5192)
|
||||
* Fix reconnect bug if status.php intermittently returns wrong data (#5188)
|
||||
* Improve sync scheduling (#5317)
|
||||
* Overlay icons: Improvements in correctnes
|
||||
* Tray menu: Only update on demand to fix Linux desktop integration glitches
|
||||
* Progress: Better time/bandwidth estimations
|
||||
* Network: Follow certain HTTP redirects (#2791)
|
||||
* Network: Remove all cookies (including load balancers etc) when logging out
|
||||
* Discovery thread: Low priority
|
||||
* owncloudsync.log: Write during propagation
|
||||
* Better error message for files with trailing spaces on Windows
|
||||
* Excludes: Consider files in hidden folders excluded (#5163)
|
||||
* Allow sync directory to be a symlinked directory
|
||||
* Add manifest file on Windows to make the application UAC aware
|
||||
* macOS: Improve monochrome tray icons
|
||||
* Don't blacklist 507 Insufficent Storage #5346 (#5424)
|
||||
* Shibboleth bugfixes
|
||||
* Fixes with regards to low disk space
|
||||
* A ton of other bugfixes
|
||||
* Refactorings
|
||||
* Improved documentation
|
||||
* Crash fixes
|
||||
|
||||
version 2.2.4 (release 2016-09-27)
|
||||
* Dolphin Plugin: Use the Application name for the socket path (#5172)
|
||||
* SyncEngine: Fix renaming of folder when file are changed (#5195)
|
||||
|
||||
+1
-1
Submodule binary updated: 0d89ac7766...741b49156b
@@ -418,10 +418,8 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${QT_DLL_PATH}\Qt5Core.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Gui.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Network.dll"
|
||||
File "${QT_DLL_PATH}\Qt5OpenGL.dll"
|
||||
File "${QT_DLL_PATH}\Qt5PrintSupport.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Qml.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Quick.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Sql.dll"
|
||||
File "${QT_DLL_PATH}\Qt5WebKit.dll"
|
||||
File "${QT_DLL_PATH}\Qt5WebKitWidgets.dll"
|
||||
@@ -435,9 +433,9 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
|
||||
;Qt deps
|
||||
File "${MING_BIN}\libpng16-16.dll"
|
||||
File "${MING_BIN}\icudata53.dll"
|
||||
File "${MING_BIN}\icui18n53.dll"
|
||||
File "${MING_BIN}\icuuc53.dll"
|
||||
File "${MING_BIN}\icudata56.dll"
|
||||
File "${MING_BIN}\icui18n56.dll"
|
||||
File "${MING_BIN}\icuuc56.dll"
|
||||
File "${MING_BIN}\libEGL.dll"
|
||||
File "${MING_BIN}\libGLESv2.dll"
|
||||
File "${MING_BIN}\libjpeg-8.dll"
|
||||
@@ -446,11 +444,14 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${MING_BIN}\libcrypto-10.dll"
|
||||
File "${MING_BIN}\libssl-10.dll"
|
||||
File "${MING_BIN}\libstdc++-6.dll"
|
||||
File "${MING_BIN}\libwebp-4.dll"
|
||||
File "${MING_BIN}\libwebp-5.dll"
|
||||
File "${MING_BIN}\libxslt-1.dll"
|
||||
File "${MING_BIN}\libxml2-2.dll"
|
||||
File "${MING_BIN}\zlib1.dll"
|
||||
File "${MING_BIN}\libsqlite3-0.dll"
|
||||
File "${MING_BIN}\libharfbuzz-0.dll"
|
||||
File "${MING_BIN}\libfreetype-6.dll"
|
||||
File "${MING_BIN}\libglib-2.0-0.dll"
|
||||
File "${MING_BIN}\libintl-8.dll"
|
||||
|
||||
;QtKeyChain stuff
|
||||
File "${MING_BIN}\libqt5keychain.dll"
|
||||
|
||||
@@ -77,7 +77,7 @@ a synchronization process.
|
||||
|
||||
Before the 1.3.0 release of the Desktop Client, the synchronization process
|
||||
might create false conflict files if time deviates. Original and changed files
|
||||
conflict only in their timestamp, but not in their content. This behaviour was
|
||||
conflict only in their timestamp, but not in their content. This behavior was
|
||||
changed to employ a binary check if files differ.
|
||||
|
||||
Like files, directories also hold a unique ID that changes whenever one of the
|
||||
|
||||
+3
-20
@@ -3,7 +3,7 @@ The Automatic Updater
|
||||
=====================
|
||||
|
||||
The Automatic Updater ensures that you always have the
|
||||
latest features and bugfixes for your ownCloud synchronization client.
|
||||
latest features and bug fixes for your ownCloud synchronization client.
|
||||
|
||||
The Automatic Updater updates only on Mac OS X and Windows computers; Linux
|
||||
users only need to use their normal package managers. However, on Linux systems
|
||||
@@ -49,7 +49,6 @@ the Linux operating system do not perform any updates on their own. The client
|
||||
will inform you (``Settings -> General -> Updates``) when an update is
|
||||
available.
|
||||
|
||||
|
||||
Preventing Automatic Updates
|
||||
----------------------------
|
||||
|
||||
@@ -110,24 +109,8 @@ To prevent automatic updates and disallow manual overrides:
|
||||
Preventing Automatic Updates in Mac OS X Environments
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can disable the automatic update mechanism in Mac OS X operating systems
|
||||
using the system-wide ``.plist`` file. To access this file:
|
||||
|
||||
1. Go to this directory::
|
||||
|
||||
/Library/Preferences/
|
||||
|
||||
2. Locate and open the following file::
|
||||
|
||||
com.owncloud.desktopclient.plist
|
||||
|
||||
3. Add a new root level item of type ``bool``.
|
||||
|
||||
4. Name the item ``skipUpdateCheck``.
|
||||
|
||||
5. Set the item to ``true``.
|
||||
|
||||
Alternatively, you can copy the file
|
||||
You can disable the automatic update mechanism, in the Mac OS X operating system,
|
||||
by copying the file
|
||||
``owncloud.app/Contents/Resources/deny_autoupdate_com.owncloud.desktopclient.plist``
|
||||
to ``/Library/Preferences/com.owncloud.desktopclient.plist``.
|
||||
|
||||
|
||||
+12
-8
@@ -54,7 +54,7 @@ distribution. Make sure the repositories for source packages are enabled.
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
In additon to needing XCode (along with the command line tools), developing in
|
||||
In addition to needing XCode (along with the command line tools), developing in
|
||||
the Mac OS X environment requires extra dependencies. You can install these
|
||||
dependencies through MacPorts_ or Homebrew_. These dependencies are required
|
||||
only on the build machine, because non-standard libs are deployed in the app
|
||||
@@ -77,25 +77,29 @@ To set up your build environment for development using HomeBrew_:
|
||||
|
||||
brew tap owncloud/owncloud
|
||||
|
||||
5. Install any missing dependencies::
|
||||
5. Install a Qt5 version with qtwebkit support::
|
||||
|
||||
brew install qt5 --with-qtwebkit
|
||||
|
||||
6. Install any missing dependencies::
|
||||
|
||||
brew install $(brew deps owncloud-client)
|
||||
|
||||
3. Add Qt from brew to the path::
|
||||
7. Add Qt from brew to the path::
|
||||
|
||||
export PATH=/usr/local/Cellar/qt5/5.x.y/bin:$PATH
|
||||
|
||||
Where ``x.z`` is the current version of Qt 5 that brew has installed
|
||||
Where ``x.y`` is the current version of Qt 5 that brew has installed
|
||||
on your machine.
|
||||
4. Install qtkeychain from here: git clone https://github.com/frankosterfeld/qtkeychain.git
|
||||
8. Install qtkeychain from here: git clone https://github.com/frankosterfeld/qtkeychain.git
|
||||
make sure you make the same install prefix as later while building the client e.g. -
|
||||
``DCMAKE_INSTALL_PREFIX=/Path/to/client-install``
|
||||
|
||||
5. For compilation of the client, follow the :ref:`generic-build-instructions`.
|
||||
9. For compilation of the client, follow the :ref:`generic-build-instructions`.
|
||||
|
||||
6. Install the Packages_ package creation tool.
|
||||
10. Install the Packages_ package creation tool.
|
||||
|
||||
7. In the build directory, run ``admin/osx/create_mac.sh <build_dir>
|
||||
11. In the build directory, run ``admin/osx/create_mac.sh <build_dir>
|
||||
<install_dir>``. If you have a developer signing certificate, you can specify
|
||||
its Common Name as a third parameter (use quotes) to have the package
|
||||
signed automatically.
|
||||
|
||||
+8
-3
@@ -16,15 +16,20 @@ format. You can overwrite changes using the ownCloud configuration dialog.
|
||||
.. note:: Use caution when making changes to the ownCloud Client configuration
|
||||
file. Incorrect settings can produce unintended results.
|
||||
|
||||
You can change the following configuration settings (must be under the ``[ownCloud]`` section)
|
||||
You can change the following configuration settings in the ``[ownCloud]`` section:
|
||||
|
||||
- ``remotePollInterval`` (default: ``30000``) -- Specifies the poll time for the remote repository in milliseconds.
|
||||
|
||||
- ``maxLogLines`` (default: ``20000``) -- Specifies the maximum number of log lines displayed in the log window.
|
||||
- ``forceSyncInterval`` (default: ``7200000``) -- The duration of no activity after which a synchronization run shall be triggered automatically.
|
||||
|
||||
- ``notificationRefreshInterval`` (default: ``300000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
|
||||
|
||||
You can change the following configuration settings in the ``[General]`` section:
|
||||
|
||||
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
|
||||
|
||||
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
|
||||
|
||||
- ``notificationRefreshInterval`` (default``300,000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
|
||||
- ``maxLogLines`` (default: ``20000``) -- Specifies the maximum number of log lines displayed in the log window.
|
||||
|
||||
- ``timeout`` (default: ``300``) -- The timeout for network connections in seconds.
|
||||
|
||||
+9
-6
@@ -1,17 +1,20 @@
|
||||
FAQ
|
||||
===
|
||||
|
||||
**Issue:**
|
||||
|
||||
Some files are continuously uploaded to the server, even when they are not modified.
|
||||
|
||||
**Resolution:**
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
It is possible that another program is changing the modification date of the file.
|
||||
|
||||
If the file is uses the ``.eml`` extension, Windows automatically and
|
||||
continually changes all files, unless you remove
|
||||
``\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PropertySystem\PropertyHandlers``
|
||||
from the windows registry.
|
||||
|
||||
See http://petersteier.wordpress.com/2011/10/22/windows-indexer-changes-modification-dates-of-eml-files/ for more information.
|
||||
|
||||
Syncing breaks when attempting to sync deeper than 50 sub-directories, but the sync client does not report an error (RC=0)
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
The sync client has been intentionally limited to sync no deeper than
|
||||
fifty sub-directories, to help prevent memory problems.
|
||||
Unfortunately, it, *currently*, does not report an error when this occurs.
|
||||
However, a UI notification is planned for a future release of ownCloud.
|
||||
|
||||
@@ -138,7 +138,7 @@ Network
|
||||
|
||||
.. index:: proxy settings, SOCKS, bandwith, throttling, limiting
|
||||
|
||||
This tab consollidates ``Proxy Settings`` and ``Bandwith Limiting``:
|
||||
This tab consolidates ``Proxy Settings`` and ``Bandwith Limiting``:
|
||||
|
||||
.. image:: images/settings_network.png
|
||||
:scale: 50 %
|
||||
@@ -152,7 +152,7 @@ Proxy Settings
|
||||
On Linux, this will only pick up the value of the variable ``http_proxy``.
|
||||
* ``Specify proxy manually as``: Allows to specify custom proxy settings.
|
||||
If you require to go through a HTTP(S) proxy server such as Squid or Microsoft
|
||||
Forefront TMG, pick ``HTTP(S)``. ``SOCKSv5`` on the other hand is particulary
|
||||
Forefront TMG, pick ``HTTP(S)``. ``SOCKSv5`` on the other hand is particularly
|
||||
useful in special company LAN setups, or in combination with the OpenSSH
|
||||
dynamic application level forwarding feature (see ``ssh -D``).
|
||||
* ``Host``: Enter the host name or IP address of your proxy server, followed
|
||||
|
||||
@@ -37,6 +37,10 @@ Operating system:
|
||||
|
||||
OS language:
|
||||
|
||||
Qt version used by client package (Linux only, see also Settings dialog):
|
||||
|
||||
Client package (From ownCloud or distro) (Linux only):
|
||||
|
||||
Installation path of client:
|
||||
|
||||
### Logs
|
||||
|
||||
@@ -616,6 +616,36 @@ X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
@@ -751,7 +781,10 @@ Comment[th_TH]=@APPLICATION_NAME@ ไคลเอนต์ประสานข
|
||||
GenericName[th_TH]=ประสานข้อมูลโฟลเดอร์
|
||||
Name[th_TH]= @APPLICATION_NAME@ ไคลเอนต์ประสานข้อมูลเดสก์ท็อป
|
||||
Icon[th_TH]=@APPLICATION_EXECUTABLE@
|
||||
Comment[es_MX]=Cliente de escritorio para sincronziación de @APPLICATION_NAME@
|
||||
GenericName[es_MX]=Sincronización de Carpetas
|
||||
Name[es_MX]=Cliente de escritorio para sincronziación de @APPLICATION_NAME@
|
||||
Icon[es_MX]=@APPLICATION_EXECUTABLE@
|
||||
Comment[nb_NO]=@APPLICATION_NAME@ skrivebordssynkroniseringsklient
|
||||
GenericName[nb_NO]=Mappesynkronisering
|
||||
Name[nb_NO]=@APPLICATION_NAME@ skrivebordssynkroniseringsklient
|
||||
|
||||
@@ -16,12 +16,13 @@ set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/ownCloud.icns")
|
||||
# those user-defined settings to build the plist.
|
||||
add_custom_target( mac_overlayplugin ALL
|
||||
xcodebuild -project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj
|
||||
-target FinderSyncExt -configuration Release SYMROOT=${CMAKE_CURRENT_BINARY_DIR}
|
||||
OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}
|
||||
OC_APPLICATION_NAME="${APPLICATION_NAME}"
|
||||
OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}
|
||||
OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}
|
||||
COMMENT building Mac Overlay icons)
|
||||
-target FinderSyncExt -configuration Release "SYMROOT=${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}"
|
||||
"OC_APPLICATION_NAME=${APPLICATION_NAME}"
|
||||
"OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}"
|
||||
"OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}"
|
||||
COMMENT building Mac Overlay icons
|
||||
VERBATIM)
|
||||
add_dependencies(mac_overlayplugin ${APPLICATION_EXECUTABLE}) # for the ownCloud.icns to be generated
|
||||
|
||||
|
||||
|
||||
@@ -155,9 +155,13 @@
|
||||
{
|
||||
_shareMenuTitle = nil;
|
||||
|
||||
[_registeredDirectories removeAllObjects];
|
||||
// For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when
|
||||
// we reset the directoryURLs (seen on macOS 10.12 at least).
|
||||
// First setting it to the FS root and then setting it to nil seems to work around the issue.
|
||||
[FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:[NSURL fileURLWithPath:@"/"]];
|
||||
// This will tell Finder that this extension isn't attached to any directory
|
||||
// until we can reconnect to the sync client.
|
||||
[_registeredDirectories removeAllObjects];
|
||||
[FIFinderSyncController defaultController].directoryURLs = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <KIOCore/kfileitem.h>
|
||||
#include <KIOCore/KFileItemListProperties>
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QTimer>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
@@ -43,7 +44,8 @@ public:
|
||||
auto url = urls.first();
|
||||
if (!url.isLocalFile())
|
||||
return {};
|
||||
auto localFile = url.toLocalFile();
|
||||
QDir localPath(url.toLocalFile());
|
||||
auto localFile = localPath.canonicalPath();
|
||||
|
||||
const auto paths = helper->paths();
|
||||
if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <KPluginFactory>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <KIOCore/kfileitem.h>
|
||||
#include <QDir>
|
||||
#include <QTimer>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
@@ -46,7 +47,8 @@ public:
|
||||
return QStringList();
|
||||
if (!url.isLocalFile())
|
||||
return QStringList();
|
||||
const QByteArray localFile = url.toLocalFile().toUtf8();
|
||||
QDir localPath(url.toLocalFile());
|
||||
const QByteArray localFile = localPath.canonicalPath().toUtf8();
|
||||
|
||||
helper->sendCommand(QByteArray("RETRIEVE_FILE_STATUS:" + localFile + "\n"));
|
||||
|
||||
|
||||
@@ -36,54 +36,54 @@ using namespace std;
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
return {};
|
||||
}
|
||||
if (!socket.Connect(pipename)) {
|
||||
return {};
|
||||
}
|
||||
socket.SendMsg(L"SHARE_MENU_TITLE\n");
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
return {};
|
||||
}
|
||||
if (!socket.Connect(pipename)) {
|
||||
return {};
|
||||
}
|
||||
socket.SendMsg(L"SHARE_MENU_TITLE\n");
|
||||
|
||||
ContextMenuInfo info;
|
||||
std::wstring response;
|
||||
int sleptCount = 0;
|
||||
while (sleptCount < 5) {
|
||||
if (socket.ReadLine(&response)) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
wstring responsePath = response.substr(14); // length of REGISTER_PATH
|
||||
info.watchedDirectories.push_back(responsePath);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"SHARE_MENU_TITLE:"))) {
|
||||
info.shareMenuTitle = response.substr(17); // length of SHARE_MENU_TITLE:
|
||||
break; // Stop once we received the last sent request
|
||||
}
|
||||
}
|
||||
else {
|
||||
Sleep(50);
|
||||
++sleptCount;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
ContextMenuInfo info;
|
||||
std::wstring response;
|
||||
int sleptCount = 0;
|
||||
while (sleptCount < 5) {
|
||||
if (socket.ReadLine(&response)) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
wstring responsePath = response.substr(14); // length of REGISTER_PATH
|
||||
info.watchedDirectories.push_back(responsePath);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"SHARE_MENU_TITLE:"))) {
|
||||
info.shareMenuTitle = response.substr(17); // length of SHARE_MENU_TITLE:
|
||||
break; // Stop once we received the last sent request
|
||||
}
|
||||
}
|
||||
else {
|
||||
Sleep(50);
|
||||
++sleptCount;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
void OCClientInterface::ShareObject(const std::wstring &path)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
return;
|
||||
}
|
||||
if (!socket.Connect(pipename)) {
|
||||
return;
|
||||
}
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
return;
|
||||
}
|
||||
if (!socket.Connect(pipename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"SHARE:%s\n", path.c_str())))
|
||||
{
|
||||
socket.SendMsg(msg);
|
||||
}
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"SHARE:%s\n", path.c_str())))
|
||||
{
|
||||
socket.SendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ class CommunicationSocket;
|
||||
class OCClientInterface
|
||||
{
|
||||
public:
|
||||
struct ContextMenuInfo {
|
||||
std::vector<std::wstring> watchedDirectories;
|
||||
std::wstring shareMenuTitle;
|
||||
};
|
||||
static ContextMenuInfo FetchInfo();
|
||||
static void ShareObject(const std::wstring &path);
|
||||
struct ContextMenuInfo {
|
||||
std::vector<std::wstring> watchedDirectories;
|
||||
std::wstring shareMenuTitle;
|
||||
};
|
||||
static ContextMenuInfo FetchInfo();
|
||||
static void ShareObject(const std::wstring &path);
|
||||
};
|
||||
|
||||
#endif //ABSTRACTSOCKETHANDLER_H
|
||||
|
||||
@@ -30,27 +30,27 @@ extern long g_cDllRef;
|
||||
|
||||
|
||||
OCContextMenu::OCContextMenu(void)
|
||||
: m_cRef(1)
|
||||
, m_pszMenuText(L"&Share")
|
||||
, m_pszVerb("ocshare")
|
||||
, m_pwszVerb(L"ocshare")
|
||||
, m_pszVerbCanonicalName("OCShareViaOC")
|
||||
, m_pwszVerbCanonicalName(L"OCShareViaOC")
|
||||
, m_pszVerbHelpText("Share via ownCloud")
|
||||
, m_pwszVerbHelpText(L"Share via ownCloud")
|
||||
: m_cRef(1)
|
||||
, m_pszMenuText(L"&Share")
|
||||
, m_pszVerb("ocshare")
|
||||
, m_pwszVerb(L"ocshare")
|
||||
, m_pszVerbCanonicalName("OCShareViaOC")
|
||||
, m_pwszVerbCanonicalName(L"OCShareViaOC")
|
||||
, m_pszVerbHelpText("Share via ownCloud")
|
||||
, m_pwszVerbHelpText(L"Share via ownCloud")
|
||||
{
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
OCContextMenu::~OCContextMenu(void)
|
||||
{
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
|
||||
void OCContextMenu::OnVerbDisplayFileName(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::ShareObject(std::wstring(m_szSelectedFile));
|
||||
OCClientInterface::ShareObject(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,30 +59,30 @@ void OCContextMenu::OnVerbDisplayFileName(HWND hWnd)
|
||||
// Query to the interface the component supported.
|
||||
IFACEMETHODIMP OCContextMenu::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
static const QITAB qit[] =
|
||||
{
|
||||
QITABENT(OCContextMenu, IContextMenu),
|
||||
QITABENT(OCContextMenu, IShellExtInit),
|
||||
{ 0 },
|
||||
};
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
static const QITAB qit[] =
|
||||
{
|
||||
QITABENT(OCContextMenu, IContextMenu),
|
||||
QITABENT(OCContextMenu, IShellExtInit),
|
||||
{ 0 },
|
||||
};
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
// Increase the reference count for an interface on an object.
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenu::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&m_cRef);
|
||||
return InterlockedIncrement(&m_cRef);
|
||||
}
|
||||
|
||||
// Decrease the reference count for an interface on an object.
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
||||
{
|
||||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||||
if (0 == cRef) {
|
||||
delete this;
|
||||
}
|
||||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||||
if (0 == cRef) {
|
||||
delete this;
|
||||
}
|
||||
|
||||
return cRef;
|
||||
return cRef;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -92,40 +92,40 @@ IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
||||
|
||||
// Initialize the context menu handler.
|
||||
IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
|
||||
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
|
||||
{
|
||||
if (!pDataObj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (!pDataObj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stm;
|
||||
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stm;
|
||||
|
||||
if (SUCCEEDED(pDataObj->GetData(&fe, &stm))) {
|
||||
// Get an HDROP handle.
|
||||
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
||||
if (hDrop) {
|
||||
// Ignore multi-selections
|
||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||
if (nFiles == 1) {
|
||||
// Get the path of the file.
|
||||
if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile)))
|
||||
{
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
if (SUCCEEDED(pDataObj->GetData(&fe, &stm))) {
|
||||
// Get an HDROP handle.
|
||||
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
||||
if (hDrop) {
|
||||
// Ignore multi-selections
|
||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||
if (nFiles == 1) {
|
||||
// Get the path of the file.
|
||||
if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile)))
|
||||
{
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalUnlock(stm.hGlobal);
|
||||
}
|
||||
GlobalUnlock(stm.hGlobal);
|
||||
}
|
||||
|
||||
ReleaseStgMedium(&stm);
|
||||
}
|
||||
ReleaseStgMedium(&stm);
|
||||
}
|
||||
|
||||
// If any value other than S_OK is returned from the method, the context
|
||||
// menu item is not displayed.
|
||||
return hr;
|
||||
// If any value other than S_OK is returned from the method, the context
|
||||
// menu item is not displayed.
|
||||
return hr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -135,135 +135,136 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
|
||||
void InsertSeperator(HMENU hMenu, UINT indexMenu)
|
||||
{
|
||||
// Add a separator.
|
||||
MENUITEMINFO sep = { sizeof(sep) };
|
||||
sep.fMask = MIIM_TYPE;
|
||||
sep.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(hMenu, indexMenu, TRUE, &sep);
|
||||
// Add a separator.
|
||||
MENUITEMINFO sep = { sizeof(sep) };
|
||||
sep.fMask = MIIM_TYPE;
|
||||
sep.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(hMenu, indexMenu, TRUE, &sep);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
||||
{
|
||||
// If uFlags include CMF_DEFAULTONLY then we should not do anything.
|
||||
if (CMF_DEFAULTONLY & uFlags)
|
||||
{
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
// If uFlags include CMF_DEFAULTONLY then we should not do anything.
|
||||
if (CMF_DEFAULTONLY & uFlags)
|
||||
{
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
bool skip = true;
|
||||
for (const std::wstring path : info.watchedDirectories) {
|
||||
if (StringUtil::begins_with(std::wstring(m_szSelectedFile), path)) {
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
bool skip = true;
|
||||
size_t selectedFileLength = wcslen(m_szSelectedFile);
|
||||
for (const std::wstring path : info.watchedDirectories) {
|
||||
if (StringUtil::isDescendantOf(m_szSelectedFile, selectedFileLength, path)) {
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
if (skip) {
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
indexMenu++;
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
indexMenu++;
|
||||
|
||||
assert(!info.shareMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
|
||||
mii.wID = idCmdFirst + IDM_SHARE;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.shareMenuTitle[0];
|
||||
mii.fState = MFS_ENABLED;
|
||||
if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
assert(!info.shareMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
|
||||
mii.wID = idCmdFirst + IDM_SHARE;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.shareMenuTitle[0];
|
||||
mii.fState = MFS_ENABLED;
|
||||
if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
indexMenu++;
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
indexMenu++;
|
||||
InsertSeperator(hMenu, indexMenu);
|
||||
|
||||
|
||||
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
||||
// Set the code value to the offset of the largest command identifier
|
||||
// that was assigned, plus one (1).
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1));
|
||||
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
||||
// Set the code value to the offset of the largest command identifier
|
||||
// that was assigned, plus one (1).
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
|
||||
// For the Unicode case, if the high-order word is not zero, the
|
||||
// command's verb string is in lpcmi->lpVerbW.
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
||||
{
|
||||
// Is the verb supported by this context menu extension?
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
// For the Unicode case, if the high-order word is not zero, the
|
||||
// command's verb string is in lpcmi->lpVerbW.
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
||||
{
|
||||
// Is the verb supported by this context menu extension?
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
else
|
||||
{
|
||||
// Is the command identifier offset supported by this context menu
|
||||
// extension?
|
||||
if (LOWORD(pici->lpVerb) == IDM_SHARE)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
else
|
||||
{
|
||||
// Is the command identifier offset supported by this context menu
|
||||
// extension?
|
||||
if (LOWORD(pici->lpVerb) == IDM_SHARE)
|
||||
{
|
||||
OnVerbDisplayFileName(pici->hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand,
|
||||
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
|
||||
if (idCommand == IDM_SHARE)
|
||||
{
|
||||
switch (uFlags)
|
||||
{
|
||||
case GCS_HELPTEXTW:
|
||||
// Only useful for pre-Vista versions of Windows that have a
|
||||
// Status bar.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbHelpText);
|
||||
break;
|
||||
if (idCommand == IDM_SHARE)
|
||||
{
|
||||
switch (uFlags)
|
||||
{
|
||||
case GCS_HELPTEXTW:
|
||||
// Only useful for pre-Vista versions of Windows that have a
|
||||
// Status bar.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbHelpText);
|
||||
break;
|
||||
|
||||
case GCS_VERBW:
|
||||
// GCS_VERBW is an optional feature that enables a caller to
|
||||
// discover the canonical name for the verb passed in through
|
||||
// idCommand.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbCanonicalName);
|
||||
break;
|
||||
case GCS_VERBW:
|
||||
// GCS_VERBW is an optional feature that enables a caller to
|
||||
// discover the canonical name for the verb passed in through
|
||||
// idCommand.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_pwszVerbCanonicalName);
|
||||
break;
|
||||
|
||||
default:
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
default:
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If the command (idCommand) is not supported by this context menu
|
||||
// extension handler, return E_INVALIDARG.
|
||||
// If the command (idCommand) is not supported by this context menu
|
||||
// extension handler, return E_INVALIDARG.
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -26,12 +26,12 @@ extern long g_cDllRef;
|
||||
|
||||
OCContextMenuFactory::OCContextMenuFactory() : m_cRef(1)
|
||||
{
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
OCContextMenuFactory::~OCContextMenuFactory()
|
||||
{
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,22 +39,22 @@ OCContextMenuFactory::~OCContextMenuFactory()
|
||||
|
||||
IFACEMETHODIMP OCContextMenuFactory::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
static const QITAB qit[] = { QITABENT(OCContextMenuFactory, IClassFactory), { 0 }, };
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
static const QITAB qit[] = { QITABENT(OCContextMenuFactory, IClassFactory), { 0 }, };
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenuFactory::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&m_cRef);
|
||||
return InterlockedIncrement(&m_cRef);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenuFactory::Release()
|
||||
{
|
||||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||||
if (0 == cRef) {
|
||||
delete this;
|
||||
}
|
||||
return cRef;
|
||||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||||
if (0 == cRef) {
|
||||
delete this;
|
||||
}
|
||||
return cRef;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,30 +62,30 @@ IFACEMETHODIMP_(ULONG) OCContextMenuFactory::Release()
|
||||
|
||||
IFACEMETHODIMP OCContextMenuFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
|
||||
{
|
||||
HRESULT hr = CLASS_E_NOAGGREGATION;
|
||||
HRESULT hr = CLASS_E_NOAGGREGATION;
|
||||
|
||||
// pUnkOuter is used for aggregation. We do not support it in the sample.
|
||||
if (pUnkOuter == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
// pUnkOuter is used for aggregation. We do not support it in the sample.
|
||||
if (pUnkOuter == NULL) {
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
||||
// Create the COM component.
|
||||
OCContextMenu *pExt = new (std::nothrow) OCContextMenu();
|
||||
if (pExt) {
|
||||
// Query the specified interface.
|
||||
hr = pExt->QueryInterface(riid, ppv);
|
||||
pExt->Release();
|
||||
}
|
||||
}
|
||||
// Create the COM component.
|
||||
OCContextMenu *pExt = new (std::nothrow) OCContextMenu();
|
||||
if (pExt) {
|
||||
// Query the specified interface.
|
||||
hr = pExt->QueryInterface(riid, ppv);
|
||||
pExt->Release();
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenuFactory::LockServer(BOOL fLock)
|
||||
{
|
||||
if (fLock) {
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
} else {
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
return S_OK;
|
||||
if (fLock) {
|
||||
InterlockedIncrement(&g_cDllRef);
|
||||
} else {
|
||||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
@@ -23,20 +23,20 @@
|
||||
class OCContextMenuFactory : public IClassFactory
|
||||
{
|
||||
public:
|
||||
// IUnknown
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
// IUnknown
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
|
||||
// IClassFactory
|
||||
IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP LockServer(BOOL fLock);
|
||||
// IClassFactory
|
||||
IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP LockServer(BOOL fLock);
|
||||
|
||||
OCContextMenuFactory();
|
||||
OCContextMenuFactory();
|
||||
|
||||
private:
|
||||
~OCContextMenuFactory();
|
||||
long m_cRef;
|
||||
~OCContextMenuFactory();
|
||||
long m_cRef;
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENUFACTORY_H
|
||||
@@ -23,196 +23,196 @@ namespace {
|
||||
|
||||
HRESULT SetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, PCWSTR pszData)
|
||||
{
|
||||
HRESULT hr;
|
||||
HKEY hKey = NULL;
|
||||
HRESULT hr;
|
||||
HKEY hKey = NULL;
|
||||
|
||||
// Creates the specified registry key. If the key already exists, the
|
||||
// function opens it.
|
||||
hr = HRESULT_FROM_WIN32(RegCreateKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
|
||||
NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL));
|
||||
// Creates the specified registry key. If the key already exists, the
|
||||
// function opens it.
|
||||
hr = HRESULT_FROM_WIN32(RegCreateKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
|
||||
NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL));
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (pszData != NULL)
|
||||
{
|
||||
// Set the specified value of the key.
|
||||
DWORD cbData = lstrlen(pszData) * sizeof(*pszData);
|
||||
hr = HRESULT_FROM_WIN32(RegSetValueEx(hKey, pszValueName, 0,
|
||||
REG_SZ, reinterpret_cast<const BYTE *>(pszData), cbData));
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (pszData != NULL)
|
||||
{
|
||||
// Set the specified value of the key.
|
||||
DWORD cbData = lstrlen(pszData) * sizeof(*pszData);
|
||||
hr = HRESULT_FROM_WIN32(RegSetValueEx(hKey, pszValueName, 0,
|
||||
REG_SZ, reinterpret_cast<const BYTE *>(pszData), cbData));
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT GetHKCRRegistryKeyAndValue(PCWSTR pszSubKey, PCWSTR pszValueName, PWSTR pszData, DWORD cbData)
|
||||
{
|
||||
HRESULT hr;
|
||||
HKEY hKey = NULL;
|
||||
HRESULT hr;
|
||||
HKEY hKey = NULL;
|
||||
|
||||
// Try to open the specified registry key.
|
||||
hr = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
|
||||
KEY_READ, &hKey));
|
||||
// Try to open the specified registry key.
|
||||
hr = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, pszSubKey, 0,
|
||||
KEY_READ, &hKey));
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Get the data for the specified value name.
|
||||
hr = HRESULT_FROM_WIN32(RegQueryValueEx(hKey, pszValueName, NULL,
|
||||
NULL, reinterpret_cast<LPBYTE>(pszData), &cbData));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Get the data for the specified value name.
|
||||
hr = HRESULT_FROM_WIN32(RegQueryValueEx(hKey, pszValueName, NULL,
|
||||
NULL, reinterpret_cast<LPBYTE>(pszData), &cbData));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HRESULT OCContextMenuRegHandler::RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, PCWSTR pszFriendlyName, PCWSTR pszThreadModel)
|
||||
{
|
||||
if (pszModule == NULL || pszThreadModel == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (pszModule == NULL || pszThreadModel == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
HRESULT hr;
|
||||
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
|
||||
// Create the HKCR\CLSID\{<CLSID>} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName);
|
||||
// Create the HKCR\CLSID\{<CLSID>} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszFriendlyName);
|
||||
|
||||
// Create the HKCR\CLSID\{<CLSID>}\InprocServer32 key.
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"CLSID\\%s\\InprocServer32", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the default value of the InprocServer32 key to the
|
||||
// path of the COM module.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszModule);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the threading model of the component.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey,
|
||||
L"ThreadingModel", pszThreadModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Create the HKCR\CLSID\{<CLSID>}\InprocServer32 key.
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"CLSID\\%s\\InprocServer32", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the default value of the InprocServer32 key to the
|
||||
// path of the COM module.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, pszModule);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the threading model of the component.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey,
|
||||
L"ThreadingModel", pszThreadModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT OCContextMenuRegHandler::UnregisterInprocServer(const CLSID& clsid)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
|
||||
// Delete the HKCR\CLSID\{<CLSID>} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(RegDelnode(HKEY_CLASSES_ROOT, szSubkey));
|
||||
}
|
||||
// Delete the HKCR\CLSID\{<CLSID>} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(RegDelnode(HKEY_CLASSES_ROOT, szSubkey));
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT OCContextMenuRegHandler::RegisterShellExtContextMenuHandler(
|
||||
PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName)
|
||||
PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName)
|
||||
{
|
||||
if (pszFileType == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (pszFileType == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
HRESULT hr;
|
||||
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
wchar_t szCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, szCLSID, ARRAYSIZE(szCLSID));
|
||||
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
|
||||
// If pszFileType starts with '.', try to read the default value of the
|
||||
// HKCR\<File Type> key which contains the ProgID to which the file type
|
||||
// is linked.
|
||||
if (*pszFileType == L'.')
|
||||
{
|
||||
wchar_t szDefaultVal[260];
|
||||
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
|
||||
sizeof(szDefaultVal));
|
||||
// If pszFileType starts with '.', try to read the default value of the
|
||||
// HKCR\<File Type> key which contains the ProgID to which the file type
|
||||
// is linked.
|
||||
if (*pszFileType == L'.')
|
||||
{
|
||||
wchar_t szDefaultVal[260];
|
||||
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
|
||||
sizeof(szDefaultVal));
|
||||
|
||||
// If the key exists and its default value is not empty, use the
|
||||
// ProgID as the file type.
|
||||
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
|
||||
{
|
||||
pszFileType = szDefaultVal;
|
||||
}
|
||||
}
|
||||
// If the key exists and its default value is not empty, use the
|
||||
// ProgID as the file type.
|
||||
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
|
||||
{
|
||||
pszFileType = szDefaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the key HKCR\<File Type>\shellex\ContextMenuHandlers\{friendlyName>}
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, pszFriendlyName);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the default value of the key.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, szCLSID);
|
||||
}
|
||||
// Create the key HKCR\<File Type>\shellex\ContextMenuHandlers\{friendlyName>}
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, pszFriendlyName);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Set the default value of the key.
|
||||
hr = SetHKCRRegistryKeyAndValue(szSubkey, NULL, szCLSID);
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT OCContextMenuRegHandler::UnregisterShellExtContextMenuHandler(
|
||||
PCWSTR pszFileType, PCWSTR pszFriendlyName)
|
||||
PCWSTR pszFileType, PCWSTR pszFriendlyName)
|
||||
{
|
||||
if (pszFileType == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (pszFileType == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
HRESULT hr;
|
||||
|
||||
wchar_t szSubkey[MAX_PATH];
|
||||
|
||||
// If pszFileType starts with '.', try to read the default value of the
|
||||
// HKCR\<File Type> key which contains the ProgID to which the file type
|
||||
// is linked.
|
||||
if (*pszFileType == L'.')
|
||||
{
|
||||
wchar_t szDefaultVal[260];
|
||||
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
|
||||
sizeof(szDefaultVal));
|
||||
// If pszFileType starts with '.', try to read the default value of the
|
||||
// HKCR\<File Type> key which contains the ProgID to which the file type
|
||||
// is linked.
|
||||
if (*pszFileType == L'.')
|
||||
{
|
||||
wchar_t szDefaultVal[260];
|
||||
hr = GetHKCRRegistryKeyAndValue(pszFileType, NULL, szDefaultVal,
|
||||
sizeof(szDefaultVal));
|
||||
|
||||
// If the key exists and its default value is not empty, use the
|
||||
// ProgID as the file type.
|
||||
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
|
||||
{
|
||||
pszFileType = szDefaultVal;
|
||||
}
|
||||
}
|
||||
// If the key exists and its default value is not empty, use the
|
||||
// ProgID as the file type.
|
||||
if (SUCCEEDED(hr) && szDefaultVal[0] != L'\0')
|
||||
{
|
||||
pszFileType = szDefaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the HKCR\<File Type>\shellex\ContextMenuHandlers\{friendlyName} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, pszFriendlyName);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(RegDelnode(HKEY_CLASSES_ROOT, szSubkey));
|
||||
}
|
||||
// Remove the HKCR\<File Type>\shellex\ContextMenuHandlers\{friendlyName} key.
|
||||
hr = StringCchPrintf(szSubkey, ARRAYSIZE(szSubkey),
|
||||
L"%s\\shellex\\ContextMenuHandlers\\%s", pszFileType, pszFriendlyName);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(RegDelnode(HKEY_CLASSES_ROOT, szSubkey));
|
||||
}
|
||||
|
||||
return hr;
|
||||
return hr;
|
||||
}
|
||||
@@ -23,16 +23,16 @@
|
||||
class __declspec(dllexport) OCContextMenuRegHandler
|
||||
{
|
||||
public:
|
||||
static HRESULT MakeRegistryEntries(const CLSID& clsid, PCWSTR fileType);
|
||||
static HRESULT RegisterCOMObject(PCWSTR modulePath, PCWSTR friendlyName, const CLSID& clsid);
|
||||
static HRESULT RemoveRegistryEntries(PCWSTR friendlyName);
|
||||
static HRESULT UnregisterCOMObject(const CLSID& clsid);
|
||||
static HRESULT MakeRegistryEntries(const CLSID& clsid, PCWSTR fileType);
|
||||
static HRESULT RegisterCOMObject(PCWSTR modulePath, PCWSTR friendlyName, const CLSID& clsid);
|
||||
static HRESULT RemoveRegistryEntries(PCWSTR friendlyName);
|
||||
static HRESULT UnregisterCOMObject(const CLSID& clsid);
|
||||
|
||||
static HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, PCWSTR pszFriendlyName, PCWSTR pszThreadModel);
|
||||
static HRESULT UnregisterInprocServer(const CLSID& clsid);
|
||||
static HRESULT RegisterInprocServer(PCWSTR pszModule, const CLSID& clsid, PCWSTR pszFriendlyName, PCWSTR pszThreadModel);
|
||||
static HRESULT UnregisterInprocServer(const CLSID& clsid);
|
||||
|
||||
static HRESULT RegisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName);
|
||||
static HRESULT UnregisterShellExtContextMenuHandler(PCWSTR pszFileType, PCWSTR pszFriendlyName);
|
||||
static HRESULT RegisterShellExtContextMenuHandler(PCWSTR pszFileType, const CLSID& clsid, PCWSTR pszFriendlyName);
|
||||
static HRESULT UnregisterShellExtContextMenuHandler(PCWSTR pszFileType, PCWSTR pszFriendlyName);
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENUREGHANDLER_H
|
||||
@@ -23,142 +23,142 @@ long dllReferenceCount = 0;
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
instanceHandle = hModule;
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
instanceHandle = hModule;
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HRESULT CreateFactory(REFIID riid, void **ppv, int state)
|
||||
{
|
||||
HRESULT hResult = E_OUTOFMEMORY;
|
||||
HRESULT hResult = E_OUTOFMEMORY;
|
||||
|
||||
OCOverlayFactory* ocOverlayFactory = new OCOverlayFactory(state);
|
||||
OCOverlayFactory* ocOverlayFactory = new OCOverlayFactory(state);
|
||||
|
||||
if (ocOverlayFactory) {
|
||||
hResult = ocOverlayFactory->QueryInterface(riid, ppv);
|
||||
ocOverlayFactory->Release();
|
||||
}
|
||||
return hResult;
|
||||
if (ocOverlayFactory) {
|
||||
hResult = ocOverlayFactory->QueryInterface(riid, ppv);
|
||||
ocOverlayFactory->Release();
|
||||
}
|
||||
return hResult;
|
||||
}
|
||||
|
||||
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
|
||||
{
|
||||
HRESULT hResult = CLASS_E_CLASSNOTAVAILABLE;
|
||||
GUID guid;
|
||||
GUID guid;
|
||||
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_ERROR, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Error); }
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_ERROR, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Error); }
|
||||
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_OK, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_OK); }
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_OK, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_OK); }
|
||||
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_OK_SHARED, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_OKShared); }
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_OK_SHARED, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_OKShared); }
|
||||
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_SYNC, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Sync); }
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_SYNC, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Sync); }
|
||||
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_WARNING, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Warning); }
|
||||
hResult = CLSIDFromString(OVERLAY_GUID_WARNING, (LPCLSID)&guid);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
if (IsEqualCLSID(guid, rclsid)) { return CreateFactory(riid, ppv, State_Warning); }
|
||||
|
||||
return CLASS_E_CLASSNOTAVAILABLE;
|
||||
return CLASS_E_CLASSNOTAVAILABLE;
|
||||
}
|
||||
|
||||
STDAPI DllCanUnloadNow(void)
|
||||
{
|
||||
return dllReferenceCount > 0 ? S_FALSE : S_OK;
|
||||
|
||||
return S_FALSE;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT RegisterCLSID(LPCOLESTR guidStr, PCWSTR overlayStr, PCWSTR szModule)
|
||||
{
|
||||
HRESULT hResult = S_OK;
|
||||
HRESULT hResult = S_OK;
|
||||
|
||||
GUID guid;
|
||||
hResult = CLSIDFromString(guidStr, (LPCLSID)&guid);
|
||||
GUID guid;
|
||||
hResult = CLSIDFromString(guidStr, (LPCLSID)&guid);
|
||||
|
||||
if (hResult != S_OK) {
|
||||
return hResult;
|
||||
}
|
||||
if (hResult != S_OK) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = OCOverlayRegistrationHandler::RegisterCOMObject(szModule, OVERLAY_GENERIC_NAME, guid);
|
||||
hResult = OCOverlayRegistrationHandler::RegisterCOMObject(szModule, OVERLAY_GENERIC_NAME, guid);
|
||||
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = OCOverlayRegistrationHandler::MakeRegistryEntries(guid, overlayStr);
|
||||
hResult = OCOverlayRegistrationHandler::MakeRegistryEntries(guid, overlayStr);
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HRESULT UnregisterCLSID(LPCOLESTR guidStr, PCWSTR overlayStr)
|
||||
{
|
||||
HRESULT hResult = S_OK;
|
||||
GUID guid;
|
||||
HRESULT hResult = S_OK;
|
||||
GUID guid;
|
||||
|
||||
hResult = CLSIDFromString(guidStr, (LPCLSID)&guid);
|
||||
hResult = CLSIDFromString(guidStr, (LPCLSID)&guid);
|
||||
|
||||
if (hResult != S_OK) {
|
||||
return hResult;
|
||||
}
|
||||
if (hResult != S_OK) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = OCOverlayRegistrationHandler::UnregisterCOMObject(guid);
|
||||
hResult = OCOverlayRegistrationHandler::UnregisterCOMObject(guid);
|
||||
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = OCOverlayRegistrationHandler::RemoveRegistryEntries(overlayStr);
|
||||
hResult = OCOverlayRegistrationHandler::RemoveRegistryEntries(overlayStr);
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HRESULT _stdcall DllRegisterServer(void)
|
||||
{
|
||||
HRESULT hResult = S_OK;
|
||||
HRESULT hResult = S_OK;
|
||||
|
||||
wchar_t szModule[MAX_PATH];
|
||||
wchar_t szModule[MAX_PATH];
|
||||
|
||||
if (GetModuleFileName(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0) {
|
||||
hResult = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hResult;
|
||||
}
|
||||
if (GetModuleFileName(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0) {
|
||||
hResult = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hResult;
|
||||
}
|
||||
|
||||
// Unregister any obsolete CLSID when we register here
|
||||
// Those CLSID were removed in 2.1, but we need to make sure to prevent any previous version
|
||||
// of the extension on the system from loading at the same time as a new version to avoid crashing explorer.
|
||||
UnregisterCLSID(OVERLAY_GUID_ERROR_SHARED, OVERLAY_NAME_ERROR_SHARED);
|
||||
UnregisterCLSID(OVERLAY_GUID_SYNC_SHARED, OVERLAY_NAME_SYNC_SHARED);
|
||||
UnregisterCLSID(OVERLAY_GUID_WARNING_SHARED, OVERLAY_NAME_WARNING_SHARED);
|
||||
// Unregister any obsolete CLSID when we register here
|
||||
// Those CLSID were removed in 2.1, but we need to make sure to prevent any previous version
|
||||
// of the extension on the system from loading at the same time as a new version to avoid crashing explorer.
|
||||
UnregisterCLSID(OVERLAY_GUID_ERROR_SHARED, OVERLAY_NAME_ERROR_SHARED);
|
||||
UnregisterCLSID(OVERLAY_GUID_SYNC_SHARED, OVERLAY_NAME_SYNC_SHARED);
|
||||
UnregisterCLSID(OVERLAY_GUID_WARNING_SHARED, OVERLAY_NAME_WARNING_SHARED);
|
||||
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING, szModule);
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC, szModule);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = RegisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING, szModule);
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
STDAPI DllUnregisterServer(void)
|
||||
@@ -167,21 +167,21 @@ STDAPI DllUnregisterServer(void)
|
||||
|
||||
wchar_t szModule[MAX_PATH];
|
||||
|
||||
if (GetModuleFileNameW(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0)
|
||||
if (GetModuleFileNameW(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0)
|
||||
{
|
||||
hResult = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING);
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC);
|
||||
if (!SUCCEEDED(hResult)) { return hResult; }
|
||||
hResult = UnregisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING);
|
||||
|
||||
return hResult;
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Daniel Molkentin <danimo@owncloud.com>. 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; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "OCContextMenu.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#define IDM_SHARE 0
|
||||
|
||||
OCContextMenu::OCContextMenu()
|
||||
: m_pwszVerb(0)
|
||||
, m_referenceCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
OCContextMenu::~OCContextMenu()
|
||||
{
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenu::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&m_referenceCount);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
||||
{
|
||||
ULONG cRef = InterlockedDecrement(&m_referenceCount);
|
||||
if (0 == cRef)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return cRef;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (IsEqualIID(IID_IUnknown, riid) || IsEqualIID(IID_IContextMenu, riid))
|
||||
{
|
||||
*ppv = static_cast<IContextMenu *>(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = E_NOINTERFACE;
|
||||
*ppv = NULL;
|
||||
}
|
||||
|
||||
if (*ppv)
|
||||
{
|
||||
AddRef();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
|
||||
if (idCmd == IDM_SHARE)
|
||||
{
|
||||
switch (uFlags)
|
||||
{
|
||||
case GCS_HELPTEXTW:
|
||||
hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax, L"Shares file or directory with ownCloud");
|
||||
break;
|
||||
|
||||
case GCS_VERBW:
|
||||
// GCS_VERBW is an optional feature that enables a caller
|
||||
// to discover the canonical name for the verb that is passed in
|
||||
// through idCommand.
|
||||
hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax, L"ownCloudShare");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
|
||||
(pici->fMask & CMIC_MASK_UNICODE))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW))
|
||||
{
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW, m_pwszVerb))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOWORD(pici->lpVerb) != IDM_SHARE) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
MessageBox(pici->hwnd,
|
||||
L"ownCloud was here",
|
||||
L"ownCloud was here",
|
||||
MB_OK | MB_ICONINFORMATION);
|
||||
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (!(CMF_DEFAULTONLY & uFlags))
|
||||
{
|
||||
InsertMenu(hMenu, indexMenu, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHARE, L"&Share with ownCloud");
|
||||
}
|
||||
hr = StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"ownCloudShare");
|
||||
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_SHARE + 1));
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Daniel Molkentin <danimo@owncloud.com>. 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; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OCCONTEXTMENU_H
|
||||
#define OCCONTEXTMENU_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShObjIdl.h"
|
||||
#include "memory"
|
||||
|
||||
class OCContextMenu :public IContextMenu
|
||||
{
|
||||
public:
|
||||
OCContextMenu();
|
||||
~OCContextMenu();
|
||||
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
|
||||
|
||||
IFACEMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax);
|
||||
IFACEMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici);
|
||||
IFACEMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
|
||||
private:
|
||||
TCHAR* m_pwszVerb;
|
||||
long m_referenceCount;
|
||||
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENU_H
|
||||
@@ -45,19 +45,19 @@ unique_ptr<RemotePathChecker> s_instance;
|
||||
|
||||
RemotePathChecker *getGlobalChecker()
|
||||
{
|
||||
// On Vista we'll run into issue #2680 if we try to create the thread+pipe connection
|
||||
// on any DllGetClassObject of our registered classes.
|
||||
// Work around the issue by creating the static RemotePathChecker only once actually needed.
|
||||
static once_flag s_onceFlag;
|
||||
call_once(s_onceFlag, [] { s_instance.reset(new RemotePathChecker); });
|
||||
// On Vista we'll run into issue #2680 if we try to create the thread+pipe connection
|
||||
// on any DllGetClassObject of our registered classes.
|
||||
// Work around the issue by creating the static RemotePathChecker only once actually needed.
|
||||
static once_flag s_onceFlag;
|
||||
call_once(s_onceFlag, [] { s_instance.reset(new RemotePathChecker); });
|
||||
|
||||
return s_instance.get();
|
||||
return s_instance.get();
|
||||
}
|
||||
|
||||
}
|
||||
OCOverlay::OCOverlay(int state)
|
||||
: _referenceCount(1)
|
||||
, _state(state)
|
||||
: _referenceCount(1)
|
||||
, _state(state)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ IFACEMETHODIMP OCOverlay::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
AddRef();
|
||||
}
|
||||
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -106,80 +106,65 @@ IFACEMETHODIMP_(ULONG) OCOverlay::Release()
|
||||
|
||||
IFACEMETHODIMP OCOverlay::GetPriority(int *pPriority)
|
||||
{
|
||||
// this defines which handler has prededence, so
|
||||
// we order this in terms of likelyhood
|
||||
switch (_state) {
|
||||
case State_OK:
|
||||
*pPriority = 0; break;
|
||||
case State_OKShared:
|
||||
*pPriority = 1; break;
|
||||
case State_Warning:
|
||||
*pPriority = 2; break;
|
||||
case State_Sync:
|
||||
*pPriority = 3; break;
|
||||
case State_Error:
|
||||
*pPriority = 4; break;
|
||||
default:
|
||||
*pPriority = 5; break;
|
||||
}
|
||||
// this defines which handler has prededence, so
|
||||
// we order this in terms of likelyhood
|
||||
switch (_state) {
|
||||
case State_OK:
|
||||
*pPriority = 0; break;
|
||||
case State_OKShared:
|
||||
*pPriority = 1; break;
|
||||
case State_Warning:
|
||||
*pPriority = 2; break;
|
||||
case State_Sync:
|
||||
*pPriority = 3; break;
|
||||
case State_Error:
|
||||
*pPriority = 4; break;
|
||||
default:
|
||||
*pPriority = 5; break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib)
|
||||
IFACEMETHODIMP OCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib)
|
||||
{
|
||||
RemotePathChecker* checker = getGlobalChecker();
|
||||
auto watchedDirectories = checker->WatchedDirectories();
|
||||
RemotePathChecker* checker = getGlobalChecker();
|
||||
std::shared_ptr<const std::vector<std::wstring>> watchedDirectories = checker->WatchedDirectories();
|
||||
|
||||
wstring wpath(pwszPath);
|
||||
wpath.append(L"\\");
|
||||
vector<wstring>::iterator it;
|
||||
bool watched = false;
|
||||
for (it = watchedDirectories.begin(); it != watchedDirectories.end(); ++it) {
|
||||
if (StringUtil::begins_with(wpath, *it)) {
|
||||
watched = true;
|
||||
}
|
||||
}
|
||||
if (watchedDirectories->empty()) {
|
||||
return MAKE_HRESULT(S_FALSE, 0, 0);
|
||||
}
|
||||
|
||||
if (!watched) {
|
||||
return MAKE_HRESULT(S_FALSE, 0, 0);
|
||||
}
|
||||
bool watched = false;
|
||||
size_t pathLength = wcslen(pwszPath);
|
||||
for (auto it = watchedDirectories->begin(); it != watchedDirectories->end(); ++it) {
|
||||
if (StringUtil::isDescendantOf(pwszPath, pathLength, *it)) {
|
||||
watched = true;
|
||||
}
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
if (!checker->IsMonitoredPath(pwszPath, &state)) {
|
||||
return MAKE_HRESULT(S_FALSE, 0, 0);
|
||||
}
|
||||
return MAKE_HRESULT(state == _state ? S_OK : S_FALSE, 0, 0);
|
||||
if (!watched) {
|
||||
return MAKE_HRESULT(S_FALSE, 0, 0);
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
if (!checker->IsMonitoredPath(pwszPath, &state)) {
|
||||
return MAKE_HRESULT(S_FALSE, 0, 0);
|
||||
}
|
||||
return MAKE_HRESULT(state == _state ? S_OK : S_FALSE, 0, 0);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCOverlay::GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags)
|
||||
{
|
||||
*pIndex = 0;
|
||||
*pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
|
||||
*pIndex = _state;
|
||||
*pIndex = 0;
|
||||
*pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
|
||||
*pIndex = _state;
|
||||
|
||||
if (GetModuleFileName(instanceHandle, pwszIconFile, cchMax) == 0) {
|
||||
HRESULT hResult = HRESULT_FROM_WIN32(GetLastError());
|
||||
wcerr << L"IsOK? " << (hResult == S_OK) << L" with path " << pwszIconFile << L", index " << *pIndex << endl;
|
||||
return hResult;
|
||||
}
|
||||
if (GetModuleFileName(instanceHandle, pwszIconFile, cchMax) == 0) {
|
||||
HRESULT hResult = HRESULT_FROM_WIN32(GetLastError());
|
||||
wcerr << L"IsOK? " << (hResult == S_OK) << L" with path " << pwszIconFile << L", index " << *pIndex << endl;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
bool OCOverlay::_IsOverlaysEnabled()
|
||||
{
|
||||
//int enable;
|
||||
bool success = false;
|
||||
|
||||
//if(RegistryUtil::ReadRegistry(REGISTRY_ROOT_KEY, REGISTRY_ENABLE_OVERLAY, &enable))
|
||||
//{
|
||||
// if(enable) {
|
||||
// success = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,22 +21,21 @@ class OCOverlay : public IShellIconOverlayIdentifier
|
||||
|
||||
{
|
||||
public:
|
||||
OCOverlay(int state);
|
||||
OCOverlay(int state);
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags);
|
||||
IFACEMETHODIMP GetPriority(int *pPriority);
|
||||
IFACEMETHODIMP IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib);
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags);
|
||||
IFACEMETHODIMP GetPriority(int *pPriority);
|
||||
IFACEMETHODIMP IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib);
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
|
||||
protected:
|
||||
~OCOverlay();
|
||||
|
||||
private:
|
||||
bool _IsOverlaysEnabled();
|
||||
long _referenceCount;
|
||||
int _state;
|
||||
int _state;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@
|
||||
extern long dllReferenceCount;
|
||||
|
||||
OCOverlayFactory::OCOverlayFactory(int state)
|
||||
: _referenceCount(1), _state(state)
|
||||
: _referenceCount(1), _state(state)
|
||||
{
|
||||
InterlockedIncrement(&dllReferenceCount);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ OCOverlayFactory::~OCOverlayFactory()
|
||||
|
||||
IFACEMETHODIMP OCOverlayFactory::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
HRESULT hResult = S_OK;
|
||||
HRESULT hResult = S_OK;
|
||||
|
||||
if (IsEqualIID(IID_IUnknown, riid) ||
|
||||
IsEqualIID(IID_IClassFactory, riid))
|
||||
@@ -66,20 +66,20 @@ IFACEMETHODIMP_(ULONG) OCOverlayFactory::Release()
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCOverlayFactory::CreateInstance(
|
||||
IUnknown *pUnkOuter, REFIID riid, void **ppv)
|
||||
IUnknown *pUnkOuter, REFIID riid, void **ppv)
|
||||
{
|
||||
HRESULT hResult = CLASS_E_NOAGGREGATION;
|
||||
HRESULT hResult = CLASS_E_NOAGGREGATION;
|
||||
|
||||
if (pUnkOuter != NULL) { return hResult; }
|
||||
|
||||
hResult = E_OUTOFMEMORY;
|
||||
hResult = E_OUTOFMEMORY;
|
||||
OCOverlay *lrOverlay = new (std::nothrow) OCOverlay(_state);
|
||||
if (!lrOverlay) { return hResult; }
|
||||
if (!lrOverlay) { return hResult; }
|
||||
|
||||
hResult = lrOverlay->QueryInterface(riid, ppv);
|
||||
lrOverlay->Release();
|
||||
lrOverlay->Release();
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCOverlayFactory::LockServer(BOOL fLock)
|
||||
|
||||
@@ -18,20 +18,20 @@
|
||||
#pragma once
|
||||
|
||||
enum State {
|
||||
State_Error = 0,
|
||||
State_OK, State_OKShared,
|
||||
State_Sync,
|
||||
State_Warning
|
||||
State_Error = 0,
|
||||
State_OK, State_OKShared,
|
||||
State_Sync,
|
||||
State_Warning
|
||||
};
|
||||
|
||||
class OCOverlayFactory : public IClassFactory
|
||||
{
|
||||
public:
|
||||
OCOverlayFactory(int state);
|
||||
OCOverlayFactory(int state);
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP_(ULONG) AddRef();
|
||||
IFACEMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP LockServer(BOOL fLock);
|
||||
IFACEMETHODIMP LockServer(BOOL fLock);
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||||
IFACEMETHODIMP_(ULONG) Release();
|
||||
|
||||
@@ -40,7 +40,7 @@ protected:
|
||||
|
||||
private:
|
||||
long _referenceCount;
|
||||
int _state;
|
||||
int _state;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -23,52 +23,52 @@ using namespace std;
|
||||
|
||||
HRESULT OCOverlayRegistrationHandler::MakeRegistryEntries(const CLSID& clsid, PCWSTR friendlyName)
|
||||
{
|
||||
HRESULT hResult;
|
||||
HKEY shellOverlayKey = NULL;
|
||||
// the key may not exist yet
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &shellOverlayKey, NULL));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
hResult = RegCreateKey(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, &shellOverlayKey);
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
}
|
||||
HRESULT hResult;
|
||||
HKEY shellOverlayKey = NULL;
|
||||
// the key may not exist yet
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &shellOverlayKey, NULL));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
hResult = RegCreateKey(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, &shellOverlayKey);
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
}
|
||||
|
||||
HKEY syncExOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(shellOverlayKey, friendlyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &syncExOverlayKey, NULL));
|
||||
HKEY syncExOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(shellOverlayKey, friendlyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &syncExOverlayKey, NULL));
|
||||
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
wchar_t stringCLSID[MAX_PATH];
|
||||
wchar_t stringCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, stringCLSID, ARRAYSIZE(stringCLSID));
|
||||
LPCTSTR value = stringCLSID;
|
||||
hResult = RegSetValueEx(syncExOverlayKey, NULL, 0, REG_SZ, (LPBYTE)value, (DWORD)((wcslen(value)+1) * sizeof(TCHAR)));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
LPCTSTR value = stringCLSID;
|
||||
hResult = RegSetValueEx(syncExOverlayKey, NULL, 0, REG_SZ, (LPBYTE)value, (DWORD)((wcslen(value)+1) * sizeof(TCHAR)));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HRESULT OCOverlayRegistrationHandler::RemoveRegistryEntries(PCWSTR friendlyName)
|
||||
{
|
||||
HRESULT hResult;
|
||||
HKEY shellOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, 0, KEY_WRITE, &shellOverlayKey));
|
||||
HRESULT hResult;
|
||||
HKEY shellOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_OVERLAY_KEY, 0, KEY_WRITE, &shellOverlayKey));
|
||||
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HKEY syncExOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(shellOverlayKey, friendlyName));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
HKEY syncExOverlayKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(shellOverlayKey, friendlyName));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
return hResult;
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HRESULT OCOverlayRegistrationHandler::RegisterCOMObject(PCWSTR modulePath, PCWSTR friendlyName, const CLSID& clsid)
|
||||
@@ -79,45 +79,45 @@ HRESULT OCOverlayRegistrationHandler::RegisterCOMObject(PCWSTR modulePath, PCWST
|
||||
|
||||
wchar_t stringCLSID[MAX_PATH];
|
||||
StringFromGUID2(clsid, stringCLSID, ARRAYSIZE(stringCLSID));
|
||||
HRESULT hResult;
|
||||
HKEY hKey = NULL;
|
||||
HRESULT hResult;
|
||||
HKEY hKey = NULL;
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, REGISTRY_CLSID, 0, KEY_WRITE, &hKey));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, REGISTRY_CLSID, 0, KEY_WRITE, &hKey));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HKEY clsidKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(hKey, stringCLSID, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &clsidKey, NULL));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
HKEY clsidKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(hKey, stringCLSID, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &clsidKey, NULL));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValue(clsidKey, NULL, REG_SZ, friendlyName, (DWORD) wcslen(friendlyName)));
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValue(clsidKey, NULL, REG_SZ, friendlyName, (DWORD) wcslen(friendlyName)));
|
||||
|
||||
HKEY inprocessKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(clsidKey, REGISTRY_IN_PROCESS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inprocessKey, NULL));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
HKEY inprocessKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegCreateKeyEx(clsidKey, REGISTRY_IN_PROCESS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inprocessKey, NULL));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValue(inprocessKey, NULL, REG_SZ, modulePath, (DWORD) wcslen(modulePath)));
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValue(inprocessKey, NULL, REG_SZ, modulePath, (DWORD) wcslen(modulePath)));
|
||||
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValueEx(inprocessKey, REGISTRY_THREADING, 0, REG_SZ, (LPBYTE)REGISTRY_APARTMENT, (DWORD)((wcslen(REGISTRY_APARTMENT)+1) * sizeof(TCHAR))));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValueEx(inprocessKey, REGISTRY_VERSION, 0, REG_SZ, (LPBYTE)REGISTRY_VERSION_NUMBER, (DWORD)(wcslen(REGISTRY_VERSION_NUMBER)+1) * sizeof(TCHAR)));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
hResult = HRESULT_FROM_WIN32(RegSetValueEx(inprocessKey, REGISTRY_VERSION, 0, REG_SZ, (LPBYTE)REGISTRY_VERSION_NUMBER, (DWORD)(wcslen(REGISTRY_VERSION_NUMBER)+1) * sizeof(TCHAR)));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT OCOverlayRegistrationHandler::UnregisterCOMObject(const CLSID& clsid)
|
||||
@@ -125,28 +125,28 @@ HRESULT OCOverlayRegistrationHandler::UnregisterCOMObject(const CLSID& clsid)
|
||||
wchar_t stringCLSID[MAX_PATH];
|
||||
|
||||
StringFromGUID2(clsid, stringCLSID, ARRAYSIZE(stringCLSID));
|
||||
HRESULT hResult;
|
||||
HKEY hKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, REGISTRY_CLSID, 0, DELETE, &hKey));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
HRESULT hResult;
|
||||
HKEY hKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CLASSES_ROOT, REGISTRY_CLSID, 0, DELETE, &hKey));
|
||||
if (!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
HKEY clsidKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(hKey, stringCLSID, 0, DELETE, &clsidKey));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
HKEY clsidKey = NULL;
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(hKey, stringCLSID, 0, DELETE, &clsidKey));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(clsidKey, REGISTRY_IN_PROCESS));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(clsidKey, REGISTRY_IN_PROCESS));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(hKey, stringCLSID));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
hResult = HRESULT_FROM_WIN32(RegDeleteKey(hKey, stringCLSID));
|
||||
if(!SUCCEEDED(hResult)) {
|
||||
return hResult;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
class __declspec(dllexport) OCOverlayRegistrationHandler
|
||||
{
|
||||
public:
|
||||
static HRESULT MakeRegistryEntries(const CLSID& clsid, PCWSTR fileType);
|
||||
static HRESULT RegisterCOMObject(PCWSTR modulePath, PCWSTR friendlyName, const CLSID& clsid);
|
||||
static HRESULT RemoveRegistryEntries(PCWSTR friendlyName);
|
||||
static HRESULT UnregisterCOMObject(const CLSID& clsid);
|
||||
public:
|
||||
static HRESULT MakeRegistryEntries(const CLSID& clsid, PCWSTR fileType);
|
||||
static HRESULT RegisterCOMObject(PCWSTR modulePath, PCWSTR friendlyName, const CLSID& clsid);
|
||||
static HRESULT RemoveRegistryEntries(PCWSTR friendlyName);
|
||||
static HRESULT UnregisterCOMObject(const CLSID& clsid);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -13,38 +13,38 @@
|
||||
*/
|
||||
|
||||
|
||||
#define OVERLAY_GUID_ERROR L"{0960F090-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_ERROR_SHARED L"{0960F091-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_OK L"{0960F092-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_OK_SHARED L"{0960F093-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_SYNC L"{0960F094-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_SYNC_SHARED L"{0960F095-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_WARNING L"{0960F096-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_ERROR L"{0960F090-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_ERROR_SHARED L"{0960F091-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_OK L"{0960F092-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_OK_SHARED L"{0960F093-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_SYNC L"{0960F094-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_SYNC_SHARED L"{0960F095-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_WARNING L"{0960F096-F328-48A3-B746-276B1E3C3722}"
|
||||
#define OVERLAY_GUID_WARNING_SHARED L"{0960F097-F328-48A3-B746-276B1E3C3722}"
|
||||
|
||||
#define OVERLAY_GENERIC_NAME L"OC Overlay Handler"
|
||||
|
||||
// two spaces to put us ahead of the competition :/
|
||||
#define OVERLAY_NAME_ERROR L" OCError"
|
||||
#define OVERLAY_NAME_ERROR_SHARED L" OCErrorShared"
|
||||
#define OVERLAY_NAME_OK L" OCOK"
|
||||
#define OVERLAY_NAME_OK_SHARED L" OCOKShared"
|
||||
#define OVERLAY_NAME_SYNC L" OCSync"
|
||||
#define OVERLAY_NAME_SYNC_SHARED L" OCSyncShared"
|
||||
#define OVERLAY_NAME_WARNING L" OCWarning"
|
||||
#define OVERLAY_NAME_WARNING_SHARED L" OCWarningShared"
|
||||
#define OVERLAY_NAME_ERROR L" OCError"
|
||||
#define OVERLAY_NAME_ERROR_SHARED L" OCErrorShared"
|
||||
#define OVERLAY_NAME_OK L" OCOK"
|
||||
#define OVERLAY_NAME_OK_SHARED L" OCOKShared"
|
||||
#define OVERLAY_NAME_SYNC L" OCSync"
|
||||
#define OVERLAY_NAME_SYNC_SHARED L" OCSyncShared"
|
||||
#define OVERLAY_NAME_WARNING L" OCWarning"
|
||||
#define OVERLAY_NAME_WARNING_SHARED L" OCWarningShared"
|
||||
|
||||
#define REGISTRY_OVERLAY_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers"
|
||||
#define REGISTRY_CLSID L"CLSID"
|
||||
#define REGISTRY_IN_PROCESS L"InprocServer32"
|
||||
#define REGISTRY_THREADING L"ThreadingModel"
|
||||
#define REGISTRY_APARTMENT L"Apartment"
|
||||
#define REGISTRY_VERSION L"Version"
|
||||
#define REGISTRY_VERSION_NUMBER L"1.0"
|
||||
#define REGISTRY_OVERLAY_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers"
|
||||
#define REGISTRY_CLSID L"CLSID"
|
||||
#define REGISTRY_IN_PROCESS L"InprocServer32"
|
||||
#define REGISTRY_THREADING L"ThreadingModel"
|
||||
#define REGISTRY_APARTMENT L"Apartment"
|
||||
#define REGISTRY_VERSION L"Version"
|
||||
#define REGISTRY_VERSION_NUMBER L"1.0"
|
||||
|
||||
//Registry values for running
|
||||
#define REGISTRY_ENABLE_OVERLAY L"EnableOverlay"
|
||||
#define REGISTRY_ENABLE_OVERLAY L"EnableOverlay"
|
||||
|
||||
#define GET_FILE_OVERLAY_ID L"getFileIconId"
|
||||
#define GET_FILE_OVERLAY_ID L"getFileIconId"
|
||||
|
||||
#define PORT 34001
|
||||
#define PORT 34001
|
||||
@@ -31,23 +31,23 @@ using namespace std;
|
||||
namespace {
|
||||
|
||||
std::wstring getUserName() {
|
||||
DWORD len = DEFAULT_BUFLEN;
|
||||
TCHAR buf[DEFAULT_BUFLEN];
|
||||
if (GetUserName(buf, &len)) {
|
||||
return std::wstring(&buf[0], len);
|
||||
} else {
|
||||
return std::wstring();
|
||||
}
|
||||
DWORD len = DEFAULT_BUFLEN;
|
||||
TCHAR buf[DEFAULT_BUFLEN];
|
||||
if (GetUserName(buf, &len)) {
|
||||
return std::wstring(&buf[0], len);
|
||||
} else {
|
||||
return std::wstring();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::wstring CommunicationSocket::DefaultPipePath()
|
||||
{
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud-";
|
||||
pipename += getUserName();
|
||||
return pipename;
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud-";
|
||||
pipename += getUserName();
|
||||
return pipename;
|
||||
}
|
||||
|
||||
CommunicationSocket::CommunicationSocket()
|
||||
@@ -57,23 +57,23 @@ CommunicationSocket::CommunicationSocket()
|
||||
|
||||
CommunicationSocket::~CommunicationSocket()
|
||||
{
|
||||
Close();
|
||||
Close();
|
||||
}
|
||||
|
||||
bool CommunicationSocket::Close()
|
||||
{
|
||||
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
CloseHandle(_pipe);
|
||||
_pipe = INVALID_HANDLE_VALUE;
|
||||
return true;
|
||||
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
CloseHandle(_pipe);
|
||||
_pipe = INVALID_HANDLE_VALUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CommunicationSocket::Connect(const std::wstring &pipename)
|
||||
{
|
||||
_pipe = CreateFile(pipename.data(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
_pipe = CreateFile(pipename.data(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
@@ -84,7 +84,7 @@ bool CommunicationSocket::Connect(const std::wstring &pipename)
|
||||
|
||||
bool CommunicationSocket::SendMsg(const wchar_t* message) const
|
||||
{
|
||||
auto utf8_msg = StringUtil::toUtf8(message);
|
||||
auto utf8_msg = StringUtil::toUtf8(message);
|
||||
|
||||
DWORD numBytesWritten = 0;
|
||||
auto result = WriteFile( _pipe, utf8_msg.c_str(), DWORD(utf8_msg.size()), &numBytesWritten, NULL);
|
||||
@@ -92,7 +92,7 @@ bool CommunicationSocket::SendMsg(const wchar_t* message) const
|
||||
if (result) {
|
||||
return true;
|
||||
} else {
|
||||
const_cast<CommunicationSocket*>(this)->Close();
|
||||
const_cast<CommunicationSocket*>(this)->Close();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -100,9 +100,9 @@ bool CommunicationSocket::SendMsg(const wchar_t* message) const
|
||||
|
||||
bool CommunicationSocket::ReadLine(wstring* response)
|
||||
{
|
||||
if (!response) {
|
||||
return false;
|
||||
}
|
||||
if (!response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
response->clear();
|
||||
|
||||
@@ -110,7 +110,7 @@ bool CommunicationSocket::ReadLine(wstring* response)
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (true) {
|
||||
int lbPos = 0;
|
||||
auto it = std::find(_buffer.begin() + lbPos, _buffer.end(), '\n');
|
||||
if (it != _buffer.end()) {
|
||||
@@ -121,24 +121,24 @@ bool CommunicationSocket::ReadLine(wstring* response)
|
||||
|
||||
std::array<char, 128> resp_utf8;
|
||||
DWORD numBytesRead = 0;
|
||||
DWORD totalBytesAvailable = 0;
|
||||
DWORD totalBytesAvailable = 0;
|
||||
|
||||
if (!PeekNamedPipe(_pipe, NULL, 0, 0, &totalBytesAvailable, 0)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (totalBytesAvailable == 0) {
|
||||
return false;
|
||||
}
|
||||
if (!PeekNamedPipe(_pipe, NULL, 0, 0, &totalBytesAvailable, 0)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (totalBytesAvailable == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadFile(_pipe, resp_utf8.data(), DWORD(resp_utf8.size()), &numBytesRead, NULL)) {
|
||||
if (!ReadFile(_pipe, resp_utf8.data(), DWORD(resp_utf8.size()), &numBytesRead, NULL)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (numBytesRead <= 0) {
|
||||
return false;
|
||||
}
|
||||
_buffer.insert(_buffer.end(), resp_utf8.begin(), resp_utf8.begin()+numBytesRead);
|
||||
_buffer.insert(_buffer.end(), resp_utf8.begin(), resp_utf8.begin()+numBytesRead);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,22 +26,22 @@
|
||||
class __declspec(dllexport) CommunicationSocket
|
||||
{
|
||||
public:
|
||||
static std::wstring DefaultPipePath();
|
||||
static std::wstring DefaultPipePath();
|
||||
|
||||
CommunicationSocket();
|
||||
~CommunicationSocket();
|
||||
CommunicationSocket();
|
||||
~CommunicationSocket();
|
||||
|
||||
bool Connect(const std::wstring& pipename);
|
||||
bool Close();
|
||||
bool Connect(const std::wstring& pipename);
|
||||
bool Close();
|
||||
|
||||
bool SendMsg(const wchar_t*) const;
|
||||
bool ReadLine(std::wstring*);
|
||||
bool SendMsg(const wchar_t*) const;
|
||||
bool ReadLine(std::wstring*);
|
||||
|
||||
HANDLE Event() { return _pipe; }
|
||||
|
||||
private:
|
||||
HANDLE _pipe;
|
||||
std::vector<char> _buffer;
|
||||
private:
|
||||
HANDLE _pipe;
|
||||
std::vector<char> _buffer;
|
||||
bool _connected;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,65 +22,65 @@ using namespace std;
|
||||
|
||||
bool FileUtil::IsChildFile(const wchar_t* rootFolder, vector<wstring>* files)
|
||||
{
|
||||
for(vector<wstring>::iterator it = files->begin(); it != files->end(); it++)
|
||||
{
|
||||
wstring file = *it;
|
||||
for(vector<wstring>::iterator it = files->begin(); it != files->end(); it++)
|
||||
{
|
||||
wstring file = *it;
|
||||
|
||||
size_t found = file.find(rootFolder);
|
||||
size_t found = file.find(rootFolder);
|
||||
|
||||
if(found != string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(found != string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileUtil::IsChildFile(const wchar_t* rootFolder, const wchar_t* file)
|
||||
{
|
||||
wstring* f = new wstring(file);
|
||||
wstring* f = new wstring(file);
|
||||
|
||||
size_t found = f->find(rootFolder);
|
||||
size_t found = f->find(rootFolder);
|
||||
|
||||
if(found != string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if(found != string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileUtil::IsChildFileOfRoot(std::vector<std::wstring>* files)
|
||||
{
|
||||
wstring* rootFolder = new wstring();
|
||||
bool needed = false;
|
||||
wstring* rootFolder = new wstring();
|
||||
bool needed = false;
|
||||
|
||||
if(RegistryUtil::ReadRegistry(REGISTRY_ROOT_KEY, REGISTRY_FILTER_FOLDER, rootFolder))
|
||||
{
|
||||
if(IsChildFile(rootFolder->c_str(), files))
|
||||
{
|
||||
needed = true;
|
||||
}
|
||||
}
|
||||
if(RegistryUtil::ReadRegistry(REGISTRY_ROOT_KEY, REGISTRY_FILTER_FOLDER, rootFolder))
|
||||
{
|
||||
if(IsChildFile(rootFolder->c_str(), files))
|
||||
{
|
||||
needed = true;
|
||||
}
|
||||
}
|
||||
|
||||
delete rootFolder;
|
||||
return needed;
|
||||
delete rootFolder;
|
||||
return needed;
|
||||
}
|
||||
|
||||
bool FileUtil::IsChildFileOfRoot(const wchar_t* filePath)
|
||||
{
|
||||
wstring* rootFolder = new wstring();
|
||||
bool needed = false;
|
||||
|
||||
if(RegistryUtil::ReadRegistry(REGISTRY_ROOT_KEY, REGISTRY_FILTER_FOLDER, rootFolder))
|
||||
{
|
||||
if(FileUtil::IsChildFile(rootFolder->c_str(), filePath))
|
||||
{
|
||||
needed = true;
|
||||
}
|
||||
}
|
||||
wstring* rootFolder = new wstring();
|
||||
bool needed = false;
|
||||
|
||||
if(RegistryUtil::ReadRegistry(REGISTRY_ROOT_KEY, REGISTRY_FILTER_FOLDER, rootFolder))
|
||||
{
|
||||
if(FileUtil::IsChildFile(rootFolder->c_str(), filePath))
|
||||
{
|
||||
needed = true;
|
||||
}
|
||||
}
|
||||
|
||||
delete rootFolder;
|
||||
return needed;
|
||||
delete rootFolder;
|
||||
return needed;
|
||||
}
|
||||
@@ -25,16 +25,16 @@
|
||||
class __declspec(dllexport) FileUtil
|
||||
{
|
||||
public:
|
||||
FileUtil();
|
||||
FileUtil();
|
||||
|
||||
~FileUtil();
|
||||
~FileUtil();
|
||||
|
||||
static bool IsChildFile(const wchar_t*, std::vector<std::wstring>*);
|
||||
static bool IsChildFile(const wchar_t*, const wchar_t*);
|
||||
static bool IsChildFileOfRoot(std::vector<std::wstring>*);
|
||||
static bool IsChildFileOfRoot(const wchar_t*);
|
||||
static bool IsChildFile(const wchar_t*, std::vector<std::wstring>*);
|
||||
static bool IsChildFile(const wchar_t*, const wchar_t*);
|
||||
static bool IsChildFileOfRoot(std::vector<std::wstring>*);
|
||||
static bool IsChildFileOfRoot(const wchar_t*);
|
||||
|
||||
private:
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,8 +25,8 @@ using namespace std;
|
||||
|
||||
OCMessage::OCMessage(void)
|
||||
{
|
||||
_command = new wstring();
|
||||
_value = new wstring();
|
||||
_command = new wstring();
|
||||
_value = new wstring();
|
||||
}
|
||||
|
||||
OCMessage::~OCMessage(void)
|
||||
@@ -35,40 +35,40 @@ OCMessage::~OCMessage(void)
|
||||
|
||||
bool OCMessage::InitFromMessage(const wstring* message)
|
||||
{
|
||||
if(message->length() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ParserUtil::GetItem(COMMAND, message, _command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(message->length() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ParserUtil::GetItem(COMMAND, message, _command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ParserUtil::GetItem(VALUE, message, _value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!ParserUtil::GetItem(VALUE, message, _value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::wstring* OCMessage::GetCommand()
|
||||
{
|
||||
return _command;
|
||||
return _command;
|
||||
}
|
||||
|
||||
std::wstring* OCMessage::GetValue()
|
||||
{
|
||||
return _value;
|
||||
return _value;
|
||||
}
|
||||
|
||||
void OCMessage::SetCommand(std::wstring* command)
|
||||
{
|
||||
_command = command;
|
||||
_command = command;
|
||||
}
|
||||
|
||||
void OCMessage::SetValue(std::wstring* value)
|
||||
{
|
||||
_value = value;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@@ -23,20 +23,20 @@ class __declspec(dllexport) OCMessage
|
||||
{
|
||||
public:
|
||||
OCMessage(void);
|
||||
~OCMessage(void);
|
||||
~OCMessage(void);
|
||||
|
||||
bool InitFromMessage(const std::wstring*);
|
||||
bool InitFromMessage(const std::wstring*);
|
||||
|
||||
std::wstring* GetCommand();
|
||||
std::wstring* GetValue();
|
||||
std::wstring* GetCommand();
|
||||
std::wstring* GetValue();
|
||||
|
||||
void SetCommand(std::wstring*);
|
||||
void SetValue(std::wstring*);
|
||||
void SetCommand(std::wstring*);
|
||||
void SetValue(std::wstring*);
|
||||
|
||||
private:
|
||||
|
||||
std::wstring* _command;
|
||||
std::wstring* _value;
|
||||
std::wstring* _value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -21,369 +21,369 @@ using namespace std;
|
||||
|
||||
bool ParserUtil::GetItem(const wchar_t* item, const wstring* message, wstring* result)
|
||||
{
|
||||
size_t start = message->find(item, 0);
|
||||
size_t start = message->find(item, 0);
|
||||
|
||||
if(start == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(start == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t end = message->find(COLON, start);
|
||||
size_t end = message->find(COLON, start);
|
||||
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Move to next character after :
|
||||
end += 1;
|
||||
//Move to next character after :
|
||||
end += 1;
|
||||
|
||||
wchar_t c = message->at(end);
|
||||
wchar_t c = message->at(end);
|
||||
|
||||
//Move to the next character, which is the start of the value
|
||||
end += 1;
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return GetList(end - 1, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetValue(end, message, result);
|
||||
}
|
||||
//Move to the next character, which is the start of the value
|
||||
end += 1;
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return GetList(end - 1, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetValue(end, message, result);
|
||||
}
|
||||
}
|
||||
|
||||
bool ParserUtil::GetList(size_t start, const wstring* message, wstring* result)
|
||||
{
|
||||
size_t end = start + 1;
|
||||
size_t end = start + 1;
|
||||
|
||||
int openBraceCount = 1;
|
||||
int openBraceCount = 1;
|
||||
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_BRACE, end);
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_BRACE, end);
|
||||
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
|
||||
return GetString(start, end, message, result);
|
||||
return GetString(start, end, message, result);
|
||||
}
|
||||
|
||||
size_t ParserUtil::GetNextStringItemInList(const wstring* message, size_t start, wstring* result)
|
||||
{
|
||||
size_t end = string::npos;
|
||||
size_t commaLocation = message->find(COMMA, start);
|
||||
size_t end = string::npos;
|
||||
size_t commaLocation = message->find(COMMA, start);
|
||||
|
||||
if(commaLocation == string::npos)
|
||||
{
|
||||
end = message->find(CLOSE_BRACE, start);
|
||||
if(end == string::npos)
|
||||
{
|
||||
end = message->length();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = end - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = commaLocation - 1;
|
||||
}
|
||||
if(commaLocation == string::npos)
|
||||
{
|
||||
end = message->find(CLOSE_BRACE, start);
|
||||
if(end == string::npos)
|
||||
{
|
||||
end = message->length();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = end - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = commaLocation - 1;
|
||||
}
|
||||
|
||||
if(!GetString(start + 2, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
if(!GetString(start + 2, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
return end + 2;
|
||||
return end + 2;
|
||||
}
|
||||
|
||||
size_t ParserUtil::GetNextOCItemInList(const wstring* message, size_t start, wstring* result)
|
||||
{
|
||||
size_t end = message->find(OPEN_CURLY_BRACE, start) + 1;
|
||||
size_t end = message->find(OPEN_CURLY_BRACE, start) + 1;
|
||||
|
||||
int openBraceCount = 1;
|
||||
int openBraceCount = 1;
|
||||
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_CURLY_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_CURLY_BRACE, end);
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_CURLY_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_CURLY_BRACE, end);
|
||||
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
}
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
size_t length = end - start;
|
||||
|
||||
if(!GetString(start, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
if(!GetString(start, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
return end;
|
||||
return end;
|
||||
}
|
||||
|
||||
bool ParserUtil::GetValue(size_t start, const wstring* message, wstring* result)
|
||||
{
|
||||
if(message->at(start - 1) == '\"')
|
||||
{
|
||||
size_t end = message->find(QUOTE, start);
|
||||
return GetString(start, end, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = start - 1;
|
||||
if(message->at(start - 1) == '\"')
|
||||
{
|
||||
size_t end = message->find(QUOTE, start);
|
||||
return GetString(start, end, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = start - 1;
|
||||
|
||||
size_t end = message->find(COMMA, start);
|
||||
|
||||
result->append(message->substr(start, end-start));
|
||||
}
|
||||
size_t end = message->find(COMMA, start);
|
||||
|
||||
result->append(message->substr(start, end-start));
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::GetString(size_t start, size_t end, const wstring* message, wstring* result)
|
||||
{
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
size_t length = end - start;
|
||||
|
||||
if(length > 0)
|
||||
{
|
||||
result->append(message->substr(start, length));
|
||||
}
|
||||
else
|
||||
{
|
||||
result->append(L"");
|
||||
}
|
||||
if(length > 0)
|
||||
{
|
||||
result->append(message->substr(start, length));
|
||||
}
|
||||
else
|
||||
{
|
||||
result->append(L"");
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::IsList(wstring* message)
|
||||
{
|
||||
wchar_t c = message->at(0);
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
wchar_t c = message->at(0);
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseJsonList(wstring* message, vector<wstring*>* items)
|
||||
{
|
||||
|
||||
size_t currentLocation = message->find(OPEN_BRACE, 0);
|
||||
size_t currentLocation = message->find(OPEN_BRACE, 0);
|
||||
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
|
||||
currentLocation = ParserUtil::GetNextStringItemInList(message, currentLocation, item);
|
||||
currentLocation = ParserUtil::GetNextStringItemInList(message, currentLocation, item);
|
||||
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
items->push_back(item);
|
||||
}
|
||||
items->push_back(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseOCList(wstring* message, vector<wstring*>* items)
|
||||
{
|
||||
|
||||
size_t currentLocation = message->find(OPEN_CURLY_BRACE, 0);
|
||||
size_t currentLocation = message->find(OPEN_CURLY_BRACE, 0);
|
||||
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
|
||||
currentLocation = ParserUtil::GetNextOCItemInList(message, currentLocation, item);
|
||||
currentLocation = ParserUtil::GetNextOCItemInList(message, currentLocation, item);
|
||||
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
items->push_back(item);
|
||||
}
|
||||
items->push_back(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseOCMessageList(wstring* message, vector<OCMessage*>* messages)
|
||||
{
|
||||
vector<wstring*>* items = new vector<wstring*>();
|
||||
vector<wstring*>* items = new vector<wstring*>();
|
||||
|
||||
if(!ParseOCList(message, items))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!ParseOCList(message, items))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(vector<wstring*>::iterator it = items->begin(); it != items->end(); it++)
|
||||
{
|
||||
wstring* temp = *it;
|
||||
for(vector<wstring*>::iterator it = items->begin(); it != items->end(); it++)
|
||||
{
|
||||
wstring* temp = *it;
|
||||
|
||||
OCMessage* message = new OCMessage();
|
||||
message->InitFromMessage(temp);
|
||||
OCMessage* message = new OCMessage();
|
||||
message->InitFromMessage(temp);
|
||||
|
||||
messages->push_back(message);
|
||||
}
|
||||
messages->push_back(message);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeList(std::vector<std::wstring>* list, std::wstring* result, bool escapeQuotes)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_BRACE);
|
||||
result->append(OPEN_BRACE);
|
||||
|
||||
for(vector<wstring>::iterator it = list->begin(); it != list->end(); it++)
|
||||
{
|
||||
wstring value = *it;
|
||||
for(vector<wstring>::iterator it = list->begin(); it != list->end(); it++)
|
||||
{
|
||||
wstring value = *it;
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(value.c_str());
|
||||
result->append(QUOTE);
|
||||
result->append(value.c_str());
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COMMA);
|
||||
}
|
||||
result->append(QUOTE);
|
||||
result->append(COMMA);
|
||||
}
|
||||
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
|
||||
result->append(CLOSE_BRACE);
|
||||
result->append(CLOSE_BRACE);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeMessage(std::map<std::wstring*, std::wstring*>* arguments, std::wstring* result, bool escapeQuotes)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
|
||||
for(map<wstring*, wstring*>::iterator it = arguments->begin(); it != arguments->end(); it++)
|
||||
{
|
||||
wstring key = *it->first;
|
||||
wstring value = *it->second;
|
||||
for(map<wstring*, wstring*>::iterator it = arguments->begin(); it != arguments->end(); it++)
|
||||
{
|
||||
wstring key = *it->first;
|
||||
wstring value = *it->second;
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(key.c_str());
|
||||
result->append(QUOTE);
|
||||
result->append(key.c_str());
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COLON);
|
||||
result->append(value.c_str());
|
||||
result->append(COMMA);
|
||||
}
|
||||
result->append(QUOTE);
|
||||
result->append(COLON);
|
||||
result->append(value.c_str());
|
||||
result->append(COMMA);
|
||||
}
|
||||
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeMessage(OCMessage* OCMessage, std::wstring* result)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COMMAND);
|
||||
result->append(QUOTE);
|
||||
result->append(QUOTE);
|
||||
result->append(COMMAND);
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COLON);
|
||||
result->append(COLON);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(OCMessage->GetCommand()->c_str());
|
||||
result->append(QUOTE);
|
||||
result->append(QUOTE);
|
||||
result->append(OCMessage->GetCommand()->c_str());
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COMMA);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(VALUE);
|
||||
result->append(QUOTE);
|
||||
result->append(COMMA);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(VALUE);
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COLON);
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
|
||||
result->append(OCMessage->GetValue()->c_str());
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
result->append(COLON);
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
|
||||
result->append(OCMessage->GetValue()->c_str());
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,49 +22,49 @@ using namespace std;
|
||||
|
||||
bool RegistryUtil::ReadRegistry(const wchar_t* key, const wchar_t* name, int* result)
|
||||
{
|
||||
wstring* strResult = new wstring();
|
||||
wstring* strResult = new wstring();
|
||||
|
||||
if(!ReadRegistry(key, name, strResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!ReadRegistry(key, name, strResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = stoi( strResult->c_str() );
|
||||
*result = stoi( strResult->c_str() );
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegistryUtil::ReadRegistry(const wchar_t* key, const wchar_t* name, wstring* result)
|
||||
{
|
||||
HRESULT hResult;
|
||||
HRESULT hResult;
|
||||
|
||||
HKEY rootKey = NULL;
|
||||
HKEY rootKey = NULL;
|
||||
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CURRENT_USER, (LPCWSTR)key, NULL, KEY_READ, &rootKey));
|
||||
hResult = HRESULT_FROM_WIN32(RegOpenKeyEx(HKEY_CURRENT_USER, (LPCWSTR)key, NULL, KEY_READ, &rootKey));
|
||||
|
||||
if(!SUCCEEDED(hResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!SUCCEEDED(hResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t value[SIZE];
|
||||
DWORD value_length = SIZE;
|
||||
|
||||
wchar_t value[SIZE];
|
||||
DWORD value_length = SIZE;
|
||||
|
||||
hResult = RegQueryValueEx(rootKey, (LPCWSTR)name, NULL, NULL, (LPBYTE)value, &value_length );
|
||||
|
||||
if(!SUCCEEDED(hResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!SUCCEEDED(hResult))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(value);
|
||||
result->append(value);
|
||||
|
||||
HRESULT hResult2 = RegCloseKey(rootKey);
|
||||
HRESULT hResult2 = RegCloseKey(rootKey);
|
||||
|
||||
if (!SUCCEEDED(hResult2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!SUCCEEDED(hResult2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
class __declspec(dllexport) RegistryUtil
|
||||
{
|
||||
public:
|
||||
RegistryUtil();
|
||||
RegistryUtil();
|
||||
|
||||
~RegistryUtil();
|
||||
~RegistryUtil();
|
||||
|
||||
static bool ReadRegistry(const wchar_t*, const wchar_t*, int*);
|
||||
static bool ReadRegistry(const wchar_t*, const wchar_t*, std::wstring*);
|
||||
static bool ReadRegistry(const wchar_t*, const wchar_t*, int*);
|
||||
static bool ReadRegistry(const wchar_t*, const wchar_t*, std::wstring*);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -40,7 +40,7 @@ void RemotePathChecker::workerThreadLoop()
|
||||
std::unordered_set<std::wstring> asked;
|
||||
|
||||
while(!_stop) {
|
||||
Sleep(50);
|
||||
Sleep(50);
|
||||
|
||||
if (!connected) {
|
||||
asked.clear();
|
||||
@@ -72,33 +72,42 @@ void RemotePathChecker::workerThreadLoop()
|
||||
|
||||
std::wstring response;
|
||||
while (!_stop && socket.ReadLine(&response)) {
|
||||
if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) {
|
||||
wstring responsePath = response.substr(14); // length of REGISTER_PATH:
|
||||
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);
|
||||
}
|
||||
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
} else if (StringUtil::begins_with(response, wstring(L"UNREGISTER_PATH:"))) {
|
||||
auto sharedPtrCopy = atomic_load(&_watchedDirectories);
|
||||
auto vectorCopy = make_shared<vector<wstring>>(*sharedPtrCopy);
|
||||
vectorCopy->push_back(responsePath);
|
||||
atomic_store(&_watchedDirectories, shared_ptr<const vector<wstring>>(vectorCopy));
|
||||
|
||||
// We don't keep track of all files and can't know which file is currently visible
|
||||
// to the user, but at least reload the root dir so that any shortcut to the root
|
||||
// is updated without the user needing to refresh.
|
||||
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
} else if (StringUtil::begins_with(response, wstring(L"UNREGISTER_PATH:"))) {
|
||||
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());
|
||||
auto sharedPtrCopy = atomic_load(&_watchedDirectories);
|
||||
auto vectorCopy = make_shared<vector<wstring>>(*sharedPtrCopy);
|
||||
vectorCopy->erase(
|
||||
std::remove(vectorCopy->begin(), vectorCopy->end(), responsePath),
|
||||
vectorCopy->end());
|
||||
atomic_store(&_watchedDirectories, shared_ptr<const vector<wstring>>(vectorCopy));
|
||||
|
||||
vector<wstring> removedPaths;
|
||||
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||
// Remove any item from the cache
|
||||
for (auto it = _cache.begin(); it != _cache.end() ; ) {
|
||||
if (StringUtil::begins_with(it->first, responsePath)) {
|
||||
if (StringUtil::isDescendantOf(it->first, responsePath)) {
|
||||
removedPaths.emplace_back(move(it->first));
|
||||
it = _cache.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// Assume that we won't need this at this point, UNREGISTER_PATH is rare
|
||||
_oldCache.clear();
|
||||
}
|
||||
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
for (auto& path : removedPaths)
|
||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, path.data(), NULL);
|
||||
} else if (StringUtil::begins_with(response, wstring(L"STATUS:")) ||
|
||||
StringUtil::begins_with(response, wstring(L"BROADCAST:"))) {
|
||||
|
||||
@@ -116,57 +125,57 @@ void RemotePathChecker::workerThreadLoop()
|
||||
auto state = _StrToFileState(responseStatus);
|
||||
bool wasAsked = asked.erase(responsePath) > 0;
|
||||
|
||||
bool changed = false;
|
||||
bool updateView = false;
|
||||
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||
bool wasCached = _cache.find(responsePath) != _cache.end();
|
||||
if (wasAsked || wasCached) {
|
||||
auto &it = _cache[responsePath];
|
||||
changed = (it != state);
|
||||
it = state;
|
||||
auto it = _cache.find(responsePath);
|
||||
if (it == _cache.end()) {
|
||||
// The client only approximates requested files, if the bloom
|
||||
// filter becomes saturated after navigating multiple directories we'll start getting
|
||||
// status pushes that we never requested and fill our cache. Ignore those.
|
||||
if (!wasAsked) {
|
||||
continue;
|
||||
}
|
||||
it = _cache.insert(make_pair(responsePath, StateNone)).first;
|
||||
}
|
||||
|
||||
updateView = it->second != state;
|
||||
it->second = state;
|
||||
}
|
||||
if (changed) {
|
||||
if (updateView) {
|
||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
}
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"UPDATE_VIEW"))) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
// 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);
|
||||
lock.unlock();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (socket.Event() == INVALID_HANDLE_VALUE) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cache.clear();
|
||||
_oldCache.clear();
|
||||
_watchedDirectories.clear();
|
||||
_connected = connected = false;
|
||||
}
|
||||
if (socket.Event() == INVALID_HANDLE_VALUE) {
|
||||
atomic_store(&_watchedDirectories, make_shared<const vector<wstring>>());
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_connected = connected = false;
|
||||
|
||||
if (_stop) return;
|
||||
// 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);
|
||||
lock.unlock();
|
||||
// Let explorer know about each invalidated cache entry that needs to get its icon removed.
|
||||
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, it->first.data(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE handles[2] = { _newQueries, socket.Event() };
|
||||
WaitForMultipleObjects(2, handles, false, 0);
|
||||
if (_stop) return;
|
||||
|
||||
HANDLE handles[2] = { _newQueries, socket.Event() };
|
||||
WaitForMultipleObjects(2, handles, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
RemotePathChecker::RemotePathChecker()
|
||||
: _connected(false)
|
||||
: _watchedDirectories(make_shared<const vector<wstring>>())
|
||||
, _connected(false)
|
||||
, _newQueries(CreateEvent(NULL, true, true, NULL))
|
||||
, _thread([this]{ this->workerThreadLoop(); })
|
||||
, _thread([this]{ this->workerThreadLoop(); })
|
||||
{
|
||||
}
|
||||
|
||||
@@ -179,10 +188,9 @@ RemotePathChecker::~RemotePathChecker()
|
||||
CloseHandle(_newQueries);
|
||||
}
|
||||
|
||||
vector<wstring> RemotePathChecker::WatchedDirectories()
|
||||
std::shared_ptr<const std::vector<std::wstring>> RemotePathChecker::WatchedDirectories() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
return _watchedDirectories;
|
||||
return atomic_load(&_watchedDirectories);
|
||||
}
|
||||
|
||||
bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
||||
@@ -198,44 +206,39 @@ bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
||||
|
||||
auto it = _cache.find(path);
|
||||
if (it != _cache.end()) {
|
||||
// The path is in our cache, and we'll get updates pushed if the status changes.
|
||||
*state = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Re-request the status while we display what we have in _oldCache
|
||||
_pending.push(filePath);
|
||||
|
||||
it = _oldCache.find(path);
|
||||
bool foundInOldCache = it != _oldCache.end();
|
||||
if (foundInOldCache)
|
||||
*state = it->second;
|
||||
|
||||
lock.unlock();
|
||||
SetEvent(_newQueries);
|
||||
return foundInOldCache;
|
||||
return false;
|
||||
}
|
||||
|
||||
RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstring &str)
|
||||
{
|
||||
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 StateSync;
|
||||
} 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 StateWarning;
|
||||
} else if (str == L"ERROR") {
|
||||
return StateError;
|
||||
} else if (str == L"ERROR+SWM") {
|
||||
return StateError;
|
||||
}
|
||||
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 StateSync;
|
||||
} 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 StateWarning;
|
||||
} else if (str == L"ERROR") {
|
||||
return StateError;
|
||||
} else if (str == L"ERROR+SWM") {
|
||||
return StateError;
|
||||
}
|
||||
|
||||
return StateNone;
|
||||
return StateNone;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
@@ -27,21 +28,21 @@
|
||||
|
||||
class __declspec(dllexport) RemotePathChecker {
|
||||
public:
|
||||
enum FileState {
|
||||
// Order synced with OCOverlay
|
||||
StateError = 0,
|
||||
StateOk, StateOkSWM,
|
||||
StateSync,
|
||||
StateWarning,
|
||||
StateNone
|
||||
};
|
||||
RemotePathChecker();
|
||||
enum FileState {
|
||||
// Order synced with OCOverlay
|
||||
StateError = 0,
|
||||
StateOk, StateOkSWM,
|
||||
StateSync,
|
||||
StateWarning,
|
||||
StateNone
|
||||
};
|
||||
RemotePathChecker();
|
||||
~RemotePathChecker();
|
||||
std::vector<std::wstring> WatchedDirectories();
|
||||
bool IsMonitoredPath(const wchar_t* filePath, int* state);
|
||||
std::shared_ptr<const std::vector<std::wstring>> WatchedDirectories() const;
|
||||
bool IsMonitoredPath(const wchar_t* filePath, int* state);
|
||||
|
||||
private:
|
||||
FileState _StrToFileState(const std::wstring &str);
|
||||
FileState _StrToFileState(const std::wstring &str);
|
||||
std::mutex _mutex;
|
||||
std::atomic<bool> _stop;
|
||||
|
||||
@@ -52,8 +53,9 @@ private:
|
||||
std::queue<std::wstring> _pending;
|
||||
|
||||
std::unordered_map<std::wstring, FileState> _cache;
|
||||
std::unordered_map<std::wstring, FileState> _oldCache;
|
||||
std::vector<std::wstring> _watchedDirectories;
|
||||
// The vector is const since it will be accessed from multiple threads through OCOverlay::IsMemberOf.
|
||||
// Each modification needs to be made onto a copy and then atomically replaced in the shared_ptr.
|
||||
std::shared_ptr<const std::vector<std::wstring>> _watchedDirectories;
|
||||
bool _connected;
|
||||
|
||||
|
||||
@@ -61,7 +63,7 @@ private:
|
||||
//std::condition_variable _newQueries;
|
||||
HANDLE _newQueries;
|
||||
|
||||
std::thread _thread;
|
||||
std::thread _thread;
|
||||
void workerThreadLoop();
|
||||
};
|
||||
|
||||
|
||||
@@ -21,18 +21,18 @@
|
||||
|
||||
std::string StringUtil::toUtf8(const wchar_t *utf16, int len)
|
||||
{
|
||||
if (len < 0) {
|
||||
len = (int) wcslen(utf16);
|
||||
}
|
||||
if (len < 0) {
|
||||
len = (int) wcslen(utf16);
|
||||
}
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
|
||||
return converter.to_bytes(utf16, utf16+len);
|
||||
}
|
||||
|
||||
std::wstring StringUtil::toUtf16(const char *utf8, int len)
|
||||
{
|
||||
if (len < 0) {
|
||||
len = (int) strlen(utf8);
|
||||
}
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
|
||||
if (len < 0) {
|
||||
len = (int) strlen(utf8);
|
||||
}
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
|
||||
return converter.from_bytes(utf8, utf8+len);
|
||||
}
|
||||
|
||||
@@ -20,15 +20,30 @@
|
||||
|
||||
class __declspec(dllexport) StringUtil {
|
||||
public:
|
||||
static std::string toUtf8(const wchar_t* utf16, int len = -1);
|
||||
static std::wstring toUtf16(const char* utf8, int len = -1);
|
||||
static std::string toUtf8(const wchar_t* utf16, int len = -1);
|
||||
static std::wstring toUtf16(const char* utf8, int len = -1);
|
||||
|
||||
template<class T>
|
||||
static bool begins_with(const T& input, const T& match)
|
||||
{
|
||||
return input.size() >= match.size()
|
||||
&& std::equal(match.begin(), match.end(), input.begin());
|
||||
}
|
||||
template<class T>
|
||||
static bool begins_with(const T& input, const T& match)
|
||||
{
|
||||
return input.size() >= match.size()
|
||||
&& std::equal(match.begin(), match.end(), input.begin());
|
||||
}
|
||||
|
||||
static bool isDescendantOf(const std::wstring& child, const std::wstring& parent) {
|
||||
return isDescendantOf(child.c_str(), child.size(), parent.c_str(), parent.size());
|
||||
}
|
||||
|
||||
static bool isDescendantOf(PCWSTR child, size_t childLength, const std::wstring& parent) {
|
||||
return isDescendantOf(child, childLength, parent.c_str(), parent.size());
|
||||
}
|
||||
|
||||
static bool isDescendantOf(PCWSTR child, size_t childLength, PCWSTR parent, size_t parentLength) {
|
||||
if (!parentLength)
|
||||
return false;
|
||||
return (childLength == parentLength || childLength > parentLength && (child[parentLength] == L'\\' || child[parentLength - 1] == L'\\'))
|
||||
&& wcsncmp(child, parent, parentLength) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // STRINGUTIL_H
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// This is the number that will end up in the version window of the DLLs.
|
||||
// Increment this version before committing a new build if you are today's shell_integration build master.
|
||||
#define OCEXT_BUILD_NUM 43
|
||||
#define OCEXT_BUILD_NUM 44
|
||||
|
||||
#define STRINGIZE2(s) #s
|
||||
#define STRINGIZE(s) STRINGIZE2(s)
|
||||
|
||||
externo
+6941
-4159
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
externo
+125
-29
@@ -108,7 +108,8 @@ extern "C" {
|
||||
** be held constant and Z will be incremented or else Y will be incremented
|
||||
** and Z will be reset to zero.
|
||||
**
|
||||
** Since version 3.6.18, SQLite source code has been stored in the
|
||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||
** SQLite source code has been stored in the
|
||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||
** a string which identifies a particular check-in of SQLite
|
||||
@@ -120,13 +121,13 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.14.2"
|
||||
#define SQLITE_VERSION_NUMBER 3014002
|
||||
#define SQLITE_SOURCE_ID "2016-09-12 18:50:49 29dbef4b8585f753861a36d6dd102ca634197bd6"
|
||||
#define SQLITE_VERSION "3.16.1"
|
||||
#define SQLITE_VERSION_NUMBER 3016001
|
||||
#define SQLITE_SOURCE_ID "2017-01-03 18:27:03 979f04392853b8053817a3eea2fc679947b437fd"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
** KEYWORDS: sqlite3_version, sqlite3_sourceid
|
||||
** KEYWORDS: sqlite3_version sqlite3_sourceid
|
||||
**
|
||||
** These interfaces provide the same information as the [SQLITE_VERSION],
|
||||
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
|
||||
@@ -452,7 +453,8 @@ SQLITE_API int sqlite3_exec(
|
||||
** [result codes]. However, experience has shown that many of
|
||||
** these result codes are too coarse-grained. They do not provide as
|
||||
** much information about problems as programmers might like. In an effort to
|
||||
** address this, newer versions of SQLite (version 3.3.8 and later) include
|
||||
** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
|
||||
** and later) include
|
||||
** support for additional result codes that provide more detailed information
|
||||
** about errors. These [extended result codes] are enabled or disabled
|
||||
** on a per database connection basis using the
|
||||
@@ -976,6 +978,12 @@ struct sqlite3_io_methods {
|
||||
** on whether or not the file has been renamed, moved, or deleted since it
|
||||
** was first opened.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
|
||||
** underlying native file handle associated with a file handle. This file
|
||||
** control interprets its argument as a pointer to a native file handle and
|
||||
** writes the resulting value there.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
|
||||
** opcode causes the xFileControl method to swap the file handle with the one
|
||||
@@ -1026,6 +1034,8 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_RBU 26
|
||||
#define SQLITE_FCNTL_VFS_POINTER 27
|
||||
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
||||
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
||||
#define SQLITE_FCNTL_PDB 30
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
@@ -1969,13 +1979,36 @@ struct sqlite3_mem_methods {
|
||||
** be a NULL pointer, in which case the new setting is not reported back.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
||||
** <dd> ^This option is used to change the name of the "main" database
|
||||
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
||||
** which will become the new schema name in place of "main". ^SQLite
|
||||
** does not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into this DBCONFIG option is unchanged
|
||||
** until after the database connection closes.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
|
||||
** <dd> Usually, when a database in wal mode is closed or detached from a
|
||||
** database handle, SQLite checks if this will mean that there are now no
|
||||
** connections at all to the database. If so, it performs a checkpoint
|
||||
** operation before closing the connection. This option may be used to
|
||||
** override this behaviour. The first parameter passed to this operation
|
||||
** is an integer - non-zero to disable checkpoints-on-close, or zero (the
|
||||
** default) to enable them. The second parameter is a pointer to an integer
|
||||
** into which is written 0 or 1 to indicate whether checkpoints-on-close
|
||||
** have been disabled - 0 if they are not disabled, 1 if they are.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
|
||||
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
|
||||
|
||||
|
||||
/*
|
||||
@@ -3577,6 +3610,10 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
|
||||
** sqlite3_stmt_readonly() to return true since, while those statements
|
||||
** change the configuration of a database connection, they do not make
|
||||
** changes to the content of the database files on disk.
|
||||
** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since
|
||||
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
|
||||
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
|
||||
** sqlite3_stmt_readonly() returns false for those commands.
|
||||
*/
|
||||
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||
|
||||
@@ -4041,7 +4078,8 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||
** sqlite3_step(). Failure to reset the prepared statement using
|
||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||
** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
||||
** sqlite3_step() began
|
||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||
** break because any application that ever receives an SQLITE_MISUSE error
|
||||
@@ -5404,7 +5442,8 @@ SQLITE_API void *sqlite3_update_hook(
|
||||
** and disabled if the argument is false.)^
|
||||
**
|
||||
** ^Cache sharing is enabled and disabled for an entire process.
|
||||
** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
|
||||
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
|
||||
** In prior versions of SQLite,
|
||||
** sharing was enabled or disabled for each thread separately.
|
||||
**
|
||||
** ^(The cache sharing mode set by this interface effects all subsequent
|
||||
@@ -5498,7 +5537,8 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
|
||||
** from the heap.
|
||||
** </ul>)^
|
||||
**
|
||||
** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
|
||||
** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
|
||||
** the soft heap limit is enforced
|
||||
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
|
||||
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
|
||||
** the soft heap limit is enforced on every memory allocation. Without
|
||||
@@ -5892,13 +5932,15 @@ struct sqlite3_module {
|
||||
** the xUpdate method are automatically rolled back by SQLite.
|
||||
**
|
||||
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
|
||||
** structure for SQLite version 3.8.2. If a virtual table extension is
|
||||
** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
|
||||
** If a virtual table extension is
|
||||
** used with an SQLite version earlier than 3.8.2, the results of attempting
|
||||
** to read or write the estimatedRows field are undefined (but are likely
|
||||
** to included crashing the application). The estimatedRows field should
|
||||
** therefore only be used if [sqlite3_libversion_number()] returns a
|
||||
** value greater than or equal to 3008002. Similarly, the idxFlags field
|
||||
** was added for version 3.9.0. It may therefore only be used if
|
||||
** was added for [version 3.9.0] ([dateof:3.9.0]).
|
||||
** It may therefore only be used if
|
||||
** sqlite3_libversion_number() returns a value greater than or equal to
|
||||
** 3009000.
|
||||
*/
|
||||
@@ -6596,7 +6638,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
|
||||
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
|
||||
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
|
||||
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
|
||||
#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
|
||||
#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
|
||||
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
|
||||
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
|
||||
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
|
||||
@@ -6700,6 +6742,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
|
||||
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
|
||||
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
|
||||
#define SQLITE_TESTCTRL_BYTEORDER 22
|
||||
@@ -8186,7 +8229,8 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
|
||||
**
|
||||
** See also: [sqlite3_update_hook()]
|
||||
*/
|
||||
SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
|
||||
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
|
||||
SQLITE_API void *sqlite3_preupdate_hook(
|
||||
sqlite3 *db,
|
||||
void(*xPreUpdate)(
|
||||
void *pCtx, /* Copy of third arg to preupdate_hook() */
|
||||
@@ -8199,10 +8243,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
|
||||
),
|
||||
void*
|
||||
);
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *);
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
||||
SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
|
||||
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
|
||||
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
|
||||
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** CAPI3REF: Low-level system error code
|
||||
@@ -8218,7 +8263,7 @@ SQLITE_API int sqlite3_system_errno(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Snapshot
|
||||
** KEYWORDS: {snapshot}
|
||||
** KEYWORDS: {snapshot} {sqlite3_snapshot}
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** An instance of the snapshot object records the state of a [WAL mode]
|
||||
@@ -8242,7 +8287,9 @@ SQLITE_API int sqlite3_system_errno(sqlite3*);
|
||||
** to an historical snapshot (if possible). The destructor for
|
||||
** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
|
||||
*/
|
||||
typedef struct sqlite3_snapshot sqlite3_snapshot;
|
||||
typedef struct sqlite3_snapshot {
|
||||
unsigned char hidden[48];
|
||||
} sqlite3_snapshot;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Record A Database Snapshot
|
||||
@@ -8253,9 +8300,32 @@ typedef struct sqlite3_snapshot sqlite3_snapshot;
|
||||
** schema S in database connection D. ^On success, the
|
||||
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
|
||||
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
|
||||
** ^If schema S of [database connection] D is not a [WAL mode] database
|
||||
** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
|
||||
** leaves the *P value unchanged and returns an appropriate [error code].
|
||||
** If there is not already a read-transaction open on schema S when
|
||||
** this function is called, one is opened automatically.
|
||||
**
|
||||
** The following must be true for this function to succeed. If any of
|
||||
** the following statements are false when sqlite3_snapshot_get() is
|
||||
** called, SQLITE_ERROR is returned. The final value of *P is undefined
|
||||
** in this case.
|
||||
**
|
||||
** <ul>
|
||||
** <li> The database handle must be in [autocommit mode].
|
||||
**
|
||||
** <li> Schema S of [database connection] D must be a [WAL mode] database.
|
||||
**
|
||||
** <li> There must not be a write transaction open on schema S of database
|
||||
** connection D.
|
||||
**
|
||||
** <li> One or more transactions must have been written to the current wal
|
||||
** file since it was created on disk (by any connection). This means
|
||||
** that a snapshot cannot be taken on a wal mode database with no wal
|
||||
** file immediately after it is first opened. At least one transaction
|
||||
** must be written to it first.
|
||||
** </ul>
|
||||
**
|
||||
** This function may also return SQLITE_NOMEM. If it is called with the
|
||||
** database handle in autocommit mode but fails for some other reason,
|
||||
** whether or not a read transaction is opened on schema S is undefined.
|
||||
**
|
||||
** The [sqlite3_snapshot] object returned from a successful call to
|
||||
** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
|
||||
@@ -8348,6 +8418,28 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
||||
sqlite3_snapshot *p2
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Recover snapshots from a wal file
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** If all connections disconnect from a database file but do not perform
|
||||
** a checkpoint, the existing wal file is opened along with the database
|
||||
** file the next time the database is opened. At this point it is only
|
||||
** possible to successfully call sqlite3_snapshot_open() to open the most
|
||||
** recent snapshot of the database (the one at the head of the wal file),
|
||||
** even though the wal file may contain other valid snapshots for which
|
||||
** clients have sqlite3_snapshot handles.
|
||||
**
|
||||
** This function attempts to scan the wal file associated with database zDb
|
||||
** of database handle db and make all valid snapshots available to
|
||||
** sqlite3_snapshot_open(). It is an error if there is already a read
|
||||
** transaction open on the database, or if the database is not a wal mode
|
||||
** database.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
*/
|
||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
||||
|
||||
/*
|
||||
** Undo the hack that converts floating point types to integer for
|
||||
** builds on processors without floating point support.
|
||||
@@ -8639,7 +8731,7 @@ int sqlite3session_attach(
|
||||
** CAPI3REF: Set a table filter on a Session Object.
|
||||
**
|
||||
** The second argument (xFilter) is the "filter callback". For changes to rows
|
||||
** in tables that are not attached to the Session oject, the filter is called
|
||||
** in tables that are not attached to the Session object, the filter is called
|
||||
** to determine whether changes to the table's rows should be tracked or not.
|
||||
** If xFilter returns 0, changes is not tracked. Note that once a table is
|
||||
** attached, xFilter will not be called again.
|
||||
@@ -8905,7 +8997,7 @@ int sqlite3session_isempty(sqlite3_session *pSession);
|
||||
** [sqlite3changeset_invert()] functions, all changes within the changeset
|
||||
** that apply to a single table are grouped together. This means that when
|
||||
** an application iterates through a changeset using an iterator created by
|
||||
** this function, all changes that relate to a single table are visted
|
||||
** this function, all changes that relate to a single table are visited
|
||||
** consecutively. There is no chance that the iterator will visit a change
|
||||
** the applies to table X, then one for table Y, and then later on visit
|
||||
** another change for table X.
|
||||
@@ -8992,7 +9084,7 @@ int sqlite3changeset_op(
|
||||
** 0x01 if the corresponding column is part of the tables primary key, or
|
||||
** 0x00 if it is not.
|
||||
**
|
||||
** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
|
||||
** If argument pnCol is not NULL, then *pnCol is set to the number of columns
|
||||
** in the table.
|
||||
**
|
||||
** If this function is called when the iterator does not point to a valid
|
||||
@@ -9209,12 +9301,12 @@ int sqlite3changeset_concat(
|
||||
|
||||
|
||||
/*
|
||||
** Changegroup handle.
|
||||
** CAPI3REF: Changegroup Handle
|
||||
*/
|
||||
typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Combine two or more changesets into a single changeset.
|
||||
** CAPI3REF: Create A New Changegroup Object
|
||||
**
|
||||
** An sqlite3_changegroup object is used to combine two or more changesets
|
||||
** (or patchsets) into a single changeset (or patchset). A single changegroup
|
||||
@@ -9251,6 +9343,8 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add A Changeset To A Changegroup
|
||||
**
|
||||
** Add all changes within the changeset (or patchset) in buffer pData (size
|
||||
** nData bytes) to the changegroup.
|
||||
**
|
||||
@@ -9265,7 +9359,7 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
** apply to the same row as a change already present in the changegroup if
|
||||
** the two rows have the same primary key.
|
||||
**
|
||||
** Changes to rows that that do not already appear in the changegroup are
|
||||
** Changes to rows that do not already appear in the changegroup are
|
||||
** simply copied into it. Or, if both the new changeset and the changegroup
|
||||
** contain changes that apply to a single row, the final contents of the
|
||||
** changegroup depends on the type of each change, as follows:
|
||||
@@ -9326,6 +9420,8 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp);
|
||||
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
||||
**
|
||||
** Obtain a buffer containing a changeset (or patchset) representing the
|
||||
** current contents of the changegroup. If the inputs to the changegroup
|
||||
** were themselves changesets, the output is a changeset. Or, if the
|
||||
@@ -9354,7 +9450,7 @@ int sqlite3changegroup_output(
|
||||
);
|
||||
|
||||
/*
|
||||
** Delete a changegroup object.
|
||||
** CAPI3REF: Delete A Changegroup Object
|
||||
*/
|
||||
void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
project(cmd)
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
|
||||
set(cmd_NAME ${APPLICATION_EXECUTABLE}cmd)
|
||||
set(cmd_SRC
|
||||
cmd.cpp
|
||||
|
||||
@@ -3,6 +3,8 @@ set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
add_subdirectory(updater)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
|
||||
#TODO Move resources files
|
||||
qt_add_resources(MIRALL_RC_SRC ../../client.qrc)
|
||||
if ( IS_DIRECTORY ${OEM_THEME_DIR} )
|
||||
|
||||
@@ -235,6 +235,9 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
|
||||
} else {
|
||||
acc->setUrl(urlConfig.toUrl());
|
||||
}
|
||||
|
||||
qDebug() << "Account for" << acc->url() << "using auth type" << authType;
|
||||
|
||||
acc->_serverVersion = settings.value(QLatin1String(serverVersionC)).toString();
|
||||
|
||||
// We want to only restore settings for that auth type and the user value
|
||||
|
||||
@@ -46,8 +46,6 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
const char oldJournalPath[] = ".csync_journal.db";
|
||||
|
||||
Folder::Folder(const FolderDefinition& definition,
|
||||
AccountState* accountState,
|
||||
QObject* parent)
|
||||
|
||||
@@ -115,7 +115,7 @@ void FolderMan::unloadFolder( Folder *f )
|
||||
disconnect(f, SIGNAL(syncPausedChanged(Folder*,bool)),
|
||||
this, SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
disconnect(&f->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
_socketApi.data(), SLOT(broadcastStatusPushMessage(const QString &, SyncFileStatus)));
|
||||
disconnect(f, SIGNAL(watchedFileChangedExternally(QString)),
|
||||
&f->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
|
||||
}
|
||||
@@ -938,7 +938,7 @@ Folder* FolderMan::addFolderInternal(FolderDefinition folderDefinition,
|
||||
connect(folder, SIGNAL(syncPausedChanged(Folder*,bool)), SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
connect(folder, SIGNAL(canSyncChanged()), SLOT(slotFolderCanSyncChanged()));
|
||||
connect(&folder->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
_socketApi.data(), SLOT(broadcastStatusPushMessage(const QString &, SyncFileStatus)));
|
||||
connect(folder, SIGNAL(watchedFileChangedExternally(QString)),
|
||||
&folder->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
|
||||
|
||||
|
||||
@@ -482,8 +482,9 @@ void FolderWizardRemotePath::showWarn( const QString& msg ) const
|
||||
FolderWizardSelectiveSync::FolderWizardSelectiveSync(const AccountPtr& account)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
_selectiveSync = new SelectiveSyncWidget(account, this);
|
||||
layout->addWidget(_selectiveSync);
|
||||
_treeView = new SelectiveSyncTreeView(account, this);
|
||||
layout->addWidget(new QLabel(tr("Choose What to Sync: You can optionally deselect remote subfolders you do not wish to synchronize.")));
|
||||
layout->addWidget(_treeView);
|
||||
}
|
||||
|
||||
FolderWizardSelectiveSync::~FolderWizardSelectiveSync()
|
||||
@@ -500,17 +501,13 @@ void FolderWizardSelectiveSync::initializePage()
|
||||
QString alias = QFileInfo(targetPath).fileName();
|
||||
if (alias.isEmpty())
|
||||
alias = Theme::instance()->appName();
|
||||
QStringList initialBlacklist;
|
||||
if (Theme::instance()->wizardSelectiveSyncDefaultNothing()) {
|
||||
initialBlacklist = QStringList("/");
|
||||
}
|
||||
_selectiveSync->setFolderInfo(targetPath, alias, initialBlacklist);
|
||||
_treeView->setFolderInfo(targetPath, alias);
|
||||
QWizardPage::initializePage();
|
||||
}
|
||||
|
||||
bool FolderWizardSelectiveSync::validatePage()
|
||||
{
|
||||
wizard()->setProperty("selectiveSyncBlackList", QVariant(_selectiveSync->createBlackList()));
|
||||
wizard()->setProperty("selectiveSyncBlackList", QVariant(_treeView->createBlackList()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -520,7 +517,7 @@ void FolderWizardSelectiveSync::cleanupPage()
|
||||
QString alias = QFileInfo(targetPath).fileName();
|
||||
if (alias.isEmpty())
|
||||
alias = Theme::instance()->appName();
|
||||
_selectiveSync->setFolderInfo(targetPath, alias);
|
||||
_treeView->setFolderInfo(targetPath, alias);
|
||||
QWizardPage::cleanupPage();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class SelectiveSyncWidget;
|
||||
class SelectiveSyncTreeView;
|
||||
|
||||
class ownCloudInfo;
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
virtual void cleanupPage() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
SelectiveSyncWidget *_selectiveSync;
|
||||
SelectiveSyncTreeView *_treeView;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ void GeneralSettings::loadMiscSettings()
|
||||
|
||||
void GeneralSettings::slotUpdateInfo()
|
||||
{
|
||||
OCUpdater *updater = dynamic_cast<OCUpdater*>(Updater::instance());
|
||||
// Note: the sparkle-updater is not an OCUpdater
|
||||
OCUpdater *updater = qobject_cast<OCUpdater*>(Updater::instance());
|
||||
if (ConfigFile().skipUpdateCheck()) {
|
||||
updater = 0; // don't show update info if updates are disabled
|
||||
}
|
||||
|
||||
+2
-1
@@ -107,7 +107,8 @@ int main(int argc, char **argv)
|
||||
// if handleStartup returns true, main()
|
||||
// needs to terminate here, e.g. because
|
||||
// the updater is triggered
|
||||
if (Updater::instance()->handleStartup()) {
|
||||
Updater *updater = Updater::instance();
|
||||
if ( updater && updater->handleStartup()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "networkjobs.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include "configfile.h"
|
||||
#include <QDialogButtonBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeWidget>
|
||||
@@ -30,7 +29,6 @@
|
||||
#include <QScopedValueRollback>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -56,48 +54,32 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
SelectiveSyncWidget::SelectiveSyncWidget(AccountPtr account, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, _account(account)
|
||||
, _inserting(false)
|
||||
, _folderTree(new QTreeWidget(this))
|
||||
SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent)
|
||||
: QTreeWidget(parent), _inserting(false), _account(account)
|
||||
{
|
||||
_loading = new QLabel(tr("Loading ..."), _folderTree);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto header = new QLabel(this);
|
||||
header->setText(tr("Deselect remote folders you do not wish to synchronize."));
|
||||
header->setWordWrap(true);
|
||||
layout->addWidget(header);
|
||||
|
||||
layout->addWidget(_folderTree);
|
||||
|
||||
connect(_folderTree, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||
SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
connect(_folderTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
|
||||
SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||
_folderTree->setSortingEnabled(true);
|
||||
_folderTree->sortByColumn(0, Qt::AscendingOrder);
|
||||
_folderTree->setColumnCount(2);
|
||||
_loading = new QLabel(tr("Loading ..."), this);
|
||||
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
|
||||
setSortingEnabled(true);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
setColumnCount(2);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
_folderTree->header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
|
||||
_folderTree->header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
|
||||
header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
|
||||
header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
|
||||
#else
|
||||
_folderTree->header()->resizeSection(0, sizeHint().width()/2);
|
||||
header()->resizeSection(0, sizeHint().width()/2);
|
||||
#endif
|
||||
_folderTree->header()->setStretchLastSection(true);
|
||||
_folderTree->headerItem()->setText(0, tr("Name"));
|
||||
_folderTree->headerItem()->setText(1, tr("Size"));
|
||||
header()->setStretchLastSection(true);
|
||||
headerItem()->setText(0, tr("Name"));
|
||||
headerItem()->setText(1, tr("Size"));
|
||||
}
|
||||
|
||||
QSize SelectiveSyncWidget::sizeHint() const
|
||||
QSize SelectiveSyncTreeView::sizeHint() const
|
||||
{
|
||||
return QWidget::sizeHint().expandedTo(QSize(600, 600));
|
||||
return QTreeView::sizeHint().expandedTo(QSize(400, 400));
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::refreshFolders()
|
||||
void SelectiveSyncTreeView::refreshFolders()
|
||||
{
|
||||
LsColJob *job = new LsColJob(_account, _folderPath, this);
|
||||
job->setProperties(QList<QByteArray>() << "resourcetype" << "http://owncloud.org/ns:size");
|
||||
@@ -106,12 +88,12 @@ void SelectiveSyncWidget::refreshFolders()
|
||||
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
|
||||
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
|
||||
job->start();
|
||||
_folderTree->clear();
|
||||
clear();
|
||||
_loading->show();
|
||||
_loading->move(10, _folderTree->header()->height() + 10);
|
||||
_loading->move(10,header()->height() + 10);
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
|
||||
void SelectiveSyncTreeView::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
|
||||
{
|
||||
_folderPath = folderPath;
|
||||
if (_folderPath.startsWith(QLatin1Char('/'))) {
|
||||
@@ -134,7 +116,7 @@ static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& t
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
|
||||
void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
@@ -177,13 +159,13 @@ void SelectiveSyncWidget::recursiveInsert(QTreeWidgetItem* parent, QStringList p
|
||||
}
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
|
||||
void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
||||
{
|
||||
auto job = qobject_cast<LsColJob *>(sender());
|
||||
QScopedValueRollback<bool> isInserting(_inserting);
|
||||
_inserting = true;
|
||||
|
||||
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(_folderTree->topLevelItem(0));
|
||||
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(topLevelItem(0));
|
||||
|
||||
QUrl url = _account->davUrl();
|
||||
QString pathToRemove = url.path();
|
||||
@@ -224,11 +206,15 @@ void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
root = new SelectiveSyncTreeViewItem(_folderTree);
|
||||
root = new SelectiveSyncTreeViewItem(this);
|
||||
root->setText(0, _rootName);
|
||||
root->setIcon(0, Theme::instance()->applicationIcon());
|
||||
root->setData(0, Qt::UserRole, QString());
|
||||
root->setCheckState(0, Qt::Checked);
|
||||
if (_oldBlackList.isEmpty()) {
|
||||
root->setCheckState(0, Qt::Checked);
|
||||
} else {
|
||||
root->setCheckState(0, Qt::PartiallyChecked);
|
||||
}
|
||||
qint64 size = job ? job->_sizes.value(pathToRemove, -1) : -1;
|
||||
if (size >= 0) {
|
||||
root->setText(1, Utility::octetsToString(size));
|
||||
@@ -250,19 +236,10 @@ void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
|
||||
recursiveInsert(root, paths, path, size);
|
||||
}
|
||||
|
||||
// Root is partially checked if any children are not checked
|
||||
for (int i = 0; i < root->childCount(); ++i) {
|
||||
const auto child = root->child(i);
|
||||
if (child->checkState(0) != Qt::Checked) {
|
||||
root->setCheckState(0, Qt::PartiallyChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
root->setExpanded(true);
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
void SelectiveSyncTreeView::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
{
|
||||
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
||||
_loading->setText(tr("No subfolders currently on the server."));
|
||||
@@ -272,7 +249,7 @@ void SelectiveSyncWidget::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
_loading->resize(_loading->sizeHint()); // because it's not in a layout
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::slotItemExpanded(QTreeWidgetItem *item)
|
||||
void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
QString dir = item->data(0, Qt::UserRole).toString();
|
||||
if (dir.isEmpty()) return;
|
||||
@@ -287,7 +264,7 @@ void SelectiveSyncWidget::slotItemExpanded(QTreeWidgetItem *item)
|
||||
job->start();
|
||||
}
|
||||
|
||||
void SelectiveSyncWidget::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
{
|
||||
if (col != 0 || _inserting)
|
||||
return;
|
||||
@@ -345,10 +322,10 @@ void SelectiveSyncWidget::slotItemChanged(QTreeWidgetItem *item, int col)
|
||||
}
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncWidget::createBlackList(QTreeWidgetItem* root) const
|
||||
QStringList SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
|
||||
{
|
||||
if (!root) {
|
||||
root = _folderTree->topLevelItem(0);
|
||||
root = topLevelItem(0);
|
||||
}
|
||||
if (!root) return QStringList();
|
||||
|
||||
@@ -377,15 +354,15 @@ QStringList SelectiveSyncWidget::createBlackList(QTreeWidgetItem* root) const
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncWidget::oldBlackList() const
|
||||
QStringList SelectiveSyncTreeView::oldBlackList() const
|
||||
{
|
||||
return _oldBlackList;
|
||||
}
|
||||
|
||||
qint64 SelectiveSyncWidget::estimatedSize(QTreeWidgetItem* root)
|
||||
qint64 SelectiveSyncTreeView::estimatedSize(QTreeWidgetItem* root)
|
||||
{
|
||||
if (!root) {
|
||||
root = _folderTree->topLevelItem(0);
|
||||
root = topLevelItem(0);
|
||||
}
|
||||
if (!root) return -1;
|
||||
|
||||
@@ -419,10 +396,10 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, Folder* folder, QWi
|
||||
_okButton(0) // defined in init()
|
||||
{
|
||||
bool ok;
|
||||
init(account);
|
||||
init(account, tr("Unchecked folders will be <b>removed</b> from your local file system and will not be synchronized to this computer anymore"));
|
||||
QStringList selectiveSyncList = _folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok);
|
||||
if( ok ) {
|
||||
_selectiveSync->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
|
||||
_treeView->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
|
||||
} else {
|
||||
_okButton->setEnabled(false);
|
||||
}
|
||||
@@ -434,16 +411,22 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, const QString &fold
|
||||
const QStringList& blacklist, QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f), _folder(0)
|
||||
{
|
||||
init(account);
|
||||
_selectiveSync->setFolderInfo(folder, folder, blacklist);
|
||||
init(account,
|
||||
Theme::instance()->wizardSelectiveSyncDefaultNothing() ?
|
||||
tr("Choose What to Sync: Select remote subfolders you wish to synchronize.") :
|
||||
tr("Choose What to Sync: Deselect remote subfolders you do not wish to synchronize."));
|
||||
_treeView->setFolderInfo(folder, folder, blacklist);
|
||||
}
|
||||
|
||||
void SelectiveSyncDialog::init(const AccountPtr &account)
|
||||
void SelectiveSyncDialog::init(const AccountPtr &account, const QString &labelText)
|
||||
{
|
||||
setWindowTitle(tr("Choose What to Sync"));
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
_selectiveSync = new SelectiveSyncWidget(account, this);
|
||||
layout->addWidget(_selectiveSync);
|
||||
_treeView = new SelectiveSyncTreeView(account, this);
|
||||
auto label = new QLabel(labelText);
|
||||
label->setWordWrap(true);
|
||||
layout->addWidget(label);
|
||||
layout->addWidget(_treeView);
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
|
||||
_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
|
||||
connect(_okButton, SIGNAL(clicked()), this, SLOT(accept()));
|
||||
@@ -461,7 +444,7 @@ void SelectiveSyncDialog::accept()
|
||||
if( ! ok ) {
|
||||
return;
|
||||
}
|
||||
QStringList blackList = _selectiveSync->createBlackList();
|
||||
QStringList blackList = _treeView->createBlackList();
|
||||
_folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
@@ -484,18 +467,19 @@ void SelectiveSyncDialog::accept()
|
||||
|
||||
QStringList SelectiveSyncDialog::createBlackList() const
|
||||
{
|
||||
return _selectiveSync->createBlackList();
|
||||
return _treeView->createBlackList();
|
||||
}
|
||||
|
||||
QStringList SelectiveSyncDialog::oldBlackList() const
|
||||
{
|
||||
return _selectiveSync->oldBlackList();
|
||||
return _treeView->oldBlackList();
|
||||
}
|
||||
|
||||
qint64 SelectiveSyncDialog::estimatedSize()
|
||||
{
|
||||
return _selectiveSync->estimatedSize();
|
||||
return _treeView->estimatedSize();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,50 +26,40 @@ namespace OCC {
|
||||
class Folder;
|
||||
|
||||
/**
|
||||
* @brief The SelectiveSyncWidget contains a folder tree with labels
|
||||
* @brief The SelectiveSyncTreeView class
|
||||
* @ingroup gui
|
||||
*/
|
||||
class SelectiveSyncWidget : public QWidget {
|
||||
class SelectiveSyncTreeView : public QTreeWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelectiveSyncWidget(AccountPtr account, QWidget* parent = 0);
|
||||
explicit SelectiveSyncTreeView(AccountPtr account, QWidget* parent = 0);
|
||||
|
||||
/// Returns a list of blacklisted paths, each including the trailing /
|
||||
QStringList createBlackList(QTreeWidgetItem* root = 0) const;
|
||||
|
||||
/** Returns the oldBlackList passed into setFolderInfo(), except that
|
||||
* a "/" entry is expanded to all top-level folder names.
|
||||
*/
|
||||
QStringList oldBlackList() const;
|
||||
|
||||
// Estimates the total size of checked items (recursively)
|
||||
qint64 estimatedSize(QTreeWidgetItem *root = 0);
|
||||
void refreshFolders();
|
||||
|
||||
// oldBlackList is a list of excluded paths, each including a trailing /
|
||||
void setFolderInfo(const QString &folderPath, const QString &rootName,
|
||||
const QStringList &oldBlackList = QStringList());
|
||||
|
||||
QSize sizeHint() const Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void slotUpdateDirectories(QStringList);
|
||||
void slotItemExpanded(QTreeWidgetItem *);
|
||||
void slotItemChanged(QTreeWidgetItem*,int);
|
||||
void slotLscolFinishedWithError(QNetworkReply*);
|
||||
private:
|
||||
void refreshFolders();
|
||||
void recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size);
|
||||
|
||||
AccountPtr _account;
|
||||
|
||||
QString _folderPath;
|
||||
QString _rootName;
|
||||
QStringList _oldBlackList;
|
||||
|
||||
bool _inserting; // set to true when we are inserting new items on the list
|
||||
AccountPtr _account;
|
||||
QLabel *_loading;
|
||||
|
||||
QTreeWidget *_folderTree;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -95,9 +85,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void init(const AccountPtr &account);
|
||||
void init(const AccountPtr &account, const QString &label);
|
||||
|
||||
SelectiveSyncWidget *_selectiveSync;
|
||||
SelectiveSyncTreeView *_treeView;
|
||||
|
||||
Folder *_folder;
|
||||
QPushButton *_okButton;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <QPixmap>
|
||||
#include <QImage>
|
||||
#include <QWidgetAction>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
|
||||
namespace {
|
||||
const char TOOLBAR_CSS[] =
|
||||
@@ -54,6 +56,23 @@ namespace {
|
||||
|
||||
namespace OCC {
|
||||
|
||||
static QIcon circleMask( const QPixmap& avatar )
|
||||
{
|
||||
int dim = avatar.width();
|
||||
|
||||
QPixmap fixedImage(dim, dim);
|
||||
fixedImage.fill(Qt::transparent);
|
||||
|
||||
QPainter imgPainter(&fixedImage);
|
||||
QPainterPath clip;
|
||||
clip.addEllipse(0, 0, dim, dim);
|
||||
imgPainter.setClipPath(clip);
|
||||
imgPainter.drawPixmap(0, 0, dim, dim, avatar);
|
||||
imgPainter.end();
|
||||
|
||||
return QIcon(fixedImage);
|
||||
}
|
||||
|
||||
//
|
||||
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
|
||||
//
|
||||
@@ -196,8 +215,17 @@ void SettingsDialog::accountAdded(AccountState *s)
|
||||
|
||||
bool brandingSingleAccount = !Theme::instance()->multiAccount();
|
||||
|
||||
auto accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
|
||||
brandingSingleAccount ? tr("Account") : s->account()->displayName());
|
||||
QAction *accountAction;
|
||||
QPixmap avatar = s->account()->avatar();
|
||||
const QString actionText = brandingSingleAccount ? tr("Account") : s->account()->displayName();
|
||||
if(avatar.isNull()) {
|
||||
accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
|
||||
actionText);
|
||||
} else {
|
||||
QIcon icon = circleMask(avatar);
|
||||
accountAction = createActionWithIcon(icon, actionText);
|
||||
}
|
||||
|
||||
if (!brandingSingleAccount) {
|
||||
accountAction->setToolTip(s->account()->displayName());
|
||||
accountAction->setIconText(s->shortDisplayNameForSettings(height * buttonSizeRatio));
|
||||
@@ -207,14 +235,30 @@ void SettingsDialog::accountAdded(AccountState *s)
|
||||
_ui->stack->insertWidget(0 , accountSettings);
|
||||
_actionGroup->addAction(accountAction);
|
||||
_actionGroupWidgets.insert(accountAction, accountSettings);
|
||||
_actionForAccount.insert(s->account().data(), accountAction);
|
||||
|
||||
connect( accountSettings, SIGNAL(folderChanged()), _gui, SLOT(slotFoldersChanged()));
|
||||
connect( accountSettings, SIGNAL(openFolderAlias(const QString&)),
|
||||
_gui, SLOT(slotFolderOpenAction(QString)));
|
||||
connect(s->account().data(), SIGNAL(accountChangedAvatar()), SLOT(slotAccountAvatarChanged()));
|
||||
|
||||
slotRefreshActivity(s);
|
||||
}
|
||||
|
||||
void SettingsDialog::slotAccountAvatarChanged()
|
||||
{
|
||||
Account *account = static_cast<Account*>(sender());
|
||||
if( account && _actionForAccount.contains(account)) {
|
||||
QAction *action = _actionForAccount[account];
|
||||
if( action ) {
|
||||
QPixmap pix = account->avatar();
|
||||
if( !pix.isNull() ) {
|
||||
action->setIcon( circleMask(pix) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::accountRemoved(AccountState *s)
|
||||
{
|
||||
for (auto it = _actionGroupWidgets.begin(); it != _actionGroupWidgets.end(); ++it) {
|
||||
@@ -236,6 +280,9 @@ void SettingsDialog::accountRemoved(AccountState *s)
|
||||
}
|
||||
}
|
||||
|
||||
if( _actionForAccount.contains(s->account().data()) ) {
|
||||
_actionForAccount.remove(s->account().data());
|
||||
}
|
||||
_activitySettings->slotRemoveAccount(s);
|
||||
|
||||
// Hide when the last account is deleted. We want to enter the same
|
||||
@@ -306,14 +353,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
QAction *SettingsDialog::createActionWithIcon(const QIcon& icon, const QString& text, const QString& iconPath)
|
||||
{
|
||||
QAction *action = new ToolButtonAction(icon, text, this);
|
||||
action->setCheckable(true);
|
||||
if(!iconPath.isEmpty()) {
|
||||
action->setProperty("iconPath", iconPath);
|
||||
}
|
||||
return action;
|
||||
|
||||
}
|
||||
|
||||
QAction *SettingsDialog::createColorAwareAction(const QString &iconPath, const QString &text)
|
||||
{
|
||||
// all buttons must have the same size in order to keep a good layout
|
||||
QIcon coloredIcon = createColorAwareIcon(iconPath);
|
||||
QAction *action = new ToolButtonAction(coloredIcon, text, this);
|
||||
action->setCheckable(true);
|
||||
action->setProperty("iconPath", iconPath);
|
||||
return action;
|
||||
return createActionWithIcon(coloredIcon, text, iconPath);
|
||||
}
|
||||
|
||||
void SettingsDialog::slotRefreshActivity( AccountState* accountState )
|
||||
|
||||
@@ -58,6 +58,7 @@ public slots:
|
||||
void showActivityPage();
|
||||
void slotSwitchPage(QAction *action);
|
||||
void slotRefreshActivity(AccountState *accountState );
|
||||
void slotAccountAvatarChanged();
|
||||
|
||||
protected:
|
||||
void reject() Q_DECL_OVERRIDE;
|
||||
@@ -73,12 +74,18 @@ private:
|
||||
|
||||
QIcon createColorAwareIcon(const QString &name);
|
||||
QAction *createColorAwareAction(const QString &iconName, const QString &fileName);
|
||||
QAction *createActionWithIcon(const QIcon& icon, const QString& text, const QString& iconPath = QString());
|
||||
|
||||
Ui::SettingsDialog * const _ui;
|
||||
|
||||
QActionGroup* _actionGroup;
|
||||
// Maps the actions from the action group to the corresponding widgets
|
||||
QHash<QAction*, QWidget*> _actionGroupWidgets;
|
||||
|
||||
// Maps the action in the dialog to their according account. Needed in
|
||||
// case the account avatar changes
|
||||
QHash<Account*, QAction*> _actionForAccount;
|
||||
|
||||
QToolBar* _toolBar;
|
||||
|
||||
ActivitySettings *_activitySettings;
|
||||
|
||||
@@ -229,7 +229,7 @@ void ShareLinkWidget::slotSharesFetched(const QList<QSharedPointer<Share>> &shar
|
||||
Q_FOREACH(auto share, shares) {
|
||||
|
||||
if (share->getShareType() == Share::TypeLink) {
|
||||
_share = qSharedPointerDynamicCast<LinkShare>(share);
|
||||
_share = qSharedPointerObjectCast<LinkShare>(share);
|
||||
_ui->pushButton_copy->show();
|
||||
_ui->pushButton_mail->show();
|
||||
|
||||
|
||||
+153
-100
@@ -32,8 +32,10 @@
|
||||
#include "account.h"
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QMetaMethod>
|
||||
#include <QMetaObject>
|
||||
#include <QStringList>
|
||||
#include <QScopedPointer>
|
||||
@@ -56,6 +58,11 @@
|
||||
// The second number should be changed when there are new features.
|
||||
#define MIRALL_SOCKET_API_VERSION "1.0"
|
||||
|
||||
#define DEBUG qDebug() << "SocketApi: "
|
||||
|
||||
Q_DECLARE_METATYPE(OCC::SocketListener)
|
||||
|
||||
|
||||
static inline QString removeTrailingSlash(QString path)
|
||||
{
|
||||
Q_ASSERT(path.endsWith(QLatin1Char('/')));
|
||||
@@ -63,9 +70,89 @@ static inline QString removeTrailingSlash(QString path)
|
||||
return path;
|
||||
}
|
||||
|
||||
static QString buildMessage(const QString& verb, const QString &path, const QString &status = QString::null )
|
||||
{
|
||||
QString msg(verb);
|
||||
|
||||
if( !status.isEmpty() ) {
|
||||
msg.append(QLatin1Char(':'));
|
||||
msg.append(status);
|
||||
}
|
||||
if( !path.isEmpty() ) {
|
||||
msg.append(QLatin1Char(':'));
|
||||
QFileInfo fi(path);
|
||||
msg.append(QDir::toNativeSeparators(fi.absoluteFilePath()));
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
#define DEBUG qDebug() << "SocketApi: "
|
||||
class BloomFilter {
|
||||
// Initialize with m=1024 bits and k=2 (high and low 16 bits of a qHash).
|
||||
// For a client navigating in less than 100 directories, this gives us a probability less than (1-e^(-2*100/1024))^2 = 0.03147872136 false positives.
|
||||
const static int NumBits = 1024;
|
||||
|
||||
public:
|
||||
BloomFilter() : hashBits(NumBits) { }
|
||||
|
||||
void storeHash(uint hash) {
|
||||
hashBits.setBit((hash & 0xFFFF) % NumBits);
|
||||
hashBits.setBit((hash >> 16) % NumBits);
|
||||
}
|
||||
bool isHashMaybeStored(uint hash) const {
|
||||
return hashBits.testBit((hash & 0xFFFF) % NumBits)
|
||||
&& hashBits.testBit((hash >> 16) % NumBits);
|
||||
}
|
||||
|
||||
private:
|
||||
QBitArray hashBits;
|
||||
};
|
||||
|
||||
class SocketListener {
|
||||
public:
|
||||
QIODevice* socket;
|
||||
|
||||
SocketListener(QIODevice* socket = nullptr) : socket(socket) { }
|
||||
|
||||
void sendMessage(const QString& message, bool doWait = false) const
|
||||
{
|
||||
DEBUG << "Sending message: " << message;
|
||||
QString localMessage = message;
|
||||
if( ! localMessage.endsWith(QLatin1Char('\n'))) {
|
||||
localMessage.append(QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
QByteArray bytesToSend = localMessage.toUtf8();
|
||||
qint64 sent = socket->write(bytesToSend);
|
||||
if( doWait ) {
|
||||
socket->waitForBytesWritten(1000);
|
||||
}
|
||||
if( sent != bytesToSend.length() ) {
|
||||
qDebug() << "WARN: Could not send all data on socket for " << localMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void sendMessageIfDirectoryMonitored(const QString& message, uint systemDirectoryHash) const
|
||||
{
|
||||
if (_monitoredDirectoriesBloomFilter.isHashMaybeStored(systemDirectoryHash))
|
||||
sendMessage(message, false);
|
||||
}
|
||||
|
||||
void registerMonitoredDirectory(uint systemDirectoryHash)
|
||||
{
|
||||
_monitoredDirectoriesBloomFilter.storeHash(systemDirectoryHash);
|
||||
}
|
||||
private:
|
||||
BloomFilter _monitoredDirectoriesBloomFilter;
|
||||
};
|
||||
|
||||
struct ListenerHasSocketPred {
|
||||
QIODevice *socket;
|
||||
ListenerHasSocketPred(QIODevice *socket) : socket(socket) { }
|
||||
bool operator()(const SocketListener &listener) const { return listener.socket == socket; }
|
||||
};
|
||||
|
||||
SocketApi::SocketApi(QObject* parent)
|
||||
: QObject(parent)
|
||||
@@ -129,7 +216,7 @@ SocketApi::~SocketApi()
|
||||
DEBUG << "dtor";
|
||||
_localServer.close();
|
||||
// All remaining sockets will be destroyed with _localServer, their parent
|
||||
Q_ASSERT(_listeners.isEmpty() || _listeners.first()->parent() == &_localServer);
|
||||
Q_ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
|
||||
_listeners.clear();
|
||||
}
|
||||
|
||||
@@ -143,14 +230,16 @@ void SocketApi::slotNewConnection()
|
||||
DEBUG << "New connection" << socket;
|
||||
connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadSocket()));
|
||||
connect(socket, SIGNAL(disconnected()), this, SLOT(onLostConnection()));
|
||||
connect(socket, SIGNAL(destroyed(QObject*)), this, SLOT(slotSocketDestroyed(QObject*)));
|
||||
Q_ASSERT(socket->readAll().isEmpty());
|
||||
|
||||
_listeners.append(socket);
|
||||
_listeners.append(SocketListener(socket));
|
||||
SocketListener &listener = _listeners.last();
|
||||
|
||||
foreach( Folder *f, FolderMan::instance()->map() ) {
|
||||
if (f->canSync()) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
sendMessage(socket, message);
|
||||
listener.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,32 +247,33 @@ void SocketApi::slotNewConnection()
|
||||
void SocketApi::onLostConnection()
|
||||
{
|
||||
DEBUG << "Lost connection " << sender();
|
||||
|
||||
QIODevice* socket = qobject_cast<QIODevice*>(sender());
|
||||
_listeners.removeAll(socket);
|
||||
socket->deleteLater();
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void SocketApi::slotSocketDestroyed(QObject* obj)
|
||||
{
|
||||
QIODevice* socket = static_cast<QIODevice*>(obj);
|
||||
_listeners.erase(std::remove_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket)), _listeners.end());
|
||||
}
|
||||
|
||||
void SocketApi::slotReadSocket()
|
||||
{
|
||||
QIODevice* socket = qobject_cast<QIODevice*>(sender());
|
||||
Q_ASSERT(socket);
|
||||
SocketListener *listener = &*std::find_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket));
|
||||
|
||||
while(socket->canReadLine()) {
|
||||
// Make sure to normalize the input from the socket to
|
||||
// make sure that the path will match, especially on OS X.
|
||||
QString line = QString::fromUtf8(socket->readLine()).normalized(QString::NormalizationForm_C);
|
||||
line.chop(1); // remove the '\n'
|
||||
QString command = line.split(":").value(0);
|
||||
QString function = QString(QLatin1String("command_")).append(command);
|
||||
|
||||
QString functionWithArguments = function + QLatin1String("(QString,QIODevice*)");
|
||||
int indexOfMethod = this->metaObject()->indexOfMethod(functionWithArguments.toAscii());
|
||||
QByteArray command = line.split(":").value(0).toAscii();
|
||||
QByteArray functionWithArguments = "command_" + command + "(QString,SocketListener*)";
|
||||
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||
|
||||
QString argument = line.remove(0, command.length()+1);
|
||||
if(indexOfMethod != -1) {
|
||||
QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(QIODevice*, socket));
|
||||
staticMetaObject.method(indexOfMethod).invoke(this, Q_ARG(QString, argument), Q_ARG(SocketListener*, listener));
|
||||
} else {
|
||||
DEBUG << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||
}
|
||||
@@ -199,8 +289,8 @@ void SocketApi::slotRegisterPath( const QString& alias )
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
foreach(QIODevice *socket, _listeners) {
|
||||
sendMessage(socket, message);
|
||||
foreach (auto &listener, _listeners) {
|
||||
listener.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +304,7 @@ void SocketApi::slotUnregisterPath( const QString& alias )
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f)
|
||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), removeTrailingSlash(f->path()), QString::null, true );
|
||||
broadcastMessage(buildMessage(QLatin1String("UNREGISTER_PATH"), removeTrailingSlash(f->path()), QString::null), true);
|
||||
|
||||
_registeredAliases.remove(alias);
|
||||
}
|
||||
@@ -235,74 +325,42 @@ void SocketApi::slotUpdateFolderView(Folder *f)
|
||||
f->syncResult().status() == SyncResult::SetupError ) {
|
||||
|
||||
QString rootPath = removeTrailingSlash(f->path());
|
||||
broadcastMessage(QLatin1String("STATUS"), rootPath,
|
||||
f->syncEngine().syncFileStatusTracker().fileStatus("").toSocketAPIString());
|
||||
broadcastStatusPushMessage(rootPath, f->syncEngine().syncFileStatusTracker().fileStatus(""));
|
||||
|
||||
broadcastMessage(QLatin1String("UPDATE_VIEW"), rootPath);
|
||||
broadcastMessage(buildMessage(QLatin1String("UPDATE_VIEW"), rootPath));
|
||||
} else {
|
||||
qDebug() << "Not sending UPDATE_VIEW for" << f->alias() << "because status() is" << f->syncResult().status();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::slotFileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus)
|
||||
void SocketApi::broadcastMessage(const QString& msg, bool doWait)
|
||||
{
|
||||
broadcastMessage(QLatin1String("STATUS"), systemFileName, fileStatus.toSocketAPIString());
|
||||
}
|
||||
|
||||
void SocketApi::sendMessage(QIODevice *socket, const QString& message, bool doWait)
|
||||
{
|
||||
DEBUG << "Sending message: " << message;
|
||||
QString localMessage = message;
|
||||
if( ! localMessage.endsWith(QLatin1Char('\n'))) {
|
||||
localMessage.append(QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
QByteArray bytesToSend = localMessage.toUtf8();
|
||||
qint64 sent = socket->write(bytesToSend);
|
||||
if( doWait ) {
|
||||
socket->waitForBytesWritten(1000);
|
||||
}
|
||||
if( sent != bytesToSend.length() ) {
|
||||
qDebug() << "WARN: Could not send all data on socket for " << localMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SocketApi::broadcastMessage( const QString& verb, const QString& path, const QString& status, bool doWait )
|
||||
{
|
||||
QString msg(verb);
|
||||
|
||||
if( !status.isEmpty() ) {
|
||||
msg.append(QLatin1Char(':'));
|
||||
msg.append(status);
|
||||
}
|
||||
if( !path.isEmpty() ) {
|
||||
msg.append(QLatin1Char(':'));
|
||||
QFileInfo fi(path);
|
||||
msg.append(QDir::toNativeSeparators(fi.absoluteFilePath()));
|
||||
}
|
||||
|
||||
foreach(QIODevice *socket, _listeners) {
|
||||
sendMessage(socket, msg, doWait);
|
||||
foreach (auto &listener, _listeners) {
|
||||
listener.sendMessage(msg, doWait);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QIODevice* socket)
|
||||
void SocketApi::broadcastStatusPushMessage(const QString& systemPath, SyncFileStatus fileStatus)
|
||||
{
|
||||
QString msg = buildMessage(QLatin1String("STATUS"), systemPath, fileStatus.toSocketAPIString());
|
||||
Q_ASSERT(!systemPath.endsWith('/'));
|
||||
uint directoryHash = qHash(systemPath.left(systemPath.lastIndexOf('/')));
|
||||
foreach (auto &listener, _listeners) {
|
||||
listener.sendMessageIfDirectoryMonitored(msg, directoryHash);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketListener* listener)
|
||||
{
|
||||
// This command is the same as RETRIEVE_FILE_STATUS
|
||||
|
||||
//qDebug() << Q_FUNC_INFO << argument;
|
||||
command_RETRIEVE_FILE_STATUS(argument, socket);
|
||||
command_RETRIEVE_FILE_STATUS(argument, listener);
|
||||
}
|
||||
|
||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice* socket)
|
||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketListener* listener)
|
||||
{
|
||||
if( !socket ) {
|
||||
qDebug() << "No valid socket object.";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << argument;
|
||||
|
||||
QString statusString;
|
||||
@@ -312,27 +370,27 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice*
|
||||
// this can happen in offline mode e.g.: nothing to worry about
|
||||
statusString = QLatin1String("NOP");
|
||||
} else {
|
||||
QString relativePath = QDir::cleanPath(argument).mid(syncFolder->cleanPath().length()+1);
|
||||
if( relativePath.endsWith(QLatin1Char('/')) ) {
|
||||
relativePath.truncate(relativePath.length()-1);
|
||||
qWarning() << "Removed trailing slash for directory: " << relativePath << "Status pushes won't have one.";
|
||||
QString systemPath = QDir::cleanPath(argument);
|
||||
if( systemPath.endsWith(QLatin1Char('/')) ) {
|
||||
systemPath.truncate(systemPath.length()-1);
|
||||
qWarning() << "Removed trailing slash for directory: " << systemPath << "Status pushes won't have one.";
|
||||
}
|
||||
SyncFileStatus fileStatus = syncFolder->syncEngine().syncFileStatusTracker().fileStatus(relativePath);
|
||||
// The user probably visited this directory in the file shell.
|
||||
// Let the listener know that it should now send status pushes for sibblings of this file.
|
||||
QString directory = systemPath.left(systemPath.lastIndexOf('/'));
|
||||
listener->registerMonitoredDirectory(qHash(directory));
|
||||
|
||||
QString relativePath = systemPath.mid(syncFolder->cleanPath().length()+1);
|
||||
SyncFileStatus fileStatus = syncFolder->syncEngine().syncFileStatusTracker().fileStatus(relativePath);
|
||||
statusString = fileStatus.toSocketAPIString();
|
||||
}
|
||||
|
||||
const QString message = QLatin1String("STATUS:") % statusString % QLatin1Char(':') % QDir::toNativeSeparators(argument);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
}
|
||||
|
||||
void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
||||
void SocketApi::command_SHARE(const QString& localFile, SocketListener* listener)
|
||||
{
|
||||
if (!socket) {
|
||||
qDebug() << Q_FUNC_INFO << "No valid socket object.";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << localFile;
|
||||
|
||||
auto theme = Theme::instance();
|
||||
@@ -341,16 +399,16 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
||||
if (!shareFolder) {
|
||||
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
|
||||
// files that are not within a sync folder are not synced.
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else if (!shareFolder->accountState()->isConnected()) {
|
||||
const QString message = QLatin1String("SHARE:NOTCONNECTED:")+QDir::toNativeSeparators(localFile);
|
||||
// if the folder isn't connected, don't open the share dialog
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else if (!theme->linkSharing() && (
|
||||
!theme->userGroupSharing() ||
|
||||
shareFolder->accountState()->account()->serverVersionInt() < ((8 << 16) + (2 << 8)))) {
|
||||
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else {
|
||||
const QString localFileClean = QDir::cleanPath(localFile);
|
||||
const QString file = localFileClean.mid(shareFolder->cleanPath().length()+1);
|
||||
@@ -359,7 +417,7 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
||||
// Verify the file is on the server (to our knowledge of course)
|
||||
if (fileStatus.tag() != SyncFileStatus::StatusUpToDate) {
|
||||
const QString message = QLatin1String("SHARE:NOTSYNCED:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -368,7 +426,7 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
||||
// Can't share root folder
|
||||
if (remotePath == "/") {
|
||||
const QString message = QLatin1String("SHARE:CANNOTSHAREROOT:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -382,31 +440,26 @@ void SocketApi::command_SHARE(const QString& localFile, QIODevice* socket)
|
||||
}
|
||||
}
|
||||
const QString message = QLatin1String("SHARE:OK:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
|
||||
emit shareCommandReceived(remotePath, localFileClean, allowReshare);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_VERSION(const QString&, QIODevice* socket)
|
||||
void SocketApi::command_VERSION(const QString&, SocketListener* listener)
|
||||
{
|
||||
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
||||
listener->sendMessage(QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
||||
}
|
||||
|
||||
void SocketApi::command_SHARE_STATUS(const QString &localFile, QIODevice *socket)
|
||||
void SocketApi::command_SHARE_STATUS(const QString &localFile, SocketListener* listener)
|
||||
{
|
||||
if (!socket) {
|
||||
qDebug() << Q_FUNC_INFO << "No valid socket object.";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << localFile;
|
||||
|
||||
Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
|
||||
|
||||
if (!shareFolder) {
|
||||
const QString message = QLatin1String("SHARE_STATUS:NOP:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else {
|
||||
const QString file = QDir::cleanPath(localFile).mid(shareFolder->cleanPath().length()+1);
|
||||
SyncFileStatus fileStatus = shareFolder->syncEngine().syncFileStatusTracker().fileStatus(file);
|
||||
@@ -414,7 +467,7 @@ void SocketApi::command_SHARE_STATUS(const QString &localFile, QIODevice *socket
|
||||
// Verify the file is on the server (to our knowledge of course)
|
||||
if (fileStatus.tag() != SyncFileStatus::StatusUpToDate) {
|
||||
const QString message = QLatin1String("SHARE_STATUS:NOTSYNCED:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -422,7 +475,7 @@ void SocketApi::command_SHARE_STATUS(const QString &localFile, QIODevice *socket
|
||||
|
||||
if (!capabilities.shareAPI()) {
|
||||
const QString message = QLatin1String("SHARE_STATUS:DISABLED:")+QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else {
|
||||
auto theme = Theme::instance();
|
||||
QString available;
|
||||
@@ -441,18 +494,18 @@ void SocketApi::command_SHARE_STATUS(const QString &localFile, QIODevice *socket
|
||||
|
||||
if (available.isEmpty()) {
|
||||
const QString message = QLatin1String("SHARE_STATUS:DISABLED") + ":" + QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
} else {
|
||||
const QString message = QLatin1String("SHARE_STATUS:") + available + ":" + QDir::toNativeSeparators(localFile);
|
||||
sendMessage(socket, message);
|
||||
listener->sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_SHARE_MENU_TITLE(const QString &, QIODevice* socket)
|
||||
void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener* listener)
|
||||
{
|
||||
sendMessage(socket, QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()));
|
||||
listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
QString SocketApi::buildRegisterPathMessage(const QString& path)
|
||||
|
||||
+11
-10
@@ -35,6 +35,7 @@ namespace OCC {
|
||||
|
||||
class SyncFileStatus;
|
||||
class Folder;
|
||||
class SocketListener;
|
||||
|
||||
/**
|
||||
* @brief The SocketApi class
|
||||
@@ -60,25 +61,25 @@ signals:
|
||||
private slots:
|
||||
void slotNewConnection();
|
||||
void onLostConnection();
|
||||
void slotSocketDestroyed(QObject* obj);
|
||||
void slotReadSocket();
|
||||
void slotFileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus);
|
||||
void broadcastStatusPushMessage(const QString& systemPath, SyncFileStatus fileStatus);
|
||||
|
||||
private:
|
||||
void sendMessage(QIODevice* socket, const QString& message, bool doWait = false);
|
||||
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
||||
void broadcastMessage(const QString& msg, bool doWait = false);
|
||||
|
||||
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, QIODevice* socket);
|
||||
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice* socket);
|
||||
Q_INVOKABLE void command_SHARE(const QString& localFile, QIODevice* socket);
|
||||
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketListener* listener);
|
||||
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, SocketListener* listener);
|
||||
Q_INVOKABLE void command_SHARE(const QString& localFile, SocketListener* listener);
|
||||
|
||||
Q_INVOKABLE void command_VERSION(const QString& argument, QIODevice* socket);
|
||||
Q_INVOKABLE void command_VERSION(const QString& argument, SocketListener* listener);
|
||||
|
||||
Q_INVOKABLE void command_SHARE_STATUS(const QString& localFile, QIODevice *socket);
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, QIODevice* socket);
|
||||
Q_INVOKABLE void command_SHARE_STATUS(const QString& localFile, SocketListener* listener);
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, SocketListener* listener);
|
||||
QString buildRegisterPathMessage(const QString& path);
|
||||
|
||||
QSet<QString> _registeredAliases;
|
||||
QList<QIODevice*> _listeners;
|
||||
QList<SocketListener> _listeners;
|
||||
SocketApiServer _localServer;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "updater/ocupdater.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtCore>
|
||||
#include <QtNetwork>
|
||||
#include <QtGui>
|
||||
@@ -43,9 +44,8 @@ UpdaterScheduler::UpdaterScheduler(QObject *parent) :
|
||||
connect( &_updateCheckTimer, SIGNAL(timeout()),
|
||||
this, SLOT(slotTimerFired()) );
|
||||
|
||||
// Note: the sparkle-updater is not an OCUpdater and thus the dynamic_cast
|
||||
// returns NULL. Clever detail.
|
||||
if (OCUpdater *updater = dynamic_cast<OCUpdater*>(Updater::instance())) {
|
||||
// Note: the sparkle-updater is not an OCUpdater
|
||||
if (OCUpdater *updater = qobject_cast<OCUpdater*>(Updater::instance())) {
|
||||
connect(updater, SIGNAL(newUpdateAvailable(QString,QString)),
|
||||
this, SIGNAL(updaterAnnouncement(QString,QString)) );
|
||||
connect(updater, SIGNAL(requestRestart()), SIGNAL(requestRestart()));
|
||||
@@ -76,14 +76,17 @@ void UpdaterScheduler::slotTimerFired()
|
||||
return;
|
||||
}
|
||||
|
||||
Updater::instance()->backgroundCheckForUpdate();
|
||||
Updater *updater = Updater::instance();
|
||||
if (updater) {
|
||||
updater->backgroundCheckForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
OCUpdater::OCUpdater(const QUrl &url, QObject *parent) :
|
||||
QObject(parent)
|
||||
OCUpdater::OCUpdater(const QUrl &url) :
|
||||
Updater()
|
||||
, _updateUrl(url)
|
||||
, _state(Unknown)
|
||||
, _accessManager(new AccessManager(this))
|
||||
@@ -242,8 +245,8 @@ void OCUpdater::slotTimedOut()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NSISUpdater::NSISUpdater(const QUrl &url, QObject *parent)
|
||||
: OCUpdater(url, parent)
|
||||
NSISUpdater::NSISUpdater(const QUrl &url)
|
||||
: OCUpdater(url)
|
||||
, _showFallbackMessage(false)
|
||||
{
|
||||
}
|
||||
@@ -421,8 +424,8 @@ void NSISUpdater::slotSetSeenVersion()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PassiveUpdateNotifier::PassiveUpdateNotifier(const QUrl &url, QObject *parent)
|
||||
: OCUpdater(url, parent)
|
||||
PassiveUpdateNotifier::PassiveUpdateNotifier(const QUrl &url)
|
||||
: OCUpdater(url)
|
||||
{
|
||||
// remember the version of the currently running binary. On Linux it might happen that the
|
||||
// package management updates the package while the app is running. This is detected in the
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
* @brief Class that uses an ownCloud proprietary XML format to fetch update information
|
||||
* @ingroup gui
|
||||
*/
|
||||
class OCUpdater : public QObject, public Updater
|
||||
class OCUpdater : public Updater
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
Downloading, DownloadComplete,
|
||||
DownloadFailed, DownloadTimedOut,
|
||||
UpdateOnlyAvailableThroughSystem };
|
||||
explicit OCUpdater(const QUrl &url, QObject *parent = 0);
|
||||
explicit OCUpdater(const QUrl &url);
|
||||
|
||||
bool performUpdate();
|
||||
|
||||
@@ -141,7 +141,7 @@ class NSISUpdater : public OCUpdater {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum UpdateState { NoUpdate = 0, UpdateAvailable, UpdateFailed };
|
||||
explicit NSISUpdater(const QUrl &url, QObject *parent = 0);
|
||||
explicit NSISUpdater(const QUrl &url);
|
||||
bool handleStartup() Q_DECL_OVERRIDE;
|
||||
private slots:
|
||||
void slotSetSeenVersion();
|
||||
@@ -167,7 +167,7 @@ private:
|
||||
class PassiveUpdateNotifier : public OCUpdater {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PassiveUpdateNotifier(const QUrl &url, QObject *parent = 0);
|
||||
explicit PassiveUpdateNotifier(const QUrl &url);
|
||||
bool handleStartup() Q_DECL_OVERRIDE { return false; }
|
||||
void backgroundCheckForUpdate() Q_DECL_OVERRIDE;
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ Updater *Updater::create()
|
||||
if (updateBaseUrl.isEmpty()) {
|
||||
updateBaseUrl = QUrl(QLatin1String(APPLICATION_UPDATE_URL));
|
||||
}
|
||||
if (!updateBaseUrl.isValid() || updateBaseUrl.host() == ".") {
|
||||
qDebug() << "Not a valid updater URL, will not do update check";
|
||||
return 0;
|
||||
}
|
||||
updateBaseUrl = addQueryParams(updateBaseUrl);
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
updateBaseUrl.addQueryItem( QLatin1String("sparkle"), QLatin1String("true"));
|
||||
|
||||
@@ -21,7 +21,8 @@ class QUrl;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class Updater {
|
||||
class Updater : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Helper {
|
||||
static qint64 stringVersionToInt(const QString& version);
|
||||
@@ -37,6 +38,7 @@ public:
|
||||
|
||||
protected:
|
||||
static QString clientVersion();
|
||||
Updater() : QObject(0) {}
|
||||
|
||||
private:
|
||||
static QString getSystemInfo();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
project(libsync)
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
|
||||
configure_file( version.h.in "${CMAKE_CURRENT_BINARY_DIR}/version.h" )
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
@@ -90,6 +90,16 @@ void Account::setDavUser(const QString &newDavUser)
|
||||
_davUser = newDavUser;
|
||||
}
|
||||
|
||||
QPixmap Account::avatar() const
|
||||
{
|
||||
return _avatarPixmap;
|
||||
}
|
||||
void Account::setAvatar(const QPixmap& pixmap)
|
||||
{
|
||||
_avatarPixmap = pixmap;
|
||||
emit accountChangedAvatar();
|
||||
}
|
||||
|
||||
QString Account::displayName() const
|
||||
{
|
||||
QString dn = QString("%1@%2").arg(_credentials->user(), _url.host());
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <QSslCipher>
|
||||
#include <QSslError>
|
||||
#include <QSharedPointer>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "utility.h"
|
||||
#include <memory>
|
||||
#include "capabilities.h"
|
||||
@@ -78,6 +80,9 @@ public:
|
||||
QString davUser() const;
|
||||
void setDavUser(const QString &newDavUser);
|
||||
|
||||
QPixmap avatar() const;
|
||||
void setAvatar(const QPixmap& pixmap);
|
||||
|
||||
/// The name of the account as shown in the toolbar
|
||||
QString displayName() const;
|
||||
|
||||
@@ -197,6 +202,8 @@ signals:
|
||||
|
||||
void serverVersionChanged(Account* account, const QString& newVersion, const QString& oldVersion);
|
||||
|
||||
void accountChangedAvatar();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotHandleSslErrors(QNetworkReply*,QList<QSslError>);
|
||||
void slotCredentialsFetched();
|
||||
@@ -209,6 +216,7 @@ private:
|
||||
QWeakPointer<Account> _sharedThis;
|
||||
QString _id;
|
||||
QString _davUser;
|
||||
QPixmap _avatarPixmap;
|
||||
QMap<QString, QVariant> _settingsMap;
|
||||
QUrl _url;
|
||||
QList<QSslCertificate> _approvedCerts;
|
||||
|
||||
@@ -121,4 +121,13 @@ bool Capabilities::chunkingParallelUploadDisabled() const
|
||||
return _capabilities["dav"].toMap()["chunkingParallelUploadDisabled"].toBool();
|
||||
}
|
||||
|
||||
QList<int> Capabilities::httpErrorCodesThatResetFailingChunkedUploads() const
|
||||
{
|
||||
QList<int> list;
|
||||
foreach (const auto & t, _capabilities["dav"].toMap()["httpErrorCodesThatResetFailingChunkedUploads"].toList()) {
|
||||
list.push_back(t.toInt());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,6 +81,25 @@ public:
|
||||
*/
|
||||
QByteArray uploadChecksumType() const;
|
||||
|
||||
/**
|
||||
* List of HTTP error codes should be guaranteed to eventually reset
|
||||
* failing chunked uploads.
|
||||
*
|
||||
* The resetting works by tracking UploadInfo::errorCount.
|
||||
*
|
||||
* Note that other error codes than the ones listed here may reset the
|
||||
* upload as well.
|
||||
*
|
||||
* Motivation: See #5344. They should always be reset on 412 (possibly
|
||||
* checksum error), but broken servers may also require resets on
|
||||
* unusual error codes such as 503.
|
||||
*
|
||||
* Path: dav/httpErrorCodesThatResetFailingChunkedUploads
|
||||
* Default: []
|
||||
* Example: [503, 500]
|
||||
*/
|
||||
QList<int> httpErrorCodesThatResetFailingChunkedUploads() const;
|
||||
|
||||
private:
|
||||
QVariantMap _capabilities;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <QtCore>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "connectionvalidator.h"
|
||||
#include "account.h"
|
||||
@@ -252,7 +253,18 @@ void ConnectionValidator::slotUserFetched(const QVariantMap &json)
|
||||
QString user = json.value("ocs").toMap().value("data").toMap().value("id").toString();
|
||||
if (!user.isEmpty()) {
|
||||
_account->setDavUser(user);
|
||||
|
||||
AvatarJob *job = new AvatarJob(_account, this);
|
||||
job->setTimeout(20*1000);
|
||||
QObject::connect(job, SIGNAL(avatarPixmap(QPixmap)), this, SLOT(slotAvatarPixmap(QPixmap)));
|
||||
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotAvatarPixmap(const QPixmap& pixmap)
|
||||
{
|
||||
_account->setAvatar(pixmap);
|
||||
reportResult(Connected);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@ namespace OCC {
|
||||
+-> fetchUser
|
||||
PropfindJob
|
||||
|
|
||||
+-> slotUserFetched --> X
|
||||
+-> slotUserFetched
|
||||
AvatarJob
|
||||
|
|
||||
+-> slotAvatarPixmap --> reportResult()
|
||||
|
||||
\endcode
|
||||
*/
|
||||
@@ -119,6 +122,7 @@ protected slots:
|
||||
|
||||
void slotCapabilitiesRecieved(const QVariantMap&);
|
||||
void slotUserFetched(const QVariantMap &);
|
||||
void slotAvatarPixmap(const QPixmap&);
|
||||
|
||||
private:
|
||||
void reportResult(Status status);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
@@ -589,6 +590,42 @@ bool PropfindJob::finished()
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
AvatarJob::AvatarJob(AccountPtr account, QObject *parent)
|
||||
: AbstractNetworkJob(account, QString(), parent)
|
||||
{
|
||||
_avatarUrl = Utility::concatUrlPath(account->url(), QString("remote.php/dav/avatars/%1/128.png").arg(account->davUser()));
|
||||
}
|
||||
|
||||
void AvatarJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
setReply(davRequest("GET", _avatarUrl, req));
|
||||
setupConnections(reply());
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
bool AvatarJob::finished()
|
||||
{
|
||||
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
QPixmap avPixmap;
|
||||
|
||||
if (http_result_code == 200) {
|
||||
|
||||
QByteArray pngData = reply()->readAll();
|
||||
if( pngData.size() ) {
|
||||
|
||||
if( avPixmap.loadFromData(pngData) ) {
|
||||
qDebug() << "Retrieved Avatar pixmap!";
|
||||
}
|
||||
}
|
||||
}
|
||||
emit(avatarPixmap(avPixmap));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
ProppatchJob::ProppatchJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
{
|
||||
|
||||
@@ -129,6 +129,36 @@ private:
|
||||
QList<QByteArray> _properties;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The AvatarJob class
|
||||
*
|
||||
* Retrieves the account users avatar from the server using a GET request.
|
||||
*
|
||||
* If the server does not have the avatar, the result Pixmap is empty.
|
||||
*
|
||||
* @ingroup libsync
|
||||
*/
|
||||
class OWNCLOUDSYNC_EXPORT AvatarJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AvatarJob(AccountPtr account, QObject *parent = 0);
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief avatarPixmap - returns either a valid pixmap or not.
|
||||
*/
|
||||
|
||||
void avatarPixmap(QPixmap);
|
||||
|
||||
private slots:
|
||||
virtual bool finished() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QUrl _avatarUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Send a Proppatch request
|
||||
*
|
||||
|
||||
@@ -99,7 +99,7 @@ int OwncloudPropagator::hardMaximumActiveJob()
|
||||
|
||||
/** Updates, creates or removes a blacklist entry for the given item.
|
||||
*
|
||||
* Returns whether the file is in the blacklist now.
|
||||
* Returns whether the error should be suppressed.
|
||||
*/
|
||||
static bool blacklistCheck(SyncJournalDb* journal, const SyncFileItem& item)
|
||||
{
|
||||
@@ -108,16 +108,13 @@ static bool blacklistCheck(SyncJournalDb* journal, const SyncFileItem& item)
|
||||
|
||||
if (newEntry.isValid()) {
|
||||
journal->updateErrorBlacklistEntry(newEntry);
|
||||
// Also clear upload info if any so we don't resume from the same transfer-id if there was too many failures (#5344)
|
||||
// (maybe the reason is that the state for this transfer id is broken on the server.)
|
||||
if (newEntry._retryCount > 3) {
|
||||
journal->setUploadInfo(item._file, SyncJournalDb::UploadInfo());
|
||||
}
|
||||
} else if (oldEntry.isValid()) {
|
||||
journal->wipeErrorBlacklistEntry(item._file);
|
||||
}
|
||||
|
||||
return newEntry.isValid();
|
||||
// In some cases we add errors to the blacklist for tracking, but don't
|
||||
// want to actively suppress them.
|
||||
return newEntry.isValid() && newEntry._ignoreDuration > 0;
|
||||
}
|
||||
|
||||
void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorString)
|
||||
@@ -135,7 +132,7 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||
}
|
||||
}
|
||||
|
||||
if( _propagator->_abortRequested.fetchAndAddRelaxed(0) &&
|
||||
if( propagator()->_abortRequested.fetchAndAddRelaxed(0) &&
|
||||
(status == SyncFileItem::NormalError || status == SyncFileItem::FatalError)) {
|
||||
// an abort request is ongoing. Change the status to Soft-Error
|
||||
status = SyncFileItem::SoftError;
|
||||
@@ -144,10 +141,12 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||
switch( status ) {
|
||||
case SyncFileItem::SoftError:
|
||||
case SyncFileItem::FatalError:
|
||||
// do not blacklist in case of soft error or fatal error.
|
||||
break;
|
||||
case SyncFileItem::NormalError:
|
||||
if (blacklistCheck(_propagator->_journal, *_item) && _item->_hasBlacklistEntry) {
|
||||
// For normal errors, we blacklist aggressively, otherwise only on
|
||||
// explicit request.
|
||||
if ((status == SyncFileItem::NormalError || _item->_errorMayBeBlacklisted)
|
||||
&& blacklistCheck(propagator()->_journal, *_item)
|
||||
&& _item->_hasBlacklistEntry) {
|
||||
// do not error if the item was, and continues to be, blacklisted
|
||||
status = SyncFileItem::FileIgnored;
|
||||
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
|
||||
@@ -157,10 +156,10 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
||||
case SyncFileItem::Restoration:
|
||||
if( _item->_hasBlacklistEntry ) {
|
||||
// wipe blacklist entry.
|
||||
_propagator->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
propagator()->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
// remove a blacklist entry in case the file was moved.
|
||||
if( _item->_originalFile != _item->_file ) {
|
||||
_propagator->_journal->wipeErrorBlacklistEntry(_item->_originalFile);
|
||||
propagator()->_journal->wipeErrorBlacklistEntry(_item->_originalFile);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -187,7 +186,7 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr
|
||||
{
|
||||
PropagateItemJob *newJob = NULL;
|
||||
|
||||
if( httpStatusCode == 403 && _propagator->isInSharedDirectory(_item->_file )) {
|
||||
if( httpStatusCode == 403 && propagator()->isInSharedDirectory(_item->_file )) {
|
||||
if( !_item->_isDirectory ) {
|
||||
SyncFileItemPtr downloadItem(new SyncFileItem(*_item));
|
||||
if (downloadItem->_instruction == CSYNC_INSTRUCTION_NEW
|
||||
@@ -206,18 +205,18 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr
|
||||
downloadItem->_instruction = CSYNC_INSTRUCTION_SYNC;
|
||||
}
|
||||
downloadItem->_direction = SyncFileItem::Down;
|
||||
newJob = new PropagateDownloadFile(_propagator, downloadItem);
|
||||
newJob = new PropagateDownloadFile(propagator(), downloadItem);
|
||||
} else {
|
||||
// Directories are harder to recover.
|
||||
// But just re-create the directory, next sync will be able to recover the files
|
||||
SyncFileItemPtr mkdirItem(new SyncFileItem(*_item));
|
||||
mkdirItem->_instruction = CSYNC_INSTRUCTION_NEW;
|
||||
mkdirItem->_direction = SyncFileItem::Down;
|
||||
newJob = new PropagateLocalMkdir(_propagator, mkdirItem);
|
||||
newJob = new PropagateLocalMkdir(propagator(), mkdirItem);
|
||||
// Also remove the inodes and fileid from the db so no further renames are tried for
|
||||
// this item.
|
||||
_propagator->_journal->avoidRenamesOnNextSync(_item->_file);
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_journal->avoidRenamesOnNextSync(_item->_file);
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
if( newJob ) {
|
||||
newJob->setRestoreJobMsg(msg);
|
||||
@@ -574,6 +573,18 @@ OwncloudPropagator::DiskSpaceResult OwncloudPropagator::diskSpaceCheck() const
|
||||
|
||||
// ================================================================================
|
||||
|
||||
PropagatorJob::PropagatorJob(OwncloudPropagator *propagator)
|
||||
: QObject(propagator)
|
||||
, _state(NotYetStarted)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OwncloudPropagator *PropagatorJob::propagator() const
|
||||
{
|
||||
return qobject_cast<OwncloudPropagator*>(parent());
|
||||
}
|
||||
|
||||
PropagatorJob::JobParallelism PropagateDirectory::parallelism()
|
||||
{
|
||||
// If any of the non-finished sub jobs is not parallel, we have to wait
|
||||
@@ -689,7 +700,7 @@ void PropagateDirectory::finalize()
|
||||
if(_item->_instruction == CSYNC_INSTRUCTION_RENAME
|
||||
&& _item->_originalFile != _item->_renameTarget) {
|
||||
// Remove the stale entries from the database.
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile, true);
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile, true);
|
||||
}
|
||||
|
||||
_item->_file = _item->_renameTarget;
|
||||
@@ -707,8 +718,8 @@ void PropagateDirectory::finalize()
|
||||
_item->_fileId = mkdir->_item->_fileId;
|
||||
}
|
||||
}
|
||||
SyncJournalFileRecord record(*_item, _propagator->_localDir + _item->_file);
|
||||
ok = _propagator->_journal->setFileRecordMetadata(record);
|
||||
SyncJournalFileRecord record(*_item, propagator()->_localDir + _item->_file);
|
||||
ok = propagator()->_journal->setFileRecordMetadata(record);
|
||||
if (!ok) {
|
||||
_hasError = _item->_status = SyncFileItem::FatalError;
|
||||
_item->_errorString = tr("Error writing metadata to the database");
|
||||
|
||||
@@ -56,11 +56,9 @@ class OwncloudPropagator;
|
||||
*/
|
||||
class PropagatorJob : public QObject {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
OwncloudPropagator *_propagator;
|
||||
|
||||
public:
|
||||
explicit PropagatorJob(OwncloudPropagator* propagator) : _propagator(propagator), _state(NotYetStarted) {}
|
||||
explicit PropagatorJob(OwncloudPropagator* propagator);
|
||||
|
||||
enum JobState {
|
||||
NotYetStarted,
|
||||
@@ -126,6 +124,8 @@ signals:
|
||||
|
||||
void progress(const SyncFileItem& item, quint64 bytes);
|
||||
|
||||
protected:
|
||||
OwncloudPropagator *propagator() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -223,7 +223,11 @@ int SqlQuery::prepare( const QString& sql, bool allow_failure )
|
||||
if( _errId != SQLITE_OK ) {
|
||||
_error = QString::fromUtf8(sqlite3_errmsg(_db));
|
||||
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
|
||||
Q_ASSERT(allow_failure || !"SQLITE Prepare error");
|
||||
if (!allow_failure) {
|
||||
qFatal("SQLITE Prepare error: %s in %s",
|
||||
_error.toLocal8Bit().data(),
|
||||
sql.toLocal8Bit().data());
|
||||
}
|
||||
}
|
||||
}
|
||||
return _errId;
|
||||
|
||||
@@ -310,10 +310,10 @@ QString GETFileJob::errorString() const
|
||||
|
||||
void PropagateDownloadFile::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _item->_file << _propagator->_activeJobList.count();
|
||||
qDebug() << Q_FUNC_INFO << _item->_file << propagator()->_activeJobList.count();
|
||||
_stopwatch.start();
|
||||
|
||||
if (_deleteExisting) {
|
||||
@@ -326,7 +326,7 @@ void PropagateDownloadFile::start()
|
||||
}
|
||||
|
||||
// do a klaas' case clash check.
|
||||
if( _propagator->localFileNameClash(_item->_file) ) {
|
||||
if( propagator()->localFileNameClash(_item->_file) ) {
|
||||
done( SyncFileItem::NormalError, tr("File %1 can not be downloaded because of a local file name clash!")
|
||||
.arg(QDir::toNativeSeparators(_item->_file)) );
|
||||
return;
|
||||
@@ -336,12 +336,12 @@ void PropagateDownloadFile::start()
|
||||
|
||||
QString tmpFileName;
|
||||
QByteArray expectedEtagForResume;
|
||||
const SyncJournalDb::DownloadInfo progressInfo = _propagator->_journal->getDownloadInfo(_item->_file);
|
||||
const SyncJournalDb::DownloadInfo progressInfo = propagator()->_journal->getDownloadInfo(_item->_file);
|
||||
if (progressInfo._valid) {
|
||||
// if the etag has changed meanwhile, remove the already downloaded part.
|
||||
if (progressInfo._etag != _item->_etag) {
|
||||
FileSystem::remove(_propagator->getFilePath(progressInfo._tmpfile));
|
||||
_propagator->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
FileSystem::remove(propagator()->getFilePath(progressInfo._tmpfile));
|
||||
propagator()->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
} else {
|
||||
tmpFileName = progressInfo._tmpfile;
|
||||
expectedEtagForResume = progressInfo._etag;
|
||||
@@ -353,7 +353,7 @@ void PropagateDownloadFile::start()
|
||||
tmpFileName = createDownloadTmpFileName(_item->_file);
|
||||
}
|
||||
|
||||
_tmpFile.setFileName(_propagator->getFilePath(tmpFileName));
|
||||
_tmpFile.setFileName(propagator()->getFilePath(tmpFileName));
|
||||
if (!_tmpFile.open(QIODevice::Append | QIODevice::Unbuffered)) {
|
||||
done(SyncFileItem::NormalError, _tmpFile.errorString());
|
||||
return;
|
||||
@@ -372,7 +372,7 @@ void PropagateDownloadFile::start()
|
||||
}
|
||||
|
||||
// If there's not enough space to fully download this file, stop.
|
||||
const auto diskSpaceResult = _propagator->diskSpaceCheck();
|
||||
const auto diskSpaceResult = propagator()->diskSpaceCheck();
|
||||
if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) {
|
||||
_item->_errorMayBeBlacklisted = true;
|
||||
done(SyncFileItem::NormalError,
|
||||
@@ -391,16 +391,16 @@ void PropagateDownloadFile::start()
|
||||
pi._etag = _item->_etag;
|
||||
pi._tmpfile = tmpFileName;
|
||||
pi._valid = true;
|
||||
_propagator->_journal->setDownloadInfo(_item->_file, pi);
|
||||
_propagator->_journal->commit("download file start");
|
||||
propagator()->_journal->setDownloadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("download file start");
|
||||
}
|
||||
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
|
||||
if (_item->_directDownloadUrl.isEmpty()) {
|
||||
// Normal job, download from oC instance
|
||||
_job = new GETFileJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
_job = new GETFileJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart, this);
|
||||
} else {
|
||||
// We were provided a direct URL, use that one
|
||||
@@ -411,14 +411,14 @@ void PropagateDownloadFile::start()
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromUserInput(_item->_directDownloadUrl);
|
||||
_job = new GETFileJob(_propagator->account(),
|
||||
_job = new GETFileJob(propagator()->account(),
|
||||
url,
|
||||
&_tmpFile, headers, expectedEtagForResume, _resumeStart, this);
|
||||
}
|
||||
_job->setBandwidthManager(&_propagator->_bandwidthManager);
|
||||
_job->setBandwidthManager(&propagator()->_bandwidthManager);
|
||||
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotGetFinished()));
|
||||
connect(_job, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(slotDownloadProgress(qint64,qint64)));
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_job->start();
|
||||
}
|
||||
|
||||
@@ -438,7 +438,7 @@ void PropagateDownloadFile::setDeleteExistingFolder(bool enabled)
|
||||
const char owncloudCustomSoftErrorStringC[] = "owncloud-custom-soft-error-string";
|
||||
void PropagateDownloadFile::slotGetFinished()
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
GETFileJob *job = qobject_cast<GETFileJob *>(sender());
|
||||
Q_ASSERT(job);
|
||||
@@ -459,7 +459,7 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
const bool badRangeHeader = job->resumeStart() > 0 && _item->_httpErrorCode == 416;
|
||||
if (badRangeHeader) {
|
||||
qDebug() << Q_FUNC_INFO << "server replied 416 to our range request, trying again without";
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
|
||||
// Getting a 404 probably means that the file was deleted on the server.
|
||||
@@ -473,7 +473,7 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
if (_tmpFile.size() == 0 || badRangeHeader || fileNotFound) {
|
||||
_tmpFile.close();
|
||||
FileSystem::remove(_tmpFile.fileName());
|
||||
_propagator->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
propagator()->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
}
|
||||
|
||||
if(!_item->_directDownloadUrl.isEmpty() && err != QNetworkReply::OperationCanceledError) {
|
||||
@@ -503,7 +503,7 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
SyncFileItem::Status status = job->errorStatus();
|
||||
if (status == SyncFileItem::NoStatus) {
|
||||
status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
}
|
||||
|
||||
done(status, job->errorString());
|
||||
@@ -544,7 +544,7 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
|
||||
if(bodySize > 0 && bodySize != _tmpFile.size() - job->resumeStart() ) {
|
||||
qDebug() << bodySize << _tmpFile.size() << job->resumeStart();
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("The file could not be downloaded completely."));
|
||||
return;
|
||||
}
|
||||
@@ -572,13 +572,13 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
void PropagateDownloadFile::slotChecksumFail( const QString& errMsg )
|
||||
{
|
||||
FileSystem::remove(_tmpFile.fileName());
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded."));
|
||||
}
|
||||
|
||||
void PropagateDownloadFile::deleteExistingFolder()
|
||||
{
|
||||
QString existingDir = _propagator->getFilePath(_item->_file);
|
||||
QString existingDir = propagator()->getFilePath(_item->_file);
|
||||
if (!QFileInfo(existingDir).isDir()) {
|
||||
return;
|
||||
}
|
||||
@@ -595,8 +595,8 @@ void PropagateDownloadFile::deleteExistingFolder()
|
||||
QString conflictDir = FileSystem::makeConflictFileName(
|
||||
existingDir, Utility::qDateTimeFromTime_t(FileSystem::getModTime(existingDir)));
|
||||
|
||||
emit _propagator->touchedFile(existingDir);
|
||||
emit _propagator->touchedFile(conflictDir);
|
||||
emit propagator()->touchedFile(existingDir);
|
||||
emit propagator()->touchedFile(conflictDir);
|
||||
QString renameError;
|
||||
if (!FileSystem::rename(existingDir, conflictDir, &renameError)) {
|
||||
done(SyncFileItem::NormalError, renameError);
|
||||
@@ -706,11 +706,11 @@ void PropagateDownloadFile::contentChecksumComputed(const QByteArray &checksumTy
|
||||
|
||||
void PropagateDownloadFile::downloadFinished()
|
||||
{
|
||||
QString fn = _propagator->getFilePath(_item->_file);
|
||||
QString fn = propagator()->getFilePath(_item->_file);
|
||||
|
||||
// In case of file name clash, report an error
|
||||
// This can happen if another parallel download saved a clashing file.
|
||||
if (_propagator->localFileNameClash(_item->_file)) {
|
||||
if (propagator()->localFileNameClash(_item->_file)) {
|
||||
done( SyncFileItem::NormalError, tr("File %1 cannot be saved because of a local file name clash!")
|
||||
.arg(QDir::toNativeSeparators(_item->_file)) );
|
||||
return;
|
||||
@@ -730,7 +730,7 @@ void PropagateDownloadFile::downloadFinished()
|
||||
// If the file is locked, we want to retry this sync when it
|
||||
// becomes available again.
|
||||
if (FileSystem::isFileLocked(fn)) {
|
||||
emit _propagator->seenLockedFile(fn);
|
||||
emit propagator()->seenLockedFile(fn);
|
||||
}
|
||||
|
||||
done(SyncFileItem::SoftError, renameError);
|
||||
@@ -759,7 +759,7 @@ void PropagateDownloadFile::downloadFinished()
|
||||
const qint64 expectedSize = _item->log._other_size;
|
||||
const time_t expectedMtime = _item->log._other_modtime;
|
||||
if (! FileSystem::verifyFileUnchanged(fn, expectedSize, expectedMtime)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("File has changed since discovery"));
|
||||
return;
|
||||
}
|
||||
@@ -769,13 +769,13 @@ void PropagateDownloadFile::downloadFinished()
|
||||
// Older server versions sometimes provide empty remote permissions
|
||||
// see #4450 - don't adjust the write permissions there.
|
||||
const int serverVersionGoodRemotePerm = 0x070000; // 7.0.0
|
||||
if (_propagator->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
|
||||
if (propagator()->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
|
||||
FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(),
|
||||
!_item->_remotePerm.contains('W'));
|
||||
}
|
||||
|
||||
QString error;
|
||||
emit _propagator->touchedFile(fn);
|
||||
emit propagator()->touchedFile(fn);
|
||||
// The fileChanged() check is done above to generate better error messages.
|
||||
if (!FileSystem::uncheckedRenameReplace(_tmpFile.fileName(), fn, &error)) {
|
||||
qDebug() << Q_FUNC_INFO << QString("Rename failed: %1 => %2").arg(_tmpFile.fileName()).arg(fn);
|
||||
@@ -788,16 +788,16 @@ void PropagateDownloadFile::downloadFinished()
|
||||
// which makes it look like we're just about to initially download
|
||||
// it.
|
||||
if (isConflict) {
|
||||
_propagator->_journal->deleteFileRecord(fn);
|
||||
_propagator->_journal->commit("download finished");
|
||||
propagator()->_journal->deleteFileRecord(fn);
|
||||
propagator()->_journal->commit("download finished");
|
||||
}
|
||||
|
||||
// If the file is locked, we want to retry this sync when it
|
||||
// becomes available again, otherwise try again directly
|
||||
if (FileSystem::isFileLocked(fn)) {
|
||||
emit _propagator->seenLockedFile(fn);
|
||||
emit propagator()->seenLockedFile(fn);
|
||||
} else {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
|
||||
done(SyncFileItem::SoftError, error);
|
||||
@@ -809,19 +809,19 @@ void PropagateDownloadFile::downloadFinished()
|
||||
// Get up to date information for the journal.
|
||||
_item->_size = FileSystem::getSize(fn);
|
||||
|
||||
if (!_propagator->_journal->setFileRecord(SyncJournalFileRecord(*_item, fn))) {
|
||||
if (!propagator()->_journal->setFileRecord(SyncJournalFileRecord(*_item, fn))) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
_propagator->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
_propagator->_journal->commit("download file start2");
|
||||
propagator()->_journal->setDownloadInfo(_item->_file, SyncJournalDb::DownloadInfo());
|
||||
propagator()->_journal->commit("download file start2");
|
||||
done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success);
|
||||
|
||||
// handle the special recall file
|
||||
if(!_item->_remotePerm.contains("S")
|
||||
&& (_item->_file == QLatin1String(".sys.admin#recall#")
|
||||
|| _item->_file.endsWith("/.sys.admin#recall#"))) {
|
||||
handleRecallFile(fn, _propagator->_localDir, *_propagator->_journal);
|
||||
handleRecallFile(fn, propagator()->_localDir, *propagator()->_journal);
|
||||
}
|
||||
|
||||
qint64 duration = _stopwatch.elapsed();
|
||||
|
||||
@@ -58,16 +58,16 @@ bool DeleteJob::finished()
|
||||
|
||||
void PropagateRemoteDelete::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _item->_file;
|
||||
|
||||
_job = new DeleteJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
_job = new DeleteJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
this);
|
||||
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotDeleteJobFinished()));
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_job->start();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ void PropagateRemoteDelete::abort()
|
||||
|
||||
void PropagateRemoteDelete::slotDeleteJobFinished()
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
Q_ASSERT(_job);
|
||||
|
||||
@@ -99,7 +99,7 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
|
||||
}
|
||||
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
done(status, _job->errorString());
|
||||
return;
|
||||
}
|
||||
@@ -120,8 +120,8 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
|
||||
return;
|
||||
}
|
||||
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile, _item->_isDirectory);
|
||||
_propagator->_journal->commit("Remote Remove");
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile, _item->_isDirectory);
|
||||
propagator()->_journal->commit("Remote Remove");
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,19 +23,19 @@ namespace OCC {
|
||||
|
||||
void PropagateRemoteMkdir::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _item->_file;
|
||||
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
|
||||
if (!_deleteExisting) {
|
||||
return slotStartMkcolJob();
|
||||
}
|
||||
|
||||
_job = new DeleteJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
_job = new DeleteJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
this);
|
||||
connect(_job, SIGNAL(finishedSignal()), SLOT(slotStartMkcolJob()));
|
||||
_job->start();
|
||||
@@ -43,13 +43,13 @@ void PropagateRemoteMkdir::start()
|
||||
|
||||
void PropagateRemoteMkdir::slotStartMkcolJob()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _item->_file;
|
||||
|
||||
_job = new MkColJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
_job = new MkColJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
this);
|
||||
connect(_job, SIGNAL(finished(QNetworkReply::NetworkError)), this, SLOT(slotMkcolJobFinished()));
|
||||
_job->start();
|
||||
@@ -68,7 +68,7 @@ void PropagateRemoteMkdir::setDeleteExisting(bool enabled)
|
||||
|
||||
void PropagateRemoteMkdir::slotMkcolJobFinished()
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
Q_ASSERT(_job);
|
||||
|
||||
@@ -83,7 +83,7 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
|
||||
// This happens when the directory already exists. Nothing to do.
|
||||
} else if (err != QNetworkReply::NoError) {
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
auto errorString = _job->reply()->errorString();
|
||||
if (_job->reply()->hasRawHeader("OC-ErrorString")) {
|
||||
errorString = _job->reply()->rawHeader("OC-ErrorString");
|
||||
@@ -109,7 +109,7 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
|
||||
// So we must get the file id using a PROPFIND
|
||||
// This is required so that we can detect moves even if the folder is renamed on the server
|
||||
// while files are still uploading
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
auto propfindJob = new PropfindJob(_job->account(), _job->path(), this);
|
||||
propfindJob->setProperties(QList<QByteArray>() << "getetag" << "http://owncloud.org/ns:id");
|
||||
QObject::connect(propfindJob, SIGNAL(result(QVariantMap)), this, SLOT(propfindResult(QVariantMap)));
|
||||
@@ -123,7 +123,7 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
|
||||
|
||||
void PropagateRemoteMkdir::propfindResult(const QVariantMap &result)
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
if (result.contains("getetag")) {
|
||||
_item->_etag = result["getetag"].toByteArray();
|
||||
}
|
||||
@@ -136,15 +136,15 @@ void PropagateRemoteMkdir::propfindResult(const QVariantMap &result)
|
||||
void PropagateRemoteMkdir::propfindError()
|
||||
{
|
||||
// ignore the PROPFIND error
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
void PropagateRemoteMkdir::success()
|
||||
{
|
||||
// save the file id already so we can detect rename or remove
|
||||
SyncJournalFileRecord record(*_item, _propagator->_localDir + _item->destination());
|
||||
if (!_propagator->_journal->setFileRecord(record)) {
|
||||
SyncJournalFileRecord record(*_item, propagator()->_localDir + _item->destination());
|
||||
if (!propagator()->_journal->setFileRecord(record)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,12 +71,12 @@ bool MoveJob::finished()
|
||||
|
||||
void PropagateRemoteMove::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _item->_file << _item->_renameTarget;
|
||||
|
||||
QString targetFile(_propagator->getFilePath(_item->_renameTarget));
|
||||
QString targetFile(propagator()->getFilePath(_item->_renameTarget));
|
||||
|
||||
if (_item->_file == _item->_renameTarget) {
|
||||
// The parent has been renamed already so there is nothing more to do.
|
||||
@@ -87,11 +87,11 @@ void PropagateRemoteMove::start()
|
||||
// Before owncloud 7, there was no permissions system. At the time all the shared files were
|
||||
// in a directory called "Shared" and were not supposed to be moved, otherwise bad things happened
|
||||
|
||||
QString versionString = _propagator->account()->serverVersion();
|
||||
QString versionString = propagator()->account()->serverVersion();
|
||||
if (versionString.contains('.') && versionString.split('.')[0].toInt() < 7) {
|
||||
QString originalFile(_propagator->getFilePath(QLatin1String("Shared")));
|
||||
emit _propagator->touchedFile(originalFile);
|
||||
emit _propagator->touchedFile(targetFile);
|
||||
QString originalFile(propagator()->getFilePath(QLatin1String("Shared")));
|
||||
emit propagator()->touchedFile(originalFile);
|
||||
emit propagator()->touchedFile(targetFile);
|
||||
QString renameError;
|
||||
if( FileSystem::rename(targetFile, originalFile, &renameError) ) {
|
||||
done(SyncFileItem::NormalError, tr("This folder must not be renamed. It is renamed back to its original name."));
|
||||
@@ -102,13 +102,13 @@ void PropagateRemoteMove::start()
|
||||
}
|
||||
}
|
||||
|
||||
QString destination = QDir::cleanPath(_propagator->account()->url().path() + QLatin1Char('/')
|
||||
+ _propagator->account()->davPath() + _propagator->_remoteFolder + _item->_renameTarget);
|
||||
_job = new MoveJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
QString destination = QDir::cleanPath(propagator()->account()->url().path() + QLatin1Char('/')
|
||||
+ propagator()->account()->davPath() + propagator()->_remoteFolder + _item->_renameTarget);
|
||||
_job = new MoveJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
destination, this);
|
||||
connect(_job, SIGNAL(finishedSignal()), this, SLOT(slotMoveJobFinished()));
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_job->start();
|
||||
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void PropagateRemoteMove::abort()
|
||||
|
||||
void PropagateRemoteMove::slotMoveJobFinished()
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
Q_ASSERT(_job);
|
||||
|
||||
@@ -140,7 +140,7 @@ void PropagateRemoteMove::slotMoveJobFinished()
|
||||
}
|
||||
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
done(status, _job->errorString());
|
||||
return;
|
||||
}
|
||||
@@ -164,14 +164,14 @@ void PropagateRemoteMove::slotMoveJobFinished()
|
||||
void PropagateRemoteMove::finalize()
|
||||
{
|
||||
SyncJournalFileRecord oldRecord =
|
||||
_propagator->_journal->getFileRecord(_item->_originalFile);
|
||||
propagator()->_journal->getFileRecord(_item->_originalFile);
|
||||
// if reading from db failed still continue hoping that deleteFileRecord
|
||||
// reopens the db successfully.
|
||||
// The db is only queried to transfer the content checksum from the old
|
||||
// to the new record. It is not a problem to skip it here.
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile);
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile);
|
||||
|
||||
SyncJournalFileRecord record(*_item, _propagator->getFilePath(_item->_renameTarget));
|
||||
SyncJournalFileRecord record(*_item, propagator()->getFilePath(_item->_renameTarget));
|
||||
record._path = _item->_renameTarget;
|
||||
if (oldRecord.isValid()) {
|
||||
record._contentChecksum = oldRecord._contentChecksum;
|
||||
@@ -182,19 +182,19 @@ void PropagateRemoteMove::finalize()
|
||||
}
|
||||
}
|
||||
|
||||
if (!_propagator->_journal->setFileRecord(record)) {
|
||||
if (!propagator()->_journal->setFileRecord(record)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_item->_isDirectory) {
|
||||
if (!adjustSelectiveSync(_propagator->_journal, _item->_file, _item->_renameTarget)) {
|
||||
if (!adjustSelectiveSync(propagator()->_journal, _item->_file, _item->_renameTarget)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_propagator->_journal->commit("Remote Rename");
|
||||
propagator()->_journal->commit("Remote Rename");
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,18 +193,18 @@ void PropagateUploadFileCommon::setDeleteExisting(bool enabled)
|
||||
|
||||
void PropagateUploadFileCommon::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
|
||||
if (!_deleteExisting) {
|
||||
return slotComputeContentChecksum();
|
||||
}
|
||||
|
||||
auto job = new DeleteJob(_propagator->account(),
|
||||
_propagator->_remoteFolder + _item->_file,
|
||||
auto job = new DeleteJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), SLOT(slotComputeContentChecksum()));
|
||||
@@ -214,11 +214,11 @@ void PropagateUploadFileCommon::start()
|
||||
|
||||
void PropagateUploadFileCommon::slotComputeContentChecksum()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString filePath = _propagator->getFilePath(_item->_file);
|
||||
const QString filePath = propagator()->getFilePath(_item->_file);
|
||||
|
||||
// remember the modtime before checksumming to be able to detect a file
|
||||
// change during the checksum calculation
|
||||
@@ -256,7 +256,7 @@ void PropagateUploadFileCommon::slotComputeTransmissionChecksum(const QByteArray
|
||||
|
||||
// Reuse the content checksum as the transmission checksum if possible
|
||||
const auto supportedTransmissionChecksums =
|
||||
_propagator->account()->capabilities().supportedChecksumTypes();
|
||||
propagator()->account()->capabilities().supportedChecksumTypes();
|
||||
if (supportedTransmissionChecksums.contains(contentChecksumType)) {
|
||||
slotStartUpload(contentChecksumType, contentChecksum);
|
||||
return;
|
||||
@@ -265,7 +265,7 @@ void PropagateUploadFileCommon::slotComputeTransmissionChecksum(const QByteArray
|
||||
// Compute the transmission checksum.
|
||||
auto computeChecksum = new ComputeChecksum(this);
|
||||
if (uploadChecksumEnabled()) {
|
||||
computeChecksum->setChecksumType(_propagator->account()->capabilities().uploadChecksumType());
|
||||
computeChecksum->setChecksumType(propagator()->account()->capabilities().uploadChecksumType());
|
||||
} else {
|
||||
computeChecksum->setChecksumType(QByteArray());
|
||||
}
|
||||
@@ -274,7 +274,7 @@ void PropagateUploadFileCommon::slotComputeTransmissionChecksum(const QByteArray
|
||||
SLOT(slotStartUpload(QByteArray,QByteArray)));
|
||||
connect(computeChecksum, SIGNAL(done(QByteArray,QByteArray)),
|
||||
computeChecksum, SLOT(deleteLater()));
|
||||
const QString filePath = _propagator->getFilePath(_item->_file);
|
||||
const QString filePath = propagator()->getFilePath(_item->_file);
|
||||
computeChecksum->start(filePath);
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ void PropagateUploadFileCommon::slotStartUpload(const QByteArray& transmissionCh
|
||||
{
|
||||
// Remove ourselfs from the list of active job, before any posible call to done()
|
||||
// When we start chunks, we will add it again, once for every chunks.
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
_transmissionChecksum = transmissionChecksum;
|
||||
_transmissionChecksumType = transmissionChecksumType;
|
||||
@@ -293,7 +293,7 @@ void PropagateUploadFileCommon::slotStartUpload(const QByteArray& transmissionCh
|
||||
_item->_contentChecksumType = transmissionChecksumType;
|
||||
}
|
||||
|
||||
const QString fullFilePath = _propagator->getFilePath(_item->_file);
|
||||
const QString fullFilePath = propagator()->getFilePath(_item->_file);
|
||||
|
||||
if (!FileSystem::fileExists(fullFilePath)) {
|
||||
done(SyncFileItem::SoftError, tr("File Removed"));
|
||||
@@ -307,7 +307,7 @@ void PropagateUploadFileCommon::slotStartUpload(const QByteArray& transmissionCh
|
||||
|
||||
_item->_modtime = FileSystem::getModTime(fullFilePath);
|
||||
if( prevModtime != _item->_modtime ) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
|
||||
return;
|
||||
}
|
||||
@@ -319,7 +319,7 @@ void PropagateUploadFileCommon::slotStartUpload(const QByteArray& transmissionCh
|
||||
// That usually indicates a file that is still being changed
|
||||
// or not yet fully copied to the destination.
|
||||
if (fileIsStillChanging(*_item)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("Local file changed during sync."));
|
||||
return;
|
||||
}
|
||||
@@ -464,16 +464,16 @@ void UploadDevice::setChoked(bool b) {
|
||||
|
||||
void PropagateUploadFileCommon::startPollJob(const QString& path)
|
||||
{
|
||||
PollJob* job = new PollJob(_propagator->account(), path, _item,
|
||||
_propagator->_journal, _propagator->_localDir, this);
|
||||
PollJob* job = new PollJob(propagator()->account(), path, _item,
|
||||
propagator()->_journal, propagator()->_localDir, this);
|
||||
connect(job, SIGNAL(finishedSignal()), SLOT(slotPollFinished()));
|
||||
SyncJournalDb::PollInfo info;
|
||||
info._file = _item->_file;
|
||||
info._url = path;
|
||||
info._modtime = _item->_modtime;
|
||||
_propagator->_journal->setPollInfo(info);
|
||||
_propagator->_journal->commit("add poll info");
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_journal->setPollInfo(info);
|
||||
propagator()->_journal->commit("add poll info");
|
||||
propagator()->_activeJobList.append(this);
|
||||
job->start();
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ void PropagateUploadFileCommon::slotPollFinished()
|
||||
PollJob *job = qobject_cast<PollJob *>(sender());
|
||||
Q_ASSERT(job);
|
||||
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
if (job->_item->_status != SyncFileItem::Success) {
|
||||
_finished = true;
|
||||
@@ -493,6 +493,27 @@ void PropagateUploadFileCommon::slotPollFinished()
|
||||
finalize();
|
||||
}
|
||||
|
||||
void PropagateUploadFileCommon::checkResettingErrors()
|
||||
{
|
||||
if (_item->_httpErrorCode == 412
|
||||
|| propagator()->account()->capabilities().httpErrorCodesThatResetFailingChunkedUploads()
|
||||
.contains(_item->_httpErrorCode)) {
|
||||
auto uploadInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
uploadInfo._errorCount += 1;
|
||||
if (uploadInfo._errorCount > 3) {
|
||||
qDebug() << "Reset transfer of" << _item->_file
|
||||
<< "due to repeated error" << _item->_httpErrorCode;
|
||||
uploadInfo = SyncJournalDb::UploadInfo();
|
||||
} else {
|
||||
qDebug() << "Error count for maybe-reset error" << _item->_httpErrorCode
|
||||
<< "on file" << _item->_file
|
||||
<< "is" << uploadInfo._errorCount;
|
||||
}
|
||||
propagator()->_journal->setUploadInfo(_item->_file, uploadInfo);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
}
|
||||
}
|
||||
|
||||
void PropagateUploadFileCommon::slotJobDestroyed(QObject* job)
|
||||
{
|
||||
_jobs.erase(std::remove(_jobs.begin(), _jobs.end(), job) , _jobs.end());
|
||||
@@ -551,13 +572,13 @@ void PropagateUploadFileCommon::finalize()
|
||||
_item->_requestDuration = _duration.elapsed();
|
||||
_finished = true;
|
||||
|
||||
if (!_propagator->_journal->setFileRecord(SyncJournalFileRecord(*_item, _propagator->getFilePath(_item->_file)))) {
|
||||
if (!propagator()->_journal->setFileRecord(SyncJournalFileRecord(*_item, propagator()->getFilePath(_item->_file)))) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
// Remove from the progress database:
|
||||
_propagator->_journal->setUploadInfo(_item->_file, SyncJournalDb::UploadInfo());
|
||||
_propagator->_journal->commit("upload file start");
|
||||
propagator()->_journal->setUploadInfo(_item->_file, SyncJournalDb::UploadInfo());
|
||||
propagator()->_journal->commit("upload file start");
|
||||
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
@@ -232,6 +232,13 @@ private slots:
|
||||
void slotPollFinished();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Checks whether the current error is one that should reset the whole
|
||||
* transfer if it happens too often. If so: Bump UploadInfo::errorCount
|
||||
* and maybe perform the reset.
|
||||
*/
|
||||
void checkResettingErrors();
|
||||
|
||||
// Bases headers that need to be sent with every chunk
|
||||
QMap<QByteArray, QByteArray> headers();
|
||||
|
||||
@@ -262,7 +269,7 @@ private:
|
||||
int _chunkCount; /// Total number of chunks for this file
|
||||
int _transferId; /// transfer id (part of the url)
|
||||
|
||||
quint64 chunkSize() const { return _propagator->chunkSize(); }
|
||||
quint64 chunkSize() const { return propagator()->chunkSize(); }
|
||||
|
||||
|
||||
public:
|
||||
@@ -293,9 +300,10 @@ private:
|
||||
|
||||
// Map chunk number with its size from the PROPFIND on resume.
|
||||
// (Only used from slotPropfindIterate/slotPropfindFinished because the LsColJob use signals to report data.)
|
||||
QMap<int, quint64> _serverChunks;
|
||||
struct ServerChunkInfo { quint64 size; QString originalName; };
|
||||
QMap<int, ServerChunkInfo> _serverChunks;
|
||||
|
||||
quint64 chunkSize() const { return _propagator->chunkSize(); }
|
||||
quint64 chunkSize() const { return propagator()->chunkSize(); }
|
||||
/**
|
||||
* Return the URL of a chunk.
|
||||
* If chunk == -1, returns the URL of the parent folder containing the chunks
|
||||
|
||||
@@ -38,12 +38,13 @@ namespace OCC {
|
||||
QUrl PropagateUploadFileNG::chunkUrl(int chunk)
|
||||
{
|
||||
QString path = QLatin1String("remote.php/dav/uploads/")
|
||||
+ _propagator->account()->davUser()
|
||||
+ propagator()->account()->davUser()
|
||||
+ QLatin1Char('/') + QString::number(_transferId);
|
||||
if (chunk >= 0) {
|
||||
path += QLatin1Char('/') + QString::number(chunk);
|
||||
// We need to do add leading 0 because the server orders the chunk alphabetically
|
||||
path += QLatin1Char('/') + QString::number(chunk).rightJustified(8, '0');
|
||||
}
|
||||
return Utility::concatUrlPath(_propagator->account()->url(), path);
|
||||
return Utility::concatUrlPath(propagator()->account()->url(), path);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -80,13 +81,13 @@ QUrl PropagateUploadFileNG::chunkUrl(int chunk)
|
||||
void PropagateUploadFileNG::doStartUpload()
|
||||
{
|
||||
_duration.start();
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
|
||||
const SyncJournalDb::UploadInfo progressInfo = _propagator->_journal->getUploadInfo(_item->_file);
|
||||
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
if (progressInfo._valid && Utility::qDateTimeToTime_t(progressInfo._modtime) == _item->_modtime ) {
|
||||
_transferId = progressInfo._transferid;
|
||||
auto url = chunkUrl();
|
||||
auto job = new LsColJob(_propagator->account(), url, this);
|
||||
auto job = new LsColJob(propagator()->account(), url, this);
|
||||
_jobs.append(job);
|
||||
job->setProperties(QList<QByteArray>() << "resourcetype" << "getcontentlength");
|
||||
connect(job, SIGNAL(finishedWithoutError()), this, SLOT(slotPropfindFinished()));
|
||||
@@ -108,9 +109,11 @@ void PropagateUploadFileNG::slotPropfindIterate(const QString &name, const QMap<
|
||||
return; // skip the info about the path itself
|
||||
}
|
||||
bool ok = false;
|
||||
auto chunkId = name.mid(name.lastIndexOf('/')+1).toUInt(&ok);
|
||||
QString chunkName = name.mid(name.lastIndexOf('/')+1);
|
||||
auto chunkId = chunkName.toUInt(&ok);
|
||||
if (ok) {
|
||||
_serverChunks[chunkId] = properties["getcontentlength"].toULongLong();
|
||||
ServerChunkInfo chunkinfo = { properties["getcontentlength"].toULongLong(), chunkName };
|
||||
_serverChunks[chunkId] = chunkinfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,12 +121,12 @@ void PropagateUploadFileNG::slotPropfindFinished()
|
||||
{
|
||||
auto job = qobject_cast<LsColJob *>(sender());
|
||||
slotJobDestroyed(job); // remove it from the _jobs list
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
_currentChunk = 0;
|
||||
_sent = 0;
|
||||
while (_serverChunks.contains(_currentChunk)) {
|
||||
_sent += _serverChunks[_currentChunk];
|
||||
_sent += _serverChunks[_currentChunk].size;
|
||||
_serverChunks.remove(_currentChunk);
|
||||
++_currentChunk;
|
||||
}
|
||||
@@ -141,19 +144,20 @@ void PropagateUploadFileNG::slotPropfindFinished()
|
||||
qDebug() << "Resuming "<< _item->_file << " from chunk " << _currentChunk << "; sent ="<< _sent;
|
||||
|
||||
if (!_serverChunks.isEmpty()) {
|
||||
qDebug() << "To Delete" << _serverChunks;
|
||||
_propagator->_activeJobList.append(this);
|
||||
qDebug() << "To Delete" << _serverChunks.keys();
|
||||
propagator()->_activeJobList.append(this);
|
||||
_removeJobError = false;
|
||||
|
||||
// Make sure that if there is a "hole" and then a few more chunks, on the server
|
||||
// we should remove the later chunks. Otherwise when we do dynamic chunk sizing, we may end up
|
||||
// with corruptions if there are too many chunks, or if we abort and there are still stale chunks.
|
||||
for (auto it = _serverChunks.begin(); it != _serverChunks.end(); ++it) {
|
||||
auto job = new DeleteJob(_propagator->account(), Utility::concatUrlPath(chunkUrl(), QString::number(it.key())), this);
|
||||
auto job = new DeleteJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), it->originalName), this);
|
||||
QObject::connect(job, SIGNAL(finishedSignal()), this, SLOT(slotDeleteJobFinished()));
|
||||
_jobs.append(job);
|
||||
job->start();
|
||||
}
|
||||
_serverChunks.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,9 +170,9 @@ void PropagateUploadFileNG::slotPropfindFinishedWithError()
|
||||
slotJobDestroyed(job); // remove it from the _jobs list
|
||||
QNetworkReply::NetworkError err = job->reply()->error();
|
||||
auto httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
auto status = classifyError(err, httpErrorCode, &_propagator->_anotherSyncNeeded);
|
||||
auto status = classifyError(err, httpErrorCode, &propagator()->_anotherSyncNeeded);
|
||||
if (status == SyncFileItem::FatalError) {
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
QString errorString = errorMessage(job->reply()->errorString(), job->reply()->readAll());
|
||||
abortWithError(status, errorString);
|
||||
return;
|
||||
@@ -197,7 +201,7 @@ void PropagateUploadFileNG::slotDeleteJobFinished()
|
||||
}
|
||||
|
||||
if (_jobs.isEmpty()) {
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
if (_removeJobError) {
|
||||
// There was an error removing some files, just start over
|
||||
startNewUpload();
|
||||
@@ -211,7 +215,7 @@ void PropagateUploadFileNG::slotDeleteJobFinished()
|
||||
|
||||
void PropagateUploadFileNG::startNewUpload()
|
||||
{
|
||||
Q_ASSERT(_propagator->_activeJobList.count(this) == 1);
|
||||
Q_ASSERT(propagator()->_activeJobList.count(this) == 1);
|
||||
_transferId = qrand() ^ _item->_modtime ^ (_item->_size << 16) ^ qHash(_item->_file);
|
||||
_sent = 0;
|
||||
_currentChunk = 0;
|
||||
@@ -222,11 +226,11 @@ void PropagateUploadFileNG::startNewUpload()
|
||||
pi._valid = true;
|
||||
pi._transferid = _transferId;
|
||||
pi._modtime = Utility::qDateTimeFromTime_t(_item->_modtime);
|
||||
_propagator->_journal->setUploadInfo(_item->_file, pi);
|
||||
_propagator->_journal->commit("Upload info");
|
||||
propagator()->_journal->setUploadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
headers["OC-Total-Length"] = QByteArray::number(_item->_size);
|
||||
auto job = new MkColJob(_propagator->account(), chunkUrl(), headers, this);
|
||||
auto job = new MkColJob(propagator()->account(), chunkUrl(), headers, this);
|
||||
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)),
|
||||
this, SLOT(slotMkColFinished(QNetworkReply::NetworkError)));
|
||||
@@ -236,7 +240,7 @@ void PropagateUploadFileNG::startNewUpload()
|
||||
|
||||
void PropagateUploadFileNG::slotMkColFinished(QNetworkReply::NetworkError)
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
auto job = qobject_cast<MkColJob *>(sender());
|
||||
slotJobDestroyed(job); // remove it from the _jobs list
|
||||
QNetworkReply::NetworkError err = job->reply()->error();
|
||||
@@ -244,7 +248,7 @@ void PropagateUploadFileNG::slotMkColFinished(QNetworkReply::NetworkError)
|
||||
|
||||
if (err != QNetworkReply::NoError || _item->_httpErrorCode != 201) {
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
QString errorString = errorMessage(job->reply()->errorString(), job->reply()->readAll());
|
||||
if (job->reply()->hasRawHeader("OC-ErrorString")) {
|
||||
errorString = job->reply()->rawHeader("OC-ErrorString");
|
||||
@@ -257,7 +261,7 @@ void PropagateUploadFileNG::slotMkColFinished(QNetworkReply::NetworkError)
|
||||
|
||||
void PropagateUploadFileNG::startNextChunk()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
quint64 fileSize = _item->_size;
|
||||
@@ -268,8 +272,8 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
Q_ASSERT(_jobs.isEmpty()); // There should be no running job anymore
|
||||
_finished = true;
|
||||
// Finish with a MOVE
|
||||
QString destination = QDir::cleanPath(_propagator->account()->url().path() + QLatin1Char('/')
|
||||
+ _propagator->account()->davPath() + _propagator->_remoteFolder + _item->_file);
|
||||
QString destination = QDir::cleanPath(propagator()->account()->url().path() + QLatin1Char('/')
|
||||
+ propagator()->account()->davPath() + propagator()->_remoteFolder + _item->_file);
|
||||
auto headers = PropagateUploadFileCommon::headers();
|
||||
|
||||
// "If-Match applies to the source, but we are interested in comparing the etag of the destination
|
||||
@@ -282,18 +286,18 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
_transmissionChecksumType, _transmissionChecksum);
|
||||
}
|
||||
|
||||
auto job = new MoveJob(_propagator->account(), Utility::concatUrlPath(chunkUrl(), "/.file"),
|
||||
auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"),
|
||||
destination, headers, this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), this, SLOT(slotMoveJobFinished()));
|
||||
connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(slotJobDestroyed(QObject*)));
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
job->start();
|
||||
return;
|
||||
}
|
||||
|
||||
auto device = new UploadDevice(&_propagator->_bandwidthManager);
|
||||
const QString fileName = _propagator->getFilePath(_item->_file);
|
||||
auto device = new UploadDevice(&propagator()->_bandwidthManager);
|
||||
const QString fileName = propagator()->getFilePath(_item->_file);
|
||||
|
||||
if (! device->prepareAndOpen(fileName, _sent, currentChunkSize)) {
|
||||
qDebug() << "ERR: Could not prepare upload device: " << device->errorString();
|
||||
@@ -301,7 +305,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
// If the file is currently locked, we want to retry the sync
|
||||
// when it becomes available again.
|
||||
if (FileSystem::isFileLocked(fileName)) {
|
||||
emit _propagator->seenLockedFile(fileName);
|
||||
emit propagator()->seenLockedFile(fileName);
|
||||
}
|
||||
// Soft error because this is likely caused by the user modifying his files while syncing
|
||||
abortWithError( SyncFileItem::SoftError, device->errorString() );
|
||||
@@ -315,7 +319,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
QUrl url = chunkUrl(_currentChunk);
|
||||
|
||||
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), url, device, headers, _currentChunk, this);
|
||||
PUTFileJob* job = new PUTFileJob(propagator()->account(), url, device, headers, _currentChunk, this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), this, SLOT(slotPutFinished()));
|
||||
connect(job, SIGNAL(uploadProgress(qint64,qint64)),
|
||||
@@ -324,7 +328,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
device, SLOT(slotJobUploadProgress(qint64,qint64)));
|
||||
connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(slotJobDestroyed(QObject*)));
|
||||
job->start();
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_currentChunk++;
|
||||
|
||||
// FIXME! parallel chunk?
|
||||
@@ -343,7 +347,7 @@ void PropagateUploadFileNG::slotPutFinished()
|
||||
<< job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute)
|
||||
<< job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
||||
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
if (_finished) {
|
||||
// We have sent the finished signal already. We don't need to handle any remaining jobs
|
||||
@@ -357,7 +361,7 @@ void PropagateUploadFileNG::slotPutFinished()
|
||||
// Abort the job and try again later.
|
||||
// This works around a bug in QNAM wich might reuse a non-empty buffer for the next request.
|
||||
qDebug() << "Forcing job abort on HTTP connection reset with Qt < 5.4.2.";
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
abortWithError(SyncFileItem::SoftError, tr("Forcing job abort on HTTP connection reset with Qt < 5.4.2."));
|
||||
return;
|
||||
}
|
||||
@@ -373,16 +377,11 @@ void PropagateUploadFileNG::slotPutFinished()
|
||||
errorString = job->reply()->rawHeader("OC-ErrorString");
|
||||
}
|
||||
|
||||
// FIXME! can this happen for the chunks?
|
||||
if (_item->_httpErrorCode == 412) {
|
||||
// Precondition Failed: Maybe the bad etag is in the database, we need to clear the
|
||||
// parent folder etag so we won't read from DB next sync.
|
||||
_propagator->_journal->avoidReadFromDbOnNextSync(_item->_file);
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
}
|
||||
// Ensure errors that should eventually reset the chunked upload are tracked.
|
||||
checkResettingErrors();
|
||||
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
abortWithError(status, errorString);
|
||||
return;
|
||||
}
|
||||
@@ -391,19 +390,19 @@ void PropagateUploadFileNG::slotPutFinished()
|
||||
bool finished = _sent == _item->_size;
|
||||
|
||||
// Check if the file still exists
|
||||
const QString fullFilePath(_propagator->getFilePath(_item->_file));
|
||||
const QString fullFilePath(propagator()->getFilePath(_item->_file));
|
||||
if( !FileSystem::fileExists(fullFilePath) ) {
|
||||
if (!finished) {
|
||||
abortWithError(SyncFileItem::SoftError, tr("The local file was removed during sync."));
|
||||
return;
|
||||
} else {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the file changed since discovery.
|
||||
if (! FileSystem::verifyFileUnchanged(fullFilePath, _item->_size, _item->_modtime)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
if( !finished ) {
|
||||
abortWithError(SyncFileItem::SoftError, tr("Local file changed during sync."));
|
||||
return;
|
||||
@@ -413,24 +412,42 @@ void PropagateUploadFileNG::slotPutFinished()
|
||||
if (!finished) {
|
||||
// Deletes an existing blacklist entry on successful chunk upload
|
||||
if (_item->_hasBlacklistEntry) {
|
||||
_propagator->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
propagator()->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
_item->_hasBlacklistEntry = false;
|
||||
}
|
||||
|
||||
// Reset the error count on successful chunk upload
|
||||
auto uploadInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
uploadInfo._errorCount = 0;
|
||||
propagator()->_journal->setUploadInfo(_item->_file, uploadInfo);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
}
|
||||
startNextChunk();
|
||||
}
|
||||
|
||||
void PropagateUploadFileNG::slotMoveJobFinished()
|
||||
{
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
auto job = qobject_cast<MoveJob *>(sender());
|
||||
slotJobDestroyed(job); // remove it from the _jobs list
|
||||
QNetworkReply::NetworkError err = job->reply()->error();
|
||||
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (err != QNetworkReply::NoError) {
|
||||
if (_item->_httpErrorCode == 412) {
|
||||
// Precondition Failed: Either an etag or a checksum mismatch.
|
||||
|
||||
// Maybe the bad etag is in the database, we need to clear the
|
||||
// parent folder etag so we won't read from DB next sync.
|
||||
propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file);
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
|
||||
// Ensure errors that should eventually reset the chunked upload are tracked.
|
||||
checkResettingErrors();
|
||||
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
QString errorString = errorMessage(job->errorString(), job->reply()->readAll());
|
||||
abortWithError(status, errorString);
|
||||
return;
|
||||
|
||||
@@ -40,7 +40,7 @@ void PropagateUploadFileV1::doStartUpload()
|
||||
_startChunk = 0;
|
||||
_transferId = qrand() ^ _item->_modtime ^ (_item->_size << 16);
|
||||
|
||||
const SyncJournalDb::UploadInfo progressInfo = _propagator->_journal->getUploadInfo(_item->_file);
|
||||
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
|
||||
if (progressInfo._valid && Utility::qDateTimeToTime_t(progressInfo._modtime) == _item->_modtime ) {
|
||||
_startChunk = progressInfo._chunk;
|
||||
@@ -57,7 +57,7 @@ void PropagateUploadFileV1::doStartUpload()
|
||||
|
||||
void PropagateUploadFileV1::startNextChunk()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
if (! _jobs.isEmpty() && _currentChunk + _startChunk >= _chunkCount - 1) {
|
||||
@@ -75,7 +75,7 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
|
||||
QString path = _item->_file;
|
||||
|
||||
UploadDevice *device = new UploadDevice(&_propagator->_bandwidthManager);
|
||||
UploadDevice *device = new UploadDevice(&propagator()->_bandwidthManager);
|
||||
qint64 chunkStart = 0;
|
||||
qint64 currentChunkSize = fileSize;
|
||||
bool isFinalChunk = false;
|
||||
@@ -101,20 +101,21 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
// if there's only one chunk, it's the final one
|
||||
isFinalChunk = true;
|
||||
}
|
||||
qDebug() << _chunkCount << isFinalChunk << chunkStart << currentChunkSize;
|
||||
|
||||
if (isFinalChunk && !_transmissionChecksumType.isEmpty()) {
|
||||
headers[checkSumHeaderC] = makeChecksumHeader(
|
||||
_transmissionChecksumType, _transmissionChecksum);
|
||||
}
|
||||
|
||||
const QString fileName = _propagator->getFilePath(_item->_file);
|
||||
const QString fileName = propagator()->getFilePath(_item->_file);
|
||||
if (! device->prepareAndOpen(fileName, chunkStart, currentChunkSize)) {
|
||||
qDebug() << "ERR: Could not prepare upload device: " << device->errorString();
|
||||
|
||||
// If the file is currently locked, we want to retry the sync
|
||||
// when it becomes available again.
|
||||
if (FileSystem::isFileLocked(fileName)) {
|
||||
emit _propagator->seenLockedFile(fileName);
|
||||
emit propagator()->seenLockedFile(fileName);
|
||||
}
|
||||
// Soft error because this is likely caused by the user modifying his files while syncing
|
||||
abortWithError( SyncFileItem::SoftError, device->errorString() );
|
||||
@@ -123,19 +124,19 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
}
|
||||
|
||||
// job takes ownership of device via a QScopedPointer. Job deletes itself when finishing
|
||||
PUTFileJob* job = new PUTFileJob(_propagator->account(), _propagator->_remoteFolder + path, device, headers, _currentChunk, this);
|
||||
PUTFileJob* job = new PUTFileJob(propagator()->account(), propagator()->_remoteFolder + path, device, headers, _currentChunk, this);
|
||||
_jobs.append(job);
|
||||
connect(job, SIGNAL(finishedSignal()), this, SLOT(slotPutFinished()));
|
||||
connect(job, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(slotUploadProgress(qint64,qint64)));
|
||||
connect(job, SIGNAL(uploadProgress(qint64,qint64)), device, SLOT(slotJobUploadProgress(qint64,qint64)));
|
||||
connect(job, SIGNAL(destroyed(QObject*)), this, SLOT(slotJobDestroyed(QObject*)));
|
||||
job->start();
|
||||
_propagator->_activeJobList.append(this);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_currentChunk++;
|
||||
|
||||
bool parallelChunkUpload = true;
|
||||
|
||||
if (_propagator->account()->capabilities().chunkingParallelUploadDisabled()) {
|
||||
if (propagator()->account()->capabilities().chunkingParallelUploadDisabled()) {
|
||||
// Server may also disable parallel chunked upload for any higher version
|
||||
parallelChunkUpload = false;
|
||||
} else {
|
||||
@@ -143,7 +144,7 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
if (!env.isEmpty()) {
|
||||
parallelChunkUpload = env != "false" && env != "0";
|
||||
} else {
|
||||
int versionNum = _propagator->account()->serverVersionInt();
|
||||
int versionNum = propagator()->account()->serverVersionInt();
|
||||
if (versionNum < 0x080003) {
|
||||
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
|
||||
// internal sever errors (#2743, #2938)
|
||||
@@ -159,7 +160,7 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
parallelChunkUpload = false;
|
||||
}
|
||||
|
||||
if (parallelChunkUpload && (_propagator->_activeJobList.count() < _propagator->maximumActiveJob())
|
||||
if (parallelChunkUpload && (propagator()->_activeJobList.count() < propagator()->maximumActiveJob())
|
||||
&& _currentChunk < _chunkCount ) {
|
||||
startNextChunk();
|
||||
}
|
||||
@@ -180,7 +181,7 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
<< job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute)
|
||||
<< job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
||||
|
||||
_propagator->_activeJobList.removeOne(this);
|
||||
propagator()->_activeJobList.removeOne(this);
|
||||
|
||||
if (_finished) {
|
||||
// We have sent the finished signal already. We don't need to handle any remaining jobs
|
||||
@@ -193,7 +194,7 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
if (err == QNetworkReply::OperationCanceledError && job->reply()->property("owncloud-should-soft-cancel").isValid()) { // Abort the job and try again later.
|
||||
// This works around a bug in QNAM wich might reuse a non-empty buffer for the next request.
|
||||
qDebug() << "Forcing job abort on HTTP connection reset with Qt < 5.4.2.";
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
abortWithError(SyncFileItem::SoftError, tr("Forcing job abort on HTTP connection reset with Qt < 5.4.2."));
|
||||
return;
|
||||
}
|
||||
@@ -215,14 +216,19 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
}
|
||||
|
||||
if (_item->_httpErrorCode == 412) {
|
||||
// Precondition Failed: Maybe the bad etag is in the database, we need to clear the
|
||||
// Precondition Failed: Either an etag or a checksum mismatch.
|
||||
|
||||
// Maybe the bad etag is in the database, we need to clear the
|
||||
// parent folder etag so we won't read from DB next sync.
|
||||
_propagator->_journal->avoidReadFromDbOnNextSync(_item->_file);
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file);
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
|
||||
// Ensure errors that should eventually reset the chunked upload are tracked.
|
||||
checkResettingErrors();
|
||||
|
||||
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
|
||||
&_propagator->_anotherSyncNeeded);
|
||||
&propagator()->_anotherSyncNeeded);
|
||||
abortWithError(status, errorString);
|
||||
return;
|
||||
}
|
||||
@@ -253,19 +259,19 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
bool finished = etag.length() > 0;
|
||||
|
||||
// Check if the file still exists
|
||||
const QString fullFilePath(_propagator->getFilePath(_item->_file));
|
||||
const QString fullFilePath(propagator()->getFilePath(_item->_file));
|
||||
if( !FileSystem::fileExists(fullFilePath) ) {
|
||||
if (!finished) {
|
||||
abortWithError(SyncFileItem::SoftError, tr("The local file was removed during sync."));
|
||||
return;
|
||||
} else {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the file changed since discovery.
|
||||
if (! FileSystem::verifyFileUnchanged(fullFilePath, _item->_size, _item->_modtime)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
propagator()->_anotherSyncNeeded = true;
|
||||
if( !finished ) {
|
||||
abortWithError(SyncFileItem::SoftError, tr("Local file changed during sync."));
|
||||
// FIXME: the legacy code was retrying for a few seconds.
|
||||
@@ -288,7 +294,7 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
|
||||
// Deletes an existing blacklist entry on successful chunk upload
|
||||
if (_item->_hasBlacklistEntry) {
|
||||
_propagator->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
propagator()->_journal->wipeErrorBlacklistEntry(_item->_file);
|
||||
_item->_hasBlacklistEntry = false;
|
||||
}
|
||||
|
||||
@@ -304,8 +310,9 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
pi._chunk = (currentChunk + _startChunk + 1) % _chunkCount ; // next chunk to start with
|
||||
pi._transferid = _transferId;
|
||||
pi._modtime = Utility::qDateTimeFromTime_t(_item->_modtime);
|
||||
_propagator->_journal->setUploadInfo(_item->_file, pi);
|
||||
_propagator->_journal->commit("Upload info");
|
||||
pi._errorCount = 0; // successful chunk upload resets
|
||||
propagator()->_journal->setUploadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
startNextChunk();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ namespace OCC {
|
||||
* in the database. But in case of error, we need to remove the entries from the database of the files
|
||||
* that were deleted.
|
||||
*
|
||||
* \a path is relative to _propagator->_localDir + _item->_file and should start with a slash
|
||||
* \a path is relative to propagator()->_localDir + _item->_file and should start with a slash
|
||||
*/
|
||||
bool PropagateLocalRemove::removeRecursively(const QString& path)
|
||||
{
|
||||
bool success = true;
|
||||
QString absolute = _propagator->_localDir + _item->_file + path;
|
||||
QString absolute = propagator()->_localDir + _item->_file + path;
|
||||
QDirIterator di(absolute, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
|
||||
|
||||
QVector<QPair<QString, bool>> deleted;
|
||||
@@ -77,7 +77,7 @@ bool PropagateLocalRemove::removeRecursively(const QString& path)
|
||||
if (success && !ok) {
|
||||
// We need to delete the entries from the database now from the deleted vector
|
||||
foreach(const auto &it, deleted) {
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + it.first,
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + it.first,
|
||||
it.second);
|
||||
}
|
||||
success = false;
|
||||
@@ -88,7 +88,7 @@ bool PropagateLocalRemove::removeRecursively(const QString& path)
|
||||
}
|
||||
if (!success && ok) {
|
||||
// This succeeded, so we need to delete it from the database now because the caller won't
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + di.fileName(),
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile + path + QLatin1Char('/') + di.fileName(),
|
||||
isDir);
|
||||
}
|
||||
}
|
||||
@@ -105,14 +105,14 @@ bool PropagateLocalRemove::removeRecursively(const QString& path)
|
||||
|
||||
void PropagateLocalRemove::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
QString filename = _propagator->_localDir + _item->_file;
|
||||
QString filename = propagator()->_localDir + _item->_file;
|
||||
|
||||
qDebug() << filename;
|
||||
|
||||
if( _propagator->localFileNameClash(_item->_file)) {
|
||||
if( propagator()->localFileNameClash(_item->_file)) {
|
||||
done(SyncFileItem::NormalError, tr("Could not remove %1 because of a local file name clash")
|
||||
.arg(QDir::toNativeSeparators(filename)));
|
||||
return;
|
||||
@@ -132,17 +132,17 @@ void PropagateLocalRemove::start()
|
||||
}
|
||||
}
|
||||
emit progress(*_item, 0);
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile, _item->_isDirectory);
|
||||
_propagator->_journal->commit("Local remove");
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile, _item->_isDirectory);
|
||||
propagator()->_journal->commit("Local remove");
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
void PropagateLocalMkdir::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
QDir newDir(_propagator->getFilePath(_item->_file));
|
||||
QDir newDir(propagator()->getFilePath(_item->_file));
|
||||
QString newDirStr = QDir::toNativeSeparators(newDir.path());
|
||||
|
||||
// When turning something that used to be a file into a directory
|
||||
@@ -158,13 +158,13 @@ void PropagateLocalMkdir::start()
|
||||
}
|
||||
}
|
||||
|
||||
if( Utility::fsCasePreserving() && _propagator->localFileNameClash(_item->_file ) ) {
|
||||
if( Utility::fsCasePreserving() && propagator()->localFileNameClash(_item->_file ) ) {
|
||||
qDebug() << "WARN: new folder to create locally already exists!";
|
||||
done( SyncFileItem::NormalError, tr("Attention, possible case sensitivity clash with %1").arg(newDirStr) );
|
||||
return;
|
||||
}
|
||||
emit _propagator->touchedFile(newDirStr);
|
||||
QDir localDir(_propagator->_localDir);
|
||||
emit propagator()->touchedFile(newDirStr);
|
||||
QDir localDir(propagator()->_localDir);
|
||||
if (!localDir.mkpath(_item->_file)) {
|
||||
done( SyncFileItem::NormalError, tr("could not create folder %1").arg(newDirStr) );
|
||||
return;
|
||||
@@ -177,11 +177,11 @@ void PropagateLocalMkdir::start()
|
||||
// before the correct etag is stored.
|
||||
SyncJournalFileRecord record(*_item, newDirStr);
|
||||
record._etag = "_invalid_";
|
||||
if (!_propagator->_journal->setFileRecord(record)) {
|
||||
if (!propagator()->_journal->setFileRecord(record)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
_propagator->_journal->commit("localMkdir");
|
||||
propagator()->_journal->commit("localMkdir");
|
||||
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
@@ -193,11 +193,11 @@ void PropagateLocalMkdir::setDeleteExistingFile(bool enabled)
|
||||
|
||||
void PropagateLocalRename::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0))
|
||||
if (propagator()->_abortRequested.fetchAndAddRelaxed(0))
|
||||
return;
|
||||
|
||||
QString existingFile = _propagator->getFilePath(_item->_file);
|
||||
QString targetFile = _propagator->getFilePath(_item->_renameTarget);
|
||||
QString existingFile = propagator()->getFilePath(_item->_file);
|
||||
QString targetFile = propagator()->getFilePath(_item->_renameTarget);
|
||||
|
||||
// if the file is a file underneath a moved dir, the _item->file is equal
|
||||
// to _item->renameTarget and the file is not moved as a result.
|
||||
@@ -206,7 +206,7 @@ void PropagateLocalRename::start()
|
||||
qDebug() << "MOVE " << existingFile << " => " << targetFile;
|
||||
|
||||
if (QString::compare(_item->_file, _item->_renameTarget, Qt::CaseInsensitive) != 0
|
||||
&& _propagator->localFileNameClash(_item->_renameTarget)) {
|
||||
&& propagator()->localFileNameClash(_item->_renameTarget)) {
|
||||
// Only use localFileNameClash for the destination if we know that the source was not
|
||||
// the one conflicting (renaming A.txt -> a.txt is OK)
|
||||
|
||||
@@ -217,8 +217,8 @@ void PropagateLocalRename::start()
|
||||
return;
|
||||
}
|
||||
|
||||
emit _propagator->touchedFile(existingFile);
|
||||
emit _propagator->touchedFile(targetFile);
|
||||
emit propagator()->touchedFile(existingFile);
|
||||
emit propagator()->touchedFile(targetFile);
|
||||
QString renameError;
|
||||
if (!FileSystem::rename(existingFile, targetFile, &renameError)) {
|
||||
done(SyncFileItem::NormalError, renameError);
|
||||
@@ -227,8 +227,8 @@ void PropagateLocalRename::start()
|
||||
}
|
||||
|
||||
SyncJournalFileRecord oldRecord =
|
||||
_propagator->_journal->getFileRecord(_item->_originalFile);
|
||||
_propagator->_journal->deleteFileRecord(_item->_originalFile);
|
||||
propagator()->_journal->getFileRecord(_item->_originalFile);
|
||||
propagator()->_journal->deleteFileRecord(_item->_originalFile);
|
||||
|
||||
// store the rename file name in the item.
|
||||
const auto oldFile = _item->_file;
|
||||
@@ -242,18 +242,18 @@ void PropagateLocalRename::start()
|
||||
}
|
||||
|
||||
if (!_item->_isDirectory) { // Directories are saved at the end
|
||||
if (!_propagator->_journal->setFileRecord(record)) {
|
||||
if (!propagator()->_journal->setFileRecord(record)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!PropagateRemoteMove::adjustSelectiveSync(_propagator->_journal, oldFile, _item->_renameTarget)) {
|
||||
if (!PropagateRemoteMove::adjustSelectiveSync(propagator()->_journal, oldFile, _item->_renameTarget)) {
|
||||
done(SyncFileItem::FatalError, tr("Error writing metadata to the database"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_propagator->_journal->commit("localRename");
|
||||
propagator()->_journal->commit("localRename");
|
||||
|
||||
done(SyncFileItem::Success);
|
||||
}
|
||||
|
||||
@@ -440,7 +440,23 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
item->_errorString = tr("File is listed on the ignore list.");
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS:
|
||||
item->_errorString = tr("Filename contains invalid characters that can not be synced cross platform.");
|
||||
if (item->_file.endsWith('.')) {
|
||||
item->_errorString = tr("File names ending with a period are not supported on this file system.");
|
||||
} else {
|
||||
char invalid = '\0';
|
||||
foreach(char x, QByteArray("\\:?*\"<>|")) {
|
||||
if (item->_file.contains(x)) {
|
||||
invalid = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (invalid) {
|
||||
item->_errorString = tr("File names containing the character '%1' are not supported on this file system.")
|
||||
.arg(QLatin1Char(invalid));
|
||||
} else {
|
||||
item->_errorString = tr("The file name is a reserved name on this file system.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_TRAILING_SPACE:
|
||||
item->_errorString = tr("Filename contains trailing spaces.");
|
||||
|
||||
@@ -129,42 +129,46 @@ void SyncFileStatusTracker::slotPathTouched(const QString& fileName)
|
||||
emit fileStatusChanged(fileName, SyncFileStatus::StatusSync);
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::incSyncCount(const QString &relativePath, EmitStatusChangeFlag emitStatusChange)
|
||||
void SyncFileStatusTracker::incSyncCountAndEmitStatusChanged(const QString &relativePath, SharedFlag sharedFlag)
|
||||
{
|
||||
// Will return 0 (and increase to 1) if the path wasn't in the map yet
|
||||
int count = _syncCount[relativePath]++;
|
||||
if (!count) {
|
||||
if (emitStatusChange)
|
||||
emit fileStatusChanged(getSystemDestination(relativePath), fileStatus(relativePath));
|
||||
SyncFileStatus status = sharedFlag == UnknownShared
|
||||
? fileStatus(relativePath)
|
||||
: resolveSyncAndErrorStatus(relativePath, sharedFlag);
|
||||
emit fileStatusChanged(getSystemDestination(relativePath), status);
|
||||
|
||||
// We passed from OK to SYNC, increment the parent to keep it marked as
|
||||
// SYNC while we propagate ourselves and our own children.
|
||||
Q_ASSERT(!relativePath.endsWith('/'));
|
||||
int lastSlashIndex = relativePath.lastIndexOf('/');
|
||||
if (lastSlashIndex != -1)
|
||||
incSyncCount(relativePath.left(lastSlashIndex), EmitStatusChange);
|
||||
incSyncCountAndEmitStatusChanged(relativePath.left(lastSlashIndex), UnknownShared);
|
||||
else if (!relativePath.isEmpty())
|
||||
incSyncCount(QString(), EmitStatusChange);
|
||||
incSyncCountAndEmitStatusChanged(QString(), UnknownShared);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::decSyncCount(const QString &relativePath, EmitStatusChangeFlag emitStatusChange)
|
||||
void SyncFileStatusTracker::decSyncCountAndEmitStatusChanged(const QString &relativePath, SharedFlag sharedFlag)
|
||||
{
|
||||
int count = --_syncCount[relativePath];
|
||||
if (!count) {
|
||||
// Remove from the map, same as 0
|
||||
_syncCount.remove(relativePath);
|
||||
|
||||
if (emitStatusChange)
|
||||
emit fileStatusChanged(getSystemDestination(relativePath), fileStatus(relativePath));
|
||||
SyncFileStatus status = sharedFlag == UnknownShared
|
||||
? fileStatus(relativePath)
|
||||
: resolveSyncAndErrorStatus(relativePath, sharedFlag);
|
||||
emit fileStatusChanged(getSystemDestination(relativePath), status);
|
||||
|
||||
// We passed from SYNC to OK, decrement our parent.
|
||||
Q_ASSERT(!relativePath.endsWith('/'));
|
||||
int lastSlashIndex = relativePath.lastIndexOf('/');
|
||||
if (lastSlashIndex != -1)
|
||||
decSyncCount(relativePath.left(lastSlashIndex), EmitStatusChange);
|
||||
decSyncCountAndEmitStatusChanged(relativePath.left(lastSlashIndex), UnknownShared);
|
||||
else if (!relativePath.isEmpty())
|
||||
decSyncCount(QString(), EmitStatusChange);
|
||||
decSyncCountAndEmitStatusChanged(QString(), UnknownShared);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +181,7 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
|
||||
foreach (const SyncFileItemPtr &item, items) {
|
||||
// qDebug() << Q_FUNC_INFO << "Investigating" << item->destination() << item->_status << item->_instruction;
|
||||
_dirtyPaths.remove(item->destination());
|
||||
|
||||
if (showErrorInSocketApi(*item)) {
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusError;
|
||||
@@ -185,18 +190,16 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusWarning;
|
||||
}
|
||||
|
||||
// Mark this path as syncing for instructions that will result in propagation,
|
||||
// but DontEmitStatusChange since we're going to emit for ourselves using the
|
||||
// info in the SyncFileItem we received, parents will still be emit if needed.
|
||||
SharedFlag sharedFlag = item->_remotePerm.contains("S") ? Shared : NotShared;
|
||||
if (item->_instruction != CSYNC_INSTRUCTION_NONE
|
||||
&& item->_instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
|
||||
&& item->_instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& item->_instruction != CSYNC_INSTRUCTION_ERROR) {
|
||||
incSyncCount(item->destination(), DontEmitStatusChange);
|
||||
// Mark this path as syncing for instructions that will result in propagation.
|
||||
incSyncCountAndEmitStatusChanged(item->destination(), sharedFlag);
|
||||
} else {
|
||||
emit fileStatusChanged(getSystemDestination(item->destination()), resolveSyncAndErrorStatus(item->destination(), sharedFlag));
|
||||
}
|
||||
|
||||
_dirtyPaths.remove(item->destination());
|
||||
emit fileStatusChanged(getSystemDestination(item->destination()), resolveSyncAndErrorStatus(item->destination(), item->_remotePerm.contains("S") ? Shared : NotShared));
|
||||
}
|
||||
|
||||
// Some metadata status won't trigger files to be synced, make sure that we
|
||||
@@ -233,14 +236,16 @@ void SyncFileStatusTracker::slotItemCompleted(const SyncFileItem &item)
|
||||
_syncProblems.erase(item._file);
|
||||
}
|
||||
|
||||
// decSyncCount calls *must* be symetric with incSyncCount calls in slotAboutToPropagate
|
||||
SharedFlag sharedFlag = item._remotePerm.contains("S") ? Shared : NotShared;
|
||||
if (item._instruction != CSYNC_INSTRUCTION_NONE
|
||||
&& item._instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
|
||||
&& item._instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& item._instruction != CSYNC_INSTRUCTION_ERROR) {
|
||||
decSyncCount(item.destination(), DontEmitStatusChange);
|
||||
// decSyncCount calls *must* be symetric with incSyncCount calls in slotAboutToPropagate
|
||||
decSyncCountAndEmitStatusChanged(item.destination(), sharedFlag);
|
||||
} else {
|
||||
emit fileStatusChanged(getSystemDestination(item.destination()), resolveSyncAndErrorStatus(item.destination(), sharedFlag));
|
||||
}
|
||||
emit fileStatusChanged(getSystemDestination(item.destination()), resolveSyncAndErrorStatus(item.destination(), item._remotePerm.contains("S") ? Shared : NotShared));
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotSyncFinished()
|
||||
@@ -254,10 +259,10 @@ void SyncFileStatusTracker::slotSyncFinished()
|
||||
|
||||
void SyncFileStatusTracker::slotSyncEngineRunningChanged()
|
||||
{
|
||||
emit fileStatusChanged(_syncEngine->localPath(), resolveSyncAndErrorStatus(QString(), NotShared));
|
||||
emit fileStatusChanged(getSystemDestination(QString()), resolveSyncAndErrorStatus(QString(), NotShared));
|
||||
}
|
||||
|
||||
SyncFileStatus SyncFileStatusTracker::resolveSyncAndErrorStatus(const QString &relativePath, SharedFlag isShared, PathKnownFlag isPathKnown)
|
||||
SyncFileStatus SyncFileStatusTracker::resolveSyncAndErrorStatus(const QString &relativePath, SharedFlag sharedFlag, PathKnownFlag isPathKnown)
|
||||
{
|
||||
// If it's a new file and that we're not syncing it yet,
|
||||
// don't show any icon and wait for the filesystem watcher to trigger a sync.
|
||||
@@ -272,7 +277,9 @@ SyncFileStatus SyncFileStatusTracker::resolveSyncAndErrorStatus(const QString &r
|
||||
status.set(problemStatus);
|
||||
}
|
||||
|
||||
if (isShared)
|
||||
// The shared status needs to have been fetched from a SyncFileItem or the DB at this point.
|
||||
Q_ASSERT(sharedFlag != UnknownShared);
|
||||
if (sharedFlag == Shared)
|
||||
status.setSharedWithMe(true);
|
||||
|
||||
return status;
|
||||
|
||||
@@ -51,20 +51,22 @@ private slots:
|
||||
void slotSyncEngineRunningChanged();
|
||||
|
||||
private:
|
||||
enum SharedFlag { NotShared = 0, Shared };
|
||||
enum SharedFlag { UnknownShared, NotShared, Shared };
|
||||
enum PathKnownFlag { PathUnknown = 0, PathKnown };
|
||||
enum EmitStatusChangeFlag { DontEmitStatusChange = 0, EmitStatusChange };
|
||||
SyncFileStatus resolveSyncAndErrorStatus(const QString &relativePath, SharedFlag isShared, PathKnownFlag isPathKnown = PathKnown);
|
||||
SyncFileStatus resolveSyncAndErrorStatus(const QString &relativePath, SharedFlag sharedState, PathKnownFlag isPathKnown = PathKnown);
|
||||
|
||||
void invalidateParentPaths(const QString& path);
|
||||
QString getSystemDestination(const QString& relativePath);
|
||||
void incSyncCount(const QString &relativePath, EmitStatusChangeFlag emitStatusChange);
|
||||
void decSyncCount(const QString &relativePath, EmitStatusChangeFlag emitStatusChange);
|
||||
void incSyncCountAndEmitStatusChanged(const QString &relativePath, SharedFlag sharedState);
|
||||
void decSyncCountAndEmitStatusChanged(const QString &relativePath, SharedFlag sharedState);
|
||||
|
||||
SyncEngine* _syncEngine;
|
||||
|
||||
std::map<QString, SyncFileStatus::SyncFileStatusTag> _syncProblems;
|
||||
QSet<QString> _dirtyPaths;
|
||||
// Counts the number direct children currently being synced (has unfinished propagation jobs).
|
||||
// We'll show a file/directory as SYNC as long as its sync count is > 0.
|
||||
// A directory that starts/ends propagation will in turn increase/decrease its own parent by 1.
|
||||
QHash<QString, int> _syncCount;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,8 +64,12 @@ bool SyncJournalDb::maybeMigrateDb(const QString& localPath, const QString& abso
|
||||
if( !FileSystem::fileExists(oldDbName) ) {
|
||||
return true;
|
||||
}
|
||||
const QString oldDbNameShm = oldDbName + "-shm";
|
||||
const QString oldDbNameWal = oldDbName + "-wal";
|
||||
|
||||
const QString newDbName = absoluteJournalPath;
|
||||
const QString newDbNameShm = newDbName + "-shm";
|
||||
const QString newDbNameWal = newDbName + "-wal";
|
||||
|
||||
// Whenever there is an old db file, migrate it to the new db path.
|
||||
// This is done to make switching from older versions to newer versions
|
||||
@@ -80,12 +84,36 @@ bool SyncJournalDb::maybeMigrateDb(const QString& localPath, const QString& abso
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( FileSystem::fileExists( newDbNameWal ) ) {
|
||||
if( !FileSystem::remove(newDbNameWal, &error) ) {
|
||||
qDebug() << "Database migration: Could not remove db WAL file" << newDbNameWal
|
||||
<< "due to" << error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if( FileSystem::fileExists( newDbNameShm ) ) {
|
||||
if( !FileSystem::remove(newDbNameShm, &error) ) {
|
||||
qDebug() << "Database migration: Could not remove db SHM file" << newDbNameShm
|
||||
<< "due to" << error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( !FileSystem::rename(oldDbName, newDbName, &error) ) {
|
||||
qDebug() << "Database migration: could not rename " << oldDbName
|
||||
<< "to" << newDbName << ":" << error;
|
||||
return false;
|
||||
}
|
||||
if( !FileSystem::rename(oldDbNameWal, newDbNameWal, &error) ) {
|
||||
qDebug() << "Database migration: could not rename " << oldDbNameWal
|
||||
<< "to" << newDbNameWal << ":" << error;
|
||||
return false;
|
||||
}
|
||||
if( !FileSystem::rename(oldDbNameShm, newDbNameShm, &error) ) {
|
||||
qDebug() << "Database migration: could not rename " << oldDbNameShm
|
||||
<< "to" << newDbNameShm << ":" << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Journal successfully migrated from" << oldDbName << "to" << newDbName;
|
||||
return true;
|
||||
|
||||
@@ -121,7 +121,7 @@ bool SyncJournalErrorBlacklistRecord::isValid() const
|
||||
{
|
||||
return ! _file.isEmpty()
|
||||
&& (!_lastTryEtag.isEmpty() || _lastTryModtime != 0)
|
||||
&& _lastTryTime > 0 && _ignoreDuration > 0;
|
||||
&& _lastTryTime > 0;
|
||||
}
|
||||
|
||||
SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
|
||||
@@ -163,6 +163,7 @@ SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
|
||||
qDebug() << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";
|
||||
entry._ignoreDuration = maxBlacklistTime;
|
||||
}
|
||||
|
||||
entry._ignoreDuration = qBound(minBlacklistTime, entry._ignoreDuration, maxBlacklistTime);
|
||||
|
||||
qDebug() << "blacklisting " << item._file
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
._*
|
||||
]Thumbs.db
|
||||
desktop.ini
|
||||
System Volume Information
|
||||
|
||||
.*.sw?
|
||||
.*.*sw?
|
||||
|
||||
@@ -2,6 +2,7 @@ include_directories(${CMAKE_BINARY_DIR}/csync ${CMAKE_BINARY_DIR}/csync/src ${CM
|
||||
include_directories(${CMAKE_SOURCE_DIR}/csync/src/)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/csync/src/std ${CMAKE_SOURCE_DIR}/src)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/qtokenizer)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
|
||||
include(QtVersionAbstraction)
|
||||
setup_qt()
|
||||
@@ -48,6 +49,7 @@ if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
|
||||
owncloud_add_test(SyncEngine "syncenginetestutils.h")
|
||||
owncloud_add_test(SyncFileStatusTracker "syncenginetestutils.h")
|
||||
owncloud_add_test(ChunkingNg "syncenginetestutils.h")
|
||||
owncloud_add_test(UploadReset "syncenginetestutils.h")
|
||||
endif(HAVE_QT5 AND NOT BUILD_WITH_QT4)
|
||||
|
||||
SET(FolderMan_SRC ../src/gui/folderman.cpp)
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
virtual void appendByte(const QString &relativePath) = 0;
|
||||
virtual void mkdir(const QString &relativePath) = 0;
|
||||
virtual void rename(const QString &relativePath, const QString &relativeDestinationDirectory) = 0;
|
||||
virtual void setModTime(const QString &relativePath, const QDateTime &modTime) = 0;
|
||||
};
|
||||
|
||||
class DiskFileModifier : public FileModifier
|
||||
@@ -114,6 +115,9 @@ public:
|
||||
QVERIFY(_rootDir.exists(from));
|
||||
QVERIFY(_rootDir.rename(from, to));
|
||||
}
|
||||
void setModTime(const QString &relativePath, const QDateTime &modTime) override {
|
||||
OCC::FileSystem::setModTime(_rootDir.filePath(relativePath), OCC::Utility::qDateTimeToTime_t(modTime));
|
||||
}
|
||||
};
|
||||
|
||||
class FileInfo : public FileModifier
|
||||
@@ -201,6 +205,12 @@ public:
|
||||
dir->children.insert(newPathComponents.fileName(), std::move(fi));
|
||||
}
|
||||
|
||||
void setModTime(const QString &relativePath, const QDateTime &modTime) override {
|
||||
FileInfo *file = findInvalidatingEtags(relativePath);
|
||||
Q_ASSERT(file);
|
||||
file->lastModified = modTime;
|
||||
}
|
||||
|
||||
FileInfo *find(const PathComponents &pathComponents, const bool invalidateEtags = false) {
|
||||
if (pathComponents.isEmpty()) {
|
||||
if (invalidateEtags)
|
||||
@@ -579,9 +589,10 @@ public:
|
||||
char payload = '*';
|
||||
|
||||
do {
|
||||
if (!sourceFolder->children.contains(QString::number(count)))
|
||||
QString chunkName = QString::number(count).rightJustified(8, '0');
|
||||
if (!sourceFolder->children.contains(chunkName))
|
||||
break;
|
||||
auto &x = sourceFolder->children[QString::number(count)];
|
||||
auto &x = sourceFolder->children[chunkName];
|
||||
Q_ASSERT(!x.isDir);
|
||||
Q_ASSERT(x.size > 0); // There should not be empty chunks
|
||||
size += x.size;
|
||||
@@ -641,6 +652,7 @@ public:
|
||||
|
||||
Q_INVOKABLE void respond() {
|
||||
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 500);
|
||||
setError(InternalServerError, "Internal Server Fake Error");
|
||||
emit metaDataChanged();
|
||||
emit finished();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,24 @@ public:
|
||||
}
|
||||
return SyncFileStatus();
|
||||
}
|
||||
|
||||
bool statusEmittedBefore(const QString &firstPath, const QString &secondPath) const {
|
||||
QFileInfo firstFile(_syncEngine.localPath(), firstPath);
|
||||
QFileInfo secondFile(_syncEngine.localPath(), secondPath);
|
||||
// Start from the end to get the latest status
|
||||
int i = size() - 1;
|
||||
for (; i >= 0; --i) {
|
||||
if (QFileInfo(at(i)[0].toString()) == secondFile)
|
||||
break;
|
||||
else if (QFileInfo(at(i)[0].toString()) == firstFile)
|
||||
return false;
|
||||
}
|
||||
for (; i >= 0; --i) {
|
||||
if (QFileInfo(at(i)[0].toString()) == firstFile)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class TestSyncFileStatusTracker : public QObject
|
||||
@@ -369,6 +387,28 @@ private slots:
|
||||
QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a"), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
}
|
||||
|
||||
// Even for status pushes immediately following each other, macOS
|
||||
// can sometimes have 1s delays between updates, so make sure that
|
||||
// children are marked as OK before their parents do.
|
||||
void childOKEmittedBeforeParent() {
|
||||
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
||||
fakeFolder.localModifier().appendByte("B/b1");
|
||||
fakeFolder.remoteModifier().appendByte("C/c1");
|
||||
StatusPushSpy statusSpy(fakeFolder.syncEngine());
|
||||
|
||||
fakeFolder.syncOnce();
|
||||
verifyThatPushMatchesPull(fakeFolder, statusSpy);
|
||||
QVERIFY(statusSpy.statusEmittedBefore("B/b1", "B"));
|
||||
QVERIFY(statusSpy.statusEmittedBefore("C/c1", "C"));
|
||||
QVERIFY(statusSpy.statusEmittedBefore("B", ""));
|
||||
QVERIFY(statusSpy.statusEmittedBefore("C", ""));
|
||||
QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusUpToDate));
|
||||
}
|
||||
|
||||
void sharedStatus() {
|
||||
SyncFileStatus sharedUpToDateStatus(SyncFileStatus::StatusUpToDate);
|
||||
sharedUpToDateStatus.setSharedWithMe(true);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This software is in the public domain, furnished "as is", without technical
|
||||
* support, and with no warranty, express or implied, as to its usefulness for
|
||||
* any purpose.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtTest>
|
||||
#include "syncenginetestutils.h"
|
||||
#include <syncengine.h>
|
||||
#include <syncjournaldb.h>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
class TestUploadReset : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
|
||||
// Verify that the chunked transfer eventually gets reset with the new chunking
|
||||
void testFileUploadNg() {
|
||||
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
||||
|
||||
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{
|
||||
{"chunking", "1.0"},
|
||||
{"httpErrorCodesThatResetFailingChunkedUploads", QVariantList{500} } } } });
|
||||
|
||||
const int size = 100 * 1000 * 1000; // 100 MB
|
||||
fakeFolder.localModifier().insert("A/a0", size);
|
||||
QDateTime modTime = QDateTime::currentDateTime();
|
||||
fakeFolder.localModifier().setModTime("A/a0", modTime);
|
||||
|
||||
// Create a transfer id, so we can make the final MOVE fail
|
||||
SyncJournalDb::UploadInfo uploadInfo;
|
||||
uploadInfo._transferid = 1;
|
||||
uploadInfo._valid = true;
|
||||
uploadInfo._modtime = modTime;
|
||||
fakeFolder.syncEngine().journal()->setUploadInfo("A/a0", uploadInfo);
|
||||
|
||||
fakeFolder.uploadState().mkdir("1");
|
||||
fakeFolder.serverErrorPaths().append("1/.file");
|
||||
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
|
||||
uploadInfo = fakeFolder.syncEngine().journal()->getUploadInfo("A/a0");
|
||||
QCOMPARE(uploadInfo._errorCount, 1);
|
||||
QCOMPARE(uploadInfo._transferid, 1);
|
||||
|
||||
fakeFolder.syncEngine().journal()->wipeErrorBlacklist();
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
|
||||
uploadInfo = fakeFolder.syncEngine().journal()->getUploadInfo("A/a0");
|
||||
QCOMPARE(uploadInfo._errorCount, 2);
|
||||
QCOMPARE(uploadInfo._transferid, 1);
|
||||
|
||||
fakeFolder.syncEngine().journal()->wipeErrorBlacklist();
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
|
||||
uploadInfo = fakeFolder.syncEngine().journal()->getUploadInfo("A/a0");
|
||||
QCOMPARE(uploadInfo._errorCount, 3);
|
||||
QCOMPARE(uploadInfo._transferid, 1);
|
||||
|
||||
fakeFolder.syncEngine().journal()->wipeErrorBlacklist();
|
||||
QVERIFY(!fakeFolder.syncOnce());
|
||||
|
||||
uploadInfo = fakeFolder.syncEngine().journal()->getUploadInfo("A/a0");
|
||||
QCOMPARE(uploadInfo._errorCount, 0);
|
||||
QCOMPARE(uploadInfo._transferid, 0);
|
||||
QVERIFY(!uploadInfo._valid);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestUploadReset)
|
||||
#include "testuploadreset.moc"
|
||||
+137
-122
@@ -547,7 +547,7 @@
|
||||
<context>
|
||||
<name>OCC::CleanupPollsJob</name>
|
||||
<message>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="768"/>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="779"/>
|
||||
<source>Error writing metadata to the database</source>
|
||||
<translation>Error en escriure les metadades a la base de dades</translation>
|
||||
</message>
|
||||
@@ -604,160 +604,160 @@
|
||||
<context>
|
||||
<name>OCC::Folder</name>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="143"/>
|
||||
<location filename="../src/gui/folder.cpp" line="141"/>
|
||||
<source>Local folder %1 does not exist.</source>
|
||||
<translation>El fitxer local %1 no existeix.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="146"/>
|
||||
<location filename="../src/gui/folder.cpp" line="144"/>
|
||||
<source>%1 should be a folder but is not.</source>
|
||||
<translation>%1 hauria de ser una carpeta, però no ho és.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="149"/>
|
||||
<location filename="../src/gui/folder.cpp" line="147"/>
|
||||
<source>%1 is not readable.</source>
|
||||
<translation>No es pot llegir %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="352"/>
|
||||
<location filename="../src/gui/folder.cpp" line="350"/>
|
||||
<source>%1: %2</source>
|
||||
<extracomment>this displays an error string (%2) for a file %1</extracomment>
|
||||
<translation>%1: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="459"/>
|
||||
<location filename="../src/gui/folder.cpp" line="457"/>
|
||||
<source>%1 has been removed.</source>
|
||||
<comment>%1 names a file.</comment>
|
||||
<translation>S'ha esborrat '%1'</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="466"/>
|
||||
<location filename="../src/gui/folder.cpp" line="464"/>
|
||||
<source>%1 has been downloaded.</source>
|
||||
<comment>%1 names a file.</comment>
|
||||
<translation>S'ha descarregat %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="473"/>
|
||||
<location filename="../src/gui/folder.cpp" line="471"/>
|
||||
<source>%1 has been updated.</source>
|
||||
<comment>%1 names a file.</comment>
|
||||
<translation>S'ha actualitzat %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="480"/>
|
||||
<location filename="../src/gui/folder.cpp" line="478"/>
|
||||
<source>%1 has been renamed to %2.</source>
|
||||
<comment>%1 and %2 name files.</comment>
|
||||
<translation>%1 s'ha reanomenat a %2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="487"/>
|
||||
<location filename="../src/gui/folder.cpp" line="485"/>
|
||||
<source>%1 has been moved to %2.</source>
|
||||
<translation>%1 s'ha mogut a %2.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="457"/>
|
||||
<location filename="../src/gui/folder.cpp" line="455"/>
|
||||
<source>%1 and %n other file(s) have been removed.</source>
|
||||
<translation><numerusform>%1 i %n altre fitxer s'ha esborrat.</numerusform><numerusform>%1 i %n altres fitxers s'han esborrat.</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="464"/>
|
||||
<location filename="../src/gui/folder.cpp" line="462"/>
|
||||
<source>%1 and %n other file(s) have been downloaded.</source>
|
||||
<translation><numerusform>%1 i %n altre fitxer s'han descarregat.</numerusform><numerusform>%1 i %n altres fitxers s'han descarregat.</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="471"/>
|
||||
<location filename="../src/gui/folder.cpp" line="469"/>
|
||||
<source>%1 and %n other file(s) have been updated.</source>
|
||||
<translation><numerusform>%1 i %n altre fitxer s'han actualitzat.</numerusform><numerusform>%1 i %n altres fitxers s'han actualitzat.</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="478"/>
|
||||
<location filename="../src/gui/folder.cpp" line="476"/>
|
||||
<source>%1 has been renamed to %2 and %n other file(s) have been renamed.</source>
|
||||
<translation><numerusform>%1 s'ha reanomenat a %2 i %n altre fitxer s'ha reanomenat.</numerusform><numerusform>%1 s'ha reanomenat a %2 i %n altres fitxers s'han reanomenat.</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="485"/>
|
||||
<location filename="../src/gui/folder.cpp" line="483"/>
|
||||
<source>%1 has been moved to %2 and %n other file(s) have been moved.</source>
|
||||
<translation><numerusform>%1 s'ha mogut a %2 i %n altre fitxer s'ha mogut.</numerusform><numerusform>%1 s'ha mogut a %2 i %n altres fitxers s'han mogut.</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="492"/>
|
||||
<location filename="../src/gui/folder.cpp" line="490"/>
|
||||
<source>%1 has and %n other file(s) have sync conflicts.</source>
|
||||
<translation><numerusform>%1 i %n altre fitxer tenen conflictes de sincronització</numerusform><numerusform>%1 i %n altres fitxers tenen conflictes de sincronització</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="494"/>
|
||||
<location filename="../src/gui/folder.cpp" line="492"/>
|
||||
<source>%1 has a sync conflict. Please check the conflict file!</source>
|
||||
<translation>%1 té conflictes de sincronització. Comproveu el fitxer conflictiu!</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../src/gui/folder.cpp" line="499"/>
|
||||
<location filename="../src/gui/folder.cpp" line="497"/>
|
||||
<source>%1 and %n other file(s) could not be synced due to errors. See the log for details.</source>
|
||||
<translation><numerusform>%1 i %n altre fitxer no s'han sincronitzat per errors.
|
||||
Consulteu el registre per obtenir més informació.</numerusform><numerusform>%1 i %n altres fitxers no s'han sincronitzat per errors.
|
||||
Consulteu el registre per obtenir més informació.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="501"/>
|
||||
<location filename="../src/gui/folder.cpp" line="499"/>
|
||||
<source>%1 could not be synced due to an error. See the log for details.</source>
|
||||
<translation>%1 no s'ha pogut sincronitzar degut a un error. Mireu el registre per més detalls.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="507"/>
|
||||
<location filename="../src/gui/folder.cpp" line="505"/>
|
||||
<source>Sync Activity</source>
|
||||
<translation>Activitat de sincronització</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="746"/>
|
||||
<location filename="../src/gui/folder.cpp" line="744"/>
|
||||
<source>Could not read system exclude file</source>
|
||||
<translation>No s'ha pogut llegir el fitxer d'exclusió del sistema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="958"/>
|
||||
<location filename="../src/gui/folder.cpp" line="956"/>
|
||||
<source>A new folder larger than %1 MB has been added: %2.
|
||||
Please go in the settings to select it if you wish to download it.</source>
|
||||
<translation>S'ha afegit una carpeta nova de més de %1 MB: %2.
|
||||
Aneu a arranjament per seleccionar-la si voleu descarregar-la.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="996"/>
|
||||
<location filename="../src/gui/folder.cpp" line="994"/>
|
||||
<source>This sync would remove all the files in the sync folder '%1'.
|
||||
This might be because the folder was silently reconfigured, or that all the files were manually removed.
|
||||
Are you sure you want to perform this operation?</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1000"/>
|
||||
<location filename="../src/gui/folder.cpp" line="998"/>
|
||||
<source>Remove All Files?</source>
|
||||
<translation>Esborra tots els fitxers?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1002"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1000"/>
|
||||
<source>Remove all files</source>
|
||||
<translation>Esborra tots els fitxers</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1003"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1001"/>
|
||||
<source>Keep files</source>
|
||||
<translation>Mantén els fitxers</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1019"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1017"/>
|
||||
<source>This sync would reset the files to an earlier time in the sync folder '%1'.
|
||||
This might be because a backup was restored on the server.
|
||||
Continuing the sync as normal will cause all your files to be overwritten by an older file in an earlier state. Do you want to keep your local most recent files as conflict files?</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1024"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1022"/>
|
||||
<source>Backup detected</source>
|
||||
<translation>Copia de seguretat detectada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1026"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1024"/>
|
||||
<source>Normal Synchronisation</source>
|
||||
<translation>Sincronització normal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folder.cpp" line="1027"/>
|
||||
<location filename="../src/gui/folder.cpp" line="1025"/>
|
||||
<source>Keep Local Files as Conflict</source>
|
||||
<translation>Manté els fitxers locals com a conflicte</translation>
|
||||
</message>
|
||||
@@ -1406,27 +1406,27 @@ Els elements que poden ser eliminats s'eliminaran si impedeixen que una car
|
||||
<context>
|
||||
<name>OCC::NSISUpdater</name>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="331"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="334"/>
|
||||
<source>New Version Available</source>
|
||||
<translation>Versió nova disponible</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="337"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="340"/>
|
||||
<source><p>A new version of the %1 Client is available.</p><p><b>%2</b> is available for download. The installed version is %3.</p></source>
|
||||
<translation><p>Hi ha una nova versió del client %1 disponible.</p><p><b>%2</b> està disponible per a la baixada. La versió instal·lada és la %3.</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="350"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="353"/>
|
||||
<source>Skip this version</source>
|
||||
<translation>Omet aquesta versió</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="351"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="354"/>
|
||||
<source>Skip this time</source>
|
||||
<translation>Omet aquesta vegada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="352"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="355"/>
|
||||
<source>Get update</source>
|
||||
<translation>Obtingueu l'actualització</translation>
|
||||
</message>
|
||||
@@ -1572,59 +1572,59 @@ Els elements que poden ser eliminats s'eliminaran si impedeixen que una car
|
||||
<context>
|
||||
<name>OCC::OCUpdater</name>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="103"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="106"/>
|
||||
<source>New %1 Update Ready</source>
|
||||
<translation>Nova actualització de %1 preparada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="104"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="107"/>
|
||||
<source>A new update for %1 is about to be installed. The updater may ask
|
||||
for additional privileges during the process.</source>
|
||||
<translation>S'instal·lara un nova actualització per %1. L'actualitzador pot demanar
|
||||
privilegis addicionals durant el procés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="141"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="144"/>
|
||||
<source>Downloading version %1. Please wait...</source>
|
||||
<translation>Descarregat la versió %1. Espeu si us play...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="145"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="148"/>
|
||||
<source>Could not download update. Please click <a href='%1'>here</a> to download the update manually.</source>
|
||||
<translation>No s'ha pogut decarregar l'actualització. Si us plau, cliqueu <a href='%1'>aqui</a>per descarregar l'actualització manualment.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="147"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="150"/>
|
||||
<source>Could not check for new updates.</source>
|
||||
<translation>No s'ha pogut comprovar si hi ha actualitzacions.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="143"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="146"/>
|
||||
<source>%1 version %2 available. Restart application to start the update.</source>
|
||||
<translation>Versió %2 de %1 disponible. Reinicieu l'aplicació per començar l'actualització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="149"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="152"/>
|
||||
<source>New %1 version %2 available. Please use the system's update tool to install it.</source>
|
||||
<translation>Nova versió %1 de %2 disponible. Utilitzeu l'eina d'actualització del sistema per instal·lar-la.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="151"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="154"/>
|
||||
<source>Checking update server...</source>
|
||||
<translation>Comprovació d'actualitzacions al servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="153"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="156"/>
|
||||
<source>Update status is unknown: Did not check for new updates.</source>
|
||||
<translation>Es desconeix l'estat de les actualitzacions: no s'ha comprovat si hi ha actualitzacions noves.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="157"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="160"/>
|
||||
<source>No updates available. Your installation is at the latest version.</source>
|
||||
<translation>No hi ha actualitzacions disponibles. La instal·lació ja es troba en la versió més recent.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="177"/>
|
||||
<location filename="../src/gui/updater/ocupdater.cpp" line="180"/>
|
||||
<source>Update Check</source>
|
||||
<translation>Comprova les actualitzacions</translation>
|
||||
</message>
|
||||
@@ -1762,134 +1762,139 @@ No és aconsellada usar-la.</translation>
|
||||
<translation><font color="green">S'ha connectat correctament amb %1: %2 versió %3 (%4)</font><br/><br/></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="210"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="214"/>
|
||||
<source>Failed to connect to %1 at %2:<br/>%3</source>
|
||||
<translation>Ha fallat la connexió amb %1 a %2:<br/>%3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="242"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="247"/>
|
||||
<source>Timeout while trying to connect to %1 at %2.</source>
|
||||
<translation>S'ha esgotat el temps d'espera mentres es conectava a %1 a les %2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="253"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="258"/>
|
||||
<source>Trying to connect to %1 at %2...</source>
|
||||
<translation>Intentant connectar amb %1 a %2...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="300"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="305"/>
|
||||
<source>The authenticated request to the server was redirected to '%1'. The URL is bad, the server is misconfigured.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="322"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="327"/>
|
||||
<source>There was an invalid response to an authenticated webdav request</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="313"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="318"/>
|
||||
<source>Access forbidden by server. To verify that you have proper access, <a href="%1">click here</a> to access the service with your browser.</source>
|
||||
<translation>El servidor ha prohibit l'accés. Per verificar que teniu permisos, <a href="%1">cliqueu aquí</a> per accedir al servei amb el vostre navegador.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="364"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="212"/>
|
||||
<source>Invalid URL</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="369"/>
|
||||
<source>Local sync folder %1 already exists, setting it up for sync.<br/><br/></source>
|
||||
<translation>La carpeta local %1 ja existeix, s'està configurant per sincronitzar.<br/><br/></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="366"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="371"/>
|
||||
<source>Creating local sync folder %1...</source>
|
||||
<translation>S'està creant la carpeta de sincronització local %1...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="370"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="375"/>
|
||||
<source>ok</source>
|
||||
<translation>correcte</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="372"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="377"/>
|
||||
<source>failed.</source>
|
||||
<translation>ha fallat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="374"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="379"/>
|
||||
<source>Could not create local folder %1</source>
|
||||
<translation>No s'ha pogut crear la carpeta local %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="399"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="404"/>
|
||||
<source>No remote folder specified!</source>
|
||||
<translation>No heu especificat cap carpeta remota!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="405"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="410"/>
|
||||
<source>Error: %1</source>
|
||||
<translation>Error: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="418"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="423"/>
|
||||
<source>creating folder on ownCloud: %1</source>
|
||||
<translation>creant la carpeta a ownCloud: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="434"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="439"/>
|
||||
<source>Remote folder %1 created successfully.</source>
|
||||
<translation>La carpeta remota %1 s'ha creat correctament.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="436"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="441"/>
|
||||
<source>The remote folder %1 already exists. Connecting it for syncing.</source>
|
||||
<translation>La carpeta remota %1 ja existeix. S'hi està connectant per sincronitzar-les.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="438"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="440"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="443"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="445"/>
|
||||
<source>The folder creation resulted in HTTP error code %1</source>
|
||||
<translation>La creació de la carpeta ha resultat en el codi d'error HTTP %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="442"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="447"/>
|
||||
<source>The remote folder creation failed because the provided credentials are wrong!<br/>Please go back and check your credentials.</p></source>
|
||||
<translation>Ha fallat la creació de la carpeta perquè les credencials proporcionades són incorrectes!<br/>Aneu enrera i comproveu les credencials.</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="445"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="450"/>
|
||||
<source><p><font color="red">Remote folder creation failed probably because the provided credentials are wrong.</font><br/>Please go back and check your credentials.</p></source>
|
||||
<translation><p><font color="red">La creació de la carpeta remota ha fallat, probablement perquè les credencials facilitades són incorrectes.</font><br/>Comproveu les vostres credencials.</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="450"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="451"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="455"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="456"/>
|
||||
<source>Remote folder %1 creation failed with error <tt>%2</tt>.</source>
|
||||
<translation>La creació de la carpeta remota %1 ha fallat amb l'error <tt>%2</tt>.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="467"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="472"/>
|
||||
<source>A sync connection from %1 to remote directory %2 was set up.</source>
|
||||
<translation>S'ha establert una connexió de sincronització des de %1 a la carpeta remota %2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="472"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="477"/>
|
||||
<source>Successfully connected to %1!</source>
|
||||
<translation>Connectat amb èxit a %1!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="479"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="484"/>
|
||||
<source>Connection to %1 could not be established. Please check again.</source>
|
||||
<translation>No s'ha pogut establir la connexió amb %1. Comproveu-ho de nou.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="492"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="497"/>
|
||||
<source>Folder rename failed</source>
|
||||
<translation>Ha fallat en canviar el nom de la carpeta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="493"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="498"/>
|
||||
<source>Can't remove and back up the folder because the folder or a file in it is open in another program. Please close the folder or file and hit retry or cancel the setup.</source>
|
||||
<translation>No es pot esborrar i restaurar la carpeta perquè una carpeta o un fitxer de dins està obert en un altre programa. Tanqueu la carpeta o el fitxer i intenteu-ho de nou o cancel·leu la configuració.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="535"/>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="540"/>
|
||||
<source><font color="green"><b>Local sync folder %1 successfully created!</b></font></source>
|
||||
<translation><font color="green"><b>la carpeta de sincronització %1 s'ha creat correctament!</b></font></translation>
|
||||
</message>
|
||||
@@ -1944,7 +1949,7 @@ No és aconsellada usar-la.</translation>
|
||||
<context>
|
||||
<name>OCC::PropagateDirectory</name>
|
||||
<message>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="714"/>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="725"/>
|
||||
<source>Error writing metadata to the database</source>
|
||||
<translation>Error en escriure les metadades a la base de dades</translation>
|
||||
</message>
|
||||
@@ -2000,17 +2005,17 @@ No és aconsellada usar-la.</translation>
|
||||
<context>
|
||||
<name>OCC::PropagateItemJob</name>
|
||||
<message>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="130"/>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="127"/>
|
||||
<source>; Restoration Failed: %1</source>
|
||||
<translation>; Ha fallat la restauració: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="153"/>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="152"/>
|
||||
<source>Continue blacklisting:</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="246"/>
|
||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="245"/>
|
||||
<source>A file or folder was removed from a read only share, but restoring failed: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
@@ -2143,7 +2148,7 @@ No és aconsellada usar-la.</translation>
|
||||
<translation>El fitxer local ha canviat durant la sincronització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateupload.cpp" line="555"/>
|
||||
<location filename="../src/libsync/propagateupload.cpp" line="576"/>
|
||||
<source>Error writing metadata to the database</source>
|
||||
<translation>Error en escriure les metadades a la base de dades</translation>
|
||||
</message>
|
||||
@@ -2151,32 +2156,32 @@ No és aconsellada usar-la.</translation>
|
||||
<context>
|
||||
<name>OCC::PropagateUploadFileNG</name>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="361"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="362"/>
|
||||
<source>Forcing job abort on HTTP connection reset with Qt < 5.4.2.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="397"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="393"/>
|
||||
<source>The local file was removed during sync.</source>
|
||||
<translation>El fitxer local s'ha eliminat durant la sincronització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="408"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="404"/>
|
||||
<source>Local file changed during sync.</source>
|
||||
<translation>El fitxer local ha canviat durant la sincronització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="439"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="453"/>
|
||||
<source>Unexpected return code from server (%1)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="446"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="460"/>
|
||||
<source>Missing File ID from server</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="459"/>
|
||||
<location filename="../src/libsync/propagateuploadng.cpp" line="473"/>
|
||||
<source>Missing ETag from server</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
@@ -2184,32 +2189,32 @@ No és aconsellada usar-la.</translation>
|
||||
<context>
|
||||
<name>OCC::PropagateUploadFileV1</name>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="197"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="198"/>
|
||||
<source>Forcing job abort on HTTP connection reset with Qt < 5.4.2.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="205"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="206"/>
|
||||
<source>The file was edited locally but is part of a read only share. It is restored and your edit is in the conflict file.</source>
|
||||
<translation>El fitxer s'ha editat localment però és part d'una compartició només de lectura. S'ha restaurat i la vostra edició és en el fitxer en conflicte.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="236"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="242"/>
|
||||
<source>Poll URL missing</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="259"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="265"/>
|
||||
<source>The local file was removed during sync.</source>
|
||||
<translation>El fitxer local s'ha eliminat durant la sincronització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="270"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="276"/>
|
||||
<source>Local file changed during sync.</source>
|
||||
<translation>El fitxer local ha canviat durant la sincronització.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="285"/>
|
||||
<location filename="../src/libsync/propagateuploadv1.cpp" line="291"/>
|
||||
<source>The server did not acknowledge the last chunk. (No e-tag was present)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
@@ -2651,7 +2656,7 @@ No és aconsellada usar-la.</translation>
|
||||
<context>
|
||||
<name>OCC::SocketApi</name>
|
||||
<message>
|
||||
<location filename="../src/gui/socketapi.cpp" line="455"/>
|
||||
<location filename="../src/gui/socketapi.cpp" line="508"/>
|
||||
<source>Share with %1</source>
|
||||
<comment>parameter is ownCloud</comment>
|
||||
<translation>Comparteix amb %1</translation>
|
||||
@@ -2945,23 +2950,23 @@ No és aconsellada usar-la.</translation>
|
||||
<translation>Error en llegir la carpeta.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="452"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="468"/>
|
||||
<source>File/Folder is ignored because it's hidden.</source>
|
||||
<translation>El fitxer/carpeta s'ha ignorat perquè és ocult.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="719"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="735"/>
|
||||
<source>Only %1 are available, need at least %2 to start</source>
|
||||
<comment>Placeholders are postfixed with file sizes using Utility::octetsToString()</comment>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1185"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1201"/>
|
||||
<source>Not allowed because you don't have permission to add parent folder</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1192"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1208"/>
|
||||
<source>Not allowed because you don't have permission to add files in that folder</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
@@ -2980,11 +2985,6 @@ No és aconsellada usar-la.</translation>
|
||||
<source>Aborted by the user</source>
|
||||
<translation>Aturat per l'usuari</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="443"/>
|
||||
<source>Filename contains invalid characters that can not be synced cross platform.</source>
|
||||
<translation>El nom del fitxer conté caràcters no vàlids que no es poden sincronitzar entre plataformes.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="168"/>
|
||||
<source>CSync failed to access</source>
|
||||
@@ -3036,99 +3036,114 @@ No és aconsellada usar-la.</translation>
|
||||
<translation>El fitxer està a la llista d'ignorats.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="446"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="444"/>
|
||||
<source>File names ending with a period are not supported on this file system.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="454"/>
|
||||
<source>File names containing the character '%1' are not supported on this file system.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="457"/>
|
||||
<source>The file name is a reserved name on this file system.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="462"/>
|
||||
<source>Filename contains trailing spaces.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="449"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="465"/>
|
||||
<source>Filename is too long.</source>
|
||||
<translation>El nom de fitxer és massa llarg.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="458"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="474"/>
|
||||
<source>Stat failed.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="485"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="501"/>
|
||||
<source>Filename encoding is not valid</source>
|
||||
<translation>La codificació del nom de fitxer no és vàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="659"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="675"/>
|
||||
<source>Invalid characters, please rename "%1"</source>
|
||||
<translation>Caràcters no vàlids. Reanomeneu "%1"</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="756"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="772"/>
|
||||
<source>Unable to initialize a sync journal.</source>
|
||||
<translation>No es pot inicialitzar un periòdic de sincronització</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="775"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="791"/>
|
||||
<source>Unable to read the blacklist from the local database</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="812"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="828"/>
|
||||
<source>Unable to read from the sync journal.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="857"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="873"/>
|
||||
<source>Cannot open the sync journal</source>
|
||||
<translation>No es pot obrir el diari de sincronització</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="914"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="930"/>
|
||||
<source>File name contains at least one invalid character</source>
|
||||
<translation>El nom del fitxer conté al menys un caràcter invàlid</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1144"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1151"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1160"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1167"/>
|
||||
<source>Ignored because of the "choose what to sync" blacklist</source>
|
||||
<translation>S'ignora degut al filtre a «Trieu què sincronitzar»</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1170"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1186"/>
|
||||
<source>Not allowed because you don't have permission to add subfolders to that folder</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1211"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1227"/>
|
||||
<source>Not allowed to upload this file because it is read-only on the server, restoring</source>
|
||||
<translation>No es permet pujar aquest fitxer perquè només és de lectura en el servidor, es restaura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1227"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1246"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1243"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1262"/>
|
||||
<source>Not allowed to remove, restoring</source>
|
||||
<translation>No es permet l'eliminació, es restaura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1259"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1275"/>
|
||||
<source>Local files and share folder removed.</source>
|
||||
<translation>Fitxers locals i carpeta compartida esborrats.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1315"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1331"/>
|
||||
<source>Move not allowed, item restored</source>
|
||||
<translation>No es permet moure'l, l'element es restaura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1326"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1342"/>
|
||||
<source>Move not allowed because %1 is read-only</source>
|
||||
<translation>No es permet moure perquè %1 només és de lectura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1327"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1343"/>
|
||||
<source>the destination</source>
|
||||
<translation>el destí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1327"/>
|
||||
<location filename="../src/libsync/syncengine.cpp" line="1343"/>
|
||||
<source>the source</source>
|
||||
<translation>l'origen</translation>
|
||||
</message>
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário